// Copyright (C) 2004-2005 TintaDigital - STI, LDA. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // As a special exception, you may use this file as part of a free software // library without restriction. Specifically, if other files instantiate // templates or use macros or inline functions from this file, or you compile // this file and link it with other files to produce an executable, this // file does not by itself cause the resulting executable to be covered by // the GNU General Public License. This exception does not however // invalidate any other reasons why the executable file might be covered by // the GNU General Public License. // // This exception applies only to the code released under the name GNU // Common C++. If you copy code from other releases into a copy of GNU // Common C++, as the General Public License permits, the exception does // not apply to the code that you add in this way. To avoid misleading // anyone as to the status of such modified files, you must delete // this exception notice from them. // // If you write modifications of your own for GNU Common C++, it is your choice // whether to permit this exception to apply to your modifications. // If you do not wish that, delete this exception notice. // /** * @file nat.c * @short Network Address Translation interface implementation. * @author Ricardo Gameiro **/ #include #include "nat.h" #ifdef CCXX_NAT # ifdef HAVE_SYS_TYPES_H # include # endif # ifdef HAVE_SYS_SOCKET_H # include # endif # ifdef HAVE_NAT_NETFILTER // Linux # ifdef HAVE_LIMITS_H # include # endif # ifdef HAVE_LINUX_NETFILTER_IPV4_H # include # endif # ifdef HAVE_LINUX_NETFILTER_IPV6_H # include # endif # else # ifdef HAVE_NET_IP6_H # include # endif # ifdef HAVE_NETINET_IN_H # include # endif # ifdef HAVE_NET_IF_H # include # endif # ifdef HAVE_SYS_IOCTL_H # include # endif # ifdef HAVE_IOCTL_H # include # endif # ifdef HAVE_UNISTD_H # include # endif # ifdef HAVE_ERRNO_H # include # endif # ifdef HAVE_NAT_IPF // Solaris, *BSD (except OpenBSD), HP-UX # ifdef HAVE_NETINET_IP_COMPAT_H # include # endif # ifdef HAVE_IP_COMPAT_H # include # endif # ifdef HAVE_NETINET_IP_FIL_COMPAT_H # include # endif # ifdef HAVE_IP_FIL_COMPAT_H # include # endif # ifdef HAVE_NETINET_IP_FIL_H # include # endif # ifdef HAVE_IP_FIL_H # include # endif # ifdef HAVE_NETINET_IP_NAT_H # include # endif # ifdef HAVE_IP_NAT_H # include # endif # endif # ifdef HAVE_NAT_PF // OpenBSD # ifdef HAVE_NET_PFVAR_H # include # endif # endif # endif #endif #ifdef CCXX_NAMESPACES namespace ost { #endif #ifdef HAVE_NAT_NETFILTER # define NAT_SYSCALL "getsockopt" # define NAT_DEVICE "" #else # define NAT_SYSCALL "ioctl" # if defined(HAVE_NAT_IPF) && defined(IPL_NAT) # define NAT_DEVICE IPL_NAT # else # ifdef HAVE_NAT_PF # define NAT_DEVICE "/dev/pf" # endif # endif #endif #ifndef NAT_DEVICE #define NAT_DEVICE "" #endif #ifdef CCXX_NAT char * natmsg[] = { "nat lookup successful", "nat address not in table", "nat not supported/implemented", "unable to open device "NAT_DEVICE, "unable to get socket name", "unable to get peer name", "unable to get socket type", "unable to lookup, nat "NAT_SYSCALL" failed", "unkown nat error code" }; #ifdef HAVE_NAT_NETFILTER // Linux natResult natv4Lookup( SOCKET sfd, struct sockaddr_in * nat ) { struct sockaddr_in local; socklen_t nlen = sizeof( *nat ), llen = sizeof( local ); if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) ) return natSocknameErr; memset( &nat->sin_addr.s_addr, 0, sizeof( nat->sin_addr.s_addr ) ); if( getsockopt( sfd, SOL_IP, SO_ORIGINAL_DST, ( struct sockaddr * ) nat, &nlen) ) return natIFaceErr; if( local.sin_addr.s_addr == nat->sin_addr.s_addr ) return natSearchErr; nat->sin_family = local.sin_family; return natOK; } #ifdef CCXX_IPV6 natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat ) { struct sockaddr_in6 local; socklen_t llen = sizeof( local ), nlen = sizeof( *nat ); if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) ) return natSocknameErr; memset( &nat->sin6_addr.s6_addr, 0, sizeof( nat->sin6_addr.s6_addr ) ); if( getsockopt( sfd, SOL_IP, SO_ORIGINAL_DST, ( struct sockaddr * ) nat, &nlen) ) return natIFaceErr; if( local.sin6_addr.s6_addr == nat->sin6_addr.s6_addr ) return natSearchErr; nat->sin6_family = local.sin6_family; return natOK; } #endif #endif #ifdef HAVE_NAT_IPF // Solaris, *BSD (except OpenBSD), HP-UX, etc. natResult natv4Lookup( int sfd, struct sockaddr_in * nat ) { static int natfd = -1; struct sockaddr_in local, peer; socklen_t nlen = sizeof( *nat ), llen = sizeof( local ), plen = sizeof( peer ); int socktype; socklen_t stlen = sizeof( socktype ); struct natlookup nlu; int nres; if( natfd < 0 ) if( ( natfd = open( NAT_DEVICE, O_RDONLY, 0 ) ) < 0 ) return natDevUnavail; if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) ) return natSocknameErr; if( getpeername( sfd, ( struct sockaddr * ) &peer, &plen ) ) return natPeernameErr; if( getsockopt( sfd, SOL_SOCKET, SO_TYPE, ( int * ) &socktype, &stlen ) ) return natSockTypeErr; memset( &nlu.nl_realip.s_addr, 0, sizeof( nlu.nl_realip.s_addr ) ); nlu.nl_inip = local.sin_addr; nlu.nl_inport = local.sin_port; nlu.nl_outip = peer.sin_addr; nlu.nl_outport = peer.sin_port; nlu.nl_flags = ( socktype == SOCK_STREAM ) ? IPN_TCP : IPN_UDP; if( 63 == ( SIOCGNATL & 0xff ) ) { struct natlookup * nlup = &nlu; nres = ioctl( natfd, SIOCGNATL, &nlup ); } else nres = ioctl( natfd, SIOCGNATL, &nlu ); if( nres ) { if( errno != ESRCH ) { close( natfd ); natfd = -1; return natIFaceErr; } else return natSearchErr; } if( local.sin_addr.s_addr == nlu.nl_realip.s_addr ) return natSearchErr; nat->sin_family = local.sin_family; nat->sin_port = nlu.nl_realport; nat->sin_addr = nlu.nl_realip; return natOK; } #ifdef CCXX_IPV6 // IPV6 is not yet supported by IPFilter natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat ) { return natNotSupported; } #endif #endif #ifdef HAVE_NAT_PF // OpenBSD natResult natv4Lookup( SOCKET sfd, struct sockaddr_in * nat ) { static int natfd = -1; struct sockaddr_in local, peer; socklen_t nlen = sizeof( *nat ), llen = sizeof( local ), plen = sizeof( peer ); int socktype; socklen_t stlen = sizeof( socktype ); struct pfioc_natlook nlu; int nres; if( natfd < 0 ) if( ( natfd = open( NAT_DEVICE, O_RDWR ) ) < 0 ) return natDevUnavail; if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) ) return natSocknameErr; if( getpeername( sfd, ( struct sockaddr * ) &peer, &plen ) ) return natPeernameErr; if( getsockopt( sfd, SOL_SOCKET, SO_TYPE, ( int * ) &socktype, &stlen ) ) return natSockTypeErr; memset( &nlu, 0, sizeof( nlu ) ); nlu.daddr.v4.s_addr = local.sin_addr.s_addr; nlu.dport = local.sin_port; nlu.saddr.v4.s_addr = peer.sin_addr.s_addr; nlu.sport = peer.sin_port; nlu.af = AF_INET; nlu.proto = ( socktype == SOCK_STREAM ) ? IPPROTO_TCP : IPROTO_UDP; nlu.direction = PF_OUT; if( ioctl( natfd, DIOCNATLOOK, &nlu ) ) { if( errno != ESRCH ) { close( natfd ); natfd = -1; return natIFaceErr; } else return natSearchErr; } if( local.sin_addr.s_addr == nlu.raddr.v4.s_addr ) return natSearchErr; nat->sin_family = local.sin_family; nat->sin_port = nlu.rdport; nat->sin_addr = nlu.rdaddr.v4; return natOK; } #ifdef CCXX_IPV6 natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat ) { static int natfd = -1; struct sockaddr_in6 local, peer; socklen_t nlen = sizeof( *nat ), llen = sizeof( local ), plen = sizeof( peer ); int socktype; socklen_t stlen = sizeof( socktype ); struct pfioc_natlook nlu; int nres; if( natfd < 0 ) if( ( natfd = open( NAT_DEVICE, O_RDWR ) ) < 0 ) return natDevUnavail; if( getsockname( sfd, ( struct sockaddr * ) &local, &llen ) ) return natSocknameErr; if( getpeername( sfd, ( struct sockaddr * ) &peer, &plen ) ) return natPeernameErr; if( getsockopt( sfd, SOL_SOCKET, SO_TYPE, ( int * ) &socktype, &stlen ) ) return natSockTypeErr; memset( &nlu, 0, sizeof( nlu ) ); nlu.daddr.v6.s6_addr = local.sin6_addr.s6_addr; nlu.dport = local.sin6_port; nlu.saddr.v6.s6_addr = peer.sin6_addr.s6_addr; nlu.sport = peer.sin6_port; nlu.af = AF_INET6; nlu.proto = ( socktype == SOCK_STREAM ) ? IPPROTO_TCP : IPROTO_UDP; nlu.direction = PF_OUT; if( ioctl( natfd, DIOCNATLOOK, &nlu ) ) { if( errno != ESRCH ) { close( natfd ); natfd = -1; return natIFaceErr; } else return natSearchErr; } if( local.sin6_addr.s6_addr == nlu.raddr.v6.s6_addr ) return natSearchErr; nat->sin6_family = local.sin6_family; nat->sin6_flowinfo = local.sin6_flowinfo; nat->sin6_port = nlu.rdport; nat->sin6_addr = nlu.rdaddr.v6; return natOK; } #endif #endif char * natErrorString( natResult res ) { return natmsg[ ( res >= natOK && res <= natIFaceErr ) ? res : natUnkownErr ]; } #else natResult natv4Lookup( SOCKET sfd, struct sockaddr_in * nat ) { return natNotSupported; } #ifdef CCXX_IPV6 natResult natv6Lookup( SOCKET sfd, struct sockaddr_in6 * nat ) { return natNotSupported; } #endif char * natErrorString( natResult res ) { return "nat support not included"; } #endif #ifdef CCXX_NAMESPACES } #endif