/* VTun - Virtual Tunnel over TCP/IP network. Copyright (C) 1998-2008 Maxim Krasnyansky VTun has been derived from VPPP package by Maxim Krasnyansky. 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. */ /* * $Id: netlib.c,v 1.11.2.3 2009/03/29 10:08:59 mtbishop Exp $ */ #include "config.h" #include "vtun_socks.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #ifdef HAVE_NETINET_TCP_H #include #endif #ifdef HAVE_RESOLV_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NETDB_H #include #endif #include "vtun.h" #include "lib.h" #include "netlib.h" /* Connect with timeout */ int connect_t(int s, struct sockaddr *svr, time_t timeout) { #if defined(VTUN_SOCKS) && VTUN_SOCKS == 2 /* Some SOCKS implementations don't support * non blocking connect */ return connect(s,svr,sizeof(struct sockaddr)); #else int sock_flags; fd_set fdset; struct timeval tv; tv.tv_usec=0; tv.tv_sec=timeout; sock_flags=fcntl(s,F_GETFL); if( fcntl(s,F_SETFL,O_NONBLOCK) < 0 ) return -1; if( connect(s,svr,sizeof(struct sockaddr)) < 0 && errno != EINPROGRESS) return -1; FD_ZERO(&fdset); FD_SET(s,&fdset); if( select(s+1,NULL,&fdset,NULL,timeout?&tv:NULL) > 0 ){ int l=sizeof(errno); errno=0; getsockopt(s,SOL_SOCKET,SO_ERROR,&errno,&l); } else errno=ETIMEDOUT; fcntl(s,F_SETFL,sock_flags); if( errno ) return -1; return 0; #endif } /* Get interface address */ unsigned long getifaddr(char * ifname) { struct sockaddr_in addr; struct ifreq ifr; int s; if( (s = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ) return -1; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)-1); ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0'; if( ioctl(s, SIOCGIFADDR, &ifr) < 0 ){ close(s); return -1; } close(s); addr = *((struct sockaddr_in *) &ifr.ifr_addr); return addr.sin_addr.s_addr; } /* * Establish UDP session with host connected to fd(socket). * Returns connected UDP socket or -1 on error. */ int udp_session(struct vtun_host *host) { struct sockaddr_in saddr; short port; int s,opt; if( (s=socket(AF_INET,SOCK_DGRAM,0))== -1 ){ vtun_syslog(LOG_ERR,"Can't create socket"); return -1; } opt=1; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /* Set local address and port */ local_addr(&saddr, host, 1); if( bind(s,(struct sockaddr *)&saddr,sizeof(saddr)) ){ vtun_syslog(LOG_ERR,"Can't bind to the socket"); return -1; } opt = sizeof(saddr); if( getsockname(s,(struct sockaddr *)&saddr,&opt) ){ vtun_syslog(LOG_ERR,"Can't get socket name"); return -1; } /* Write port of the new UDP socket */ port = saddr.sin_port; if( write_n(host->rmt_fd,(char *)&port,sizeof(short)) < 0 ){ vtun_syslog(LOG_ERR,"Can't write port number"); return -1; } host->sopt.lport = htons(port); /* Read port of the other's end UDP socket */ if( readn_t(host->rmt_fd,&port,sizeof(short),host->timeout) < 0 ){ vtun_syslog(LOG_ERR,"Can't read port number %s", strerror(errno)); return -1; } opt = sizeof(saddr); if( getpeername(host->rmt_fd,(struct sockaddr *)&saddr,&opt) ){ vtun_syslog(LOG_ERR,"Can't get peer name"); return -1; } saddr.sin_port = port; /* if the config says to delay the UDP connection, we wait for an incoming packet and then force a connection back. We need to put this here because we need to keep that incoming triggering packet and pass it back up the chain. */ if (VTUN_USE_NAT_HACK(host)) is_rmt_fd_connected=0; else { if( connect(s,(struct sockaddr *)&saddr,sizeof(saddr)) ){ vtun_syslog(LOG_ERR,"Can't connect socket"); return -1; } is_rmt_fd_connected=1; } host->sopt.rport = htons(port); /* Close TCP socket and replace with UDP socket */ close(host->rmt_fd); host->rmt_fd = s; vtun_syslog(LOG_INFO,"UDP connection initialized"); return s; } /* Set local address */ int local_addr(struct sockaddr_in *addr, struct vtun_host *host, int con) { int opt; if( con ){ /* Use address of the already connected socket. */ opt = sizeof(struct sockaddr_in); if( getsockname(host->rmt_fd, (struct sockaddr *)addr, &opt) < 0 ){ vtun_syslog(LOG_ERR,"Can't get local socket address"); return -1; } } else { if (generic_addr(addr, &host->src_addr) < 0) return -1; } host->sopt.laddr = strdup(inet_ntoa(addr->sin_addr)); return 0; } int server_addr(struct sockaddr_in *addr, struct vtun_host *host) { struct hostent * hent; memset(addr,0,sizeof(struct sockaddr_in)); addr->sin_family = AF_INET; addr->sin_port = htons(vtun.bind_addr.port); /* Lookup server's IP address. * We do it on every reconnect because server's IP * address can be dynamic. */ if( !(hent = gethostbyname(vtun.svr_name)) ){ vtun_syslog(LOG_ERR, "Can't resolv server address: %s", vtun.svr_name); return -1; } addr->sin_addr.s_addr = *(unsigned long *)hent->h_addr; host->sopt.raddr = strdup(inet_ntoa(addr->sin_addr)); host->sopt.rport = vtun.bind_addr.port; return 0; } /* Set address by interface name, ip address or hostname */ int generic_addr(struct sockaddr_in *addr, struct vtun_addr *vaddr) { struct hostent *hent; memset(addr, 0, sizeof(struct sockaddr_in)); addr->sin_family = AF_INET; switch (vaddr->type) { case VTUN_ADDR_IFACE: if (!(addr->sin_addr.s_addr = getifaddr(vaddr->name))) { vtun_syslog(LOG_ERR, "Can't get address of interface %s", vaddr->name); return -1; } break; case VTUN_ADDR_NAME: if (!(hent = gethostbyname(vaddr->name))) { vtun_syslog(LOG_ERR, "Can't resolv local address %s", vaddr->name); return -1; } addr->sin_addr.s_addr = *(unsigned long *) hent->h_addr; break; default: addr->sin_addr.s_addr = INADDR_ANY; break; } if (vaddr->port) addr->sin_port = htons(vaddr->port); return 0; }