// if the caller attempted to bind to 0.0.0.0 or ::, then change it to // this node's public IP address int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { errno = 0; // save the original bind() call void *handle = dlopen( LIBC_PATH, RTLD_LAZY ); if (!handle) { handle = dlopen( LIBC_PATH_DEBIAN, RTLD_LAZY ); if( !handle ) { fprintf( stderr, "Error loading libc.so.6\n" ); fflush( stderr ); return -1; } } bind_original = dlsym(handle, "bind"); if( bind_original == NULL ) { fprintf( stderr, "Error loading bind symbol\n" ); fflush( stderr ); return -1; } int tried_normal_bind = 0; int rc = is_addr_any( addr ); fprintf( stderr, "bind(%d, %p, %ld)\n", sockfd, addr, addrlen); if( rc > 0 ) { rc = 0; // rewrite this address struct sockaddr_storage new_addr; memset( &new_addr, 0, sizeof(struct sockaddr_storage)); memcpy( &new_addr, addr, addrlen ); rc = get_public_ip( (struct sockaddr*)&new_addr ); if( rc == -EINVAL ) { // this will happen for DHCP, so bind the normal way fprintf(stderr, "WARNING: could not get IP address; attempting normal bind."); rc = bind_original( sockfd, (struct sockaddr*)&new_addr, addrlen ); fprintf(stderr, "normal bind rc = %d, errno = %d\n", rc, errno ); tried_normal_bind = 1; } else if( rc != 0 ) { rc = -1; } if( rc == 0 && tried_normal_bind == 0 ) { debug( addr, (struct sockaddr*)&new_addr ); rc = bind_original( sockfd, (struct sockaddr*)&new_addr, addrlen ); fprintf( stderr, "re-addressed bind rc = %d, errno = %d\n", rc, errno); // save this result if( rc == 0 ) { cache_public_ip( (struct sockaddr*)&new_addr); } } } else { rc = bind_original( sockfd, (struct sockaddr*)addr, addrlen ); fprintf( stderr, "bind rc = %d, errno = %d\n", rc, errno); } return rc; }
/// This is main... int main(int argc,char* argv[]) { int ret; in_port_t fixedport = 0; struct sockaddr_storage fixedhost; struct addrinfo hints, *res, *reslist; struct tracker_s * conn; memset(&fixedhost, '\0', sizeof(fixedhost)); printf("netsed " VERSION " by Alexey Shumkin <*****@*****.**>\n" " based on 1.2 from Julien VdG <*****@*****.**>\n" " which is based on 0.01c from Michal Zalewski <*****@*****.**>\n"); setbuffer(stdout,NULL,0); parse_params(argc, argv); memset(&hints, '\0', sizeof(hints)); hints.ai_family = family; hints.ai_flags = AI_CANONNAME; hints.ai_socktype = tcp ? SOCK_STREAM : SOCK_DGRAM; if ((ret = getaddrinfo(rhost, rport, &hints, &reslist))) { ERR("getaddrinfo(): %s\n", gai_strerror(ret)); error("Impossible to resolve remote address or port."); } /* We have candidates for remote host. */ for (res = reslist; res; res = res->ai_next) { int sd = -1; if ( (sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) continue; /* Has successfully built a socket for this address family. */ /* Record the address structure and the port. */ fixedport = get_port(res->ai_addr); if (!is_addr_any(res->ai_addr)) memcpy(&fixedhost, res->ai_addr, res->ai_addrlen); close(sd); break; } freeaddrinfo(reslist); if (res == NULL) error("Failed in resolving remote host."); if (fixedhost.ss_family && fixedport) printf("[+] Using fixed forwarding to %s,%s.\n",rhost,rport); else if (fixedport) printf("[+] Using dynamic (transparent proxy) forwarding with fixed port %s.\n",rport); else if (fixedhost.ss_family) printf("[+] Using dynamic (transparent proxy) forwarding with fixed addr %s.\n",rhost); else printf("[+] Using dynamic (transparent proxy) forwarding.\n"); if (foreground) { printf("Run in foreground...\n"); } else { int x = fork(); if (x == -1) { printf("Error creating a fork. Run in foreground...\n"); } else if (x) { printf("Detached: PID=%d\n", x); exit(0); } } bind_and_listen(fixedhost.ss_family, tcp, lport); printf("[+] Listening on port %s/%s.\n", lport, (tcp)?"tcp":"udp"); signal(SIGPIPE, SIG_IGN); struct sigaction sa; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = sig_int; if (sigaction(SIGINT, &sa, NULL) == -1) error("netsed: sigaction() failed"); while (!stop) { struct sockaddr_storage s; socklen_t l = sizeof(s); struct sockaddr_storage conho; in_port_t conpo; char ipstr[INET6_ADDRSTRLEN], portstr[12]; int sel; fd_set rd_set; struct timeval timeout, *ptimeout; int nfds = lsock; FD_ZERO(&rd_set); FD_SET(lsock,&rd_set); timeout.tv_sec = UDP_TIMEOUT+1; timeout.tv_usec = 0; ptimeout = NULL; { conn = connections; while(conn != NULL) { if(tcp) { FD_SET(conn->csock, &rd_set); if (nfds < conn->csock) nfds = conn->csock; } else { // adjust timeout to earliest connection end time int remain = UDP_TIMEOUT - (now - conn->time); if (remain < 0) remain = 0; if (timeout.tv_sec > remain) { timeout.tv_sec = remain; // time updated to need to timeout ptimeout = &timeout; } } FD_SET(conn->fsock, &rd_set); if (nfds < conn->fsock) nfds = conn->fsock; // point on next conn = conn->n; } } sel=select(nfds+1, &rd_set, (fd_set*)0, (fd_set*)0, ptimeout); time(&now); if (stop) { break; } if (sel < 0) { DBG("[!] select fail! %s\n", strerror(errno)); break; } if (sel == 0) { DBG("[*] select timeout. now: %d\n", now); // Here we still have to go through the list to expire some udp // connection if they timed out... But no descriptor will be set. // For tcp, select will not timeout. } if (FD_ISSET(lsock, &rd_set)) { int csock=-1; ssize_t rd=-1; if (tcp) { csock = accept(lsock,(struct sockaddr*)&s,&l); } else { // udp does not handle accept, so track connections manually // also set csock if a new connection need to be registered // to share the code with tcp ;) rd = recvfrom(lsock,buf,sizeof(buf),0,(struct sockaddr*)&s,&l); if(rd >= 0) { conn = connections; while(conn != NULL) { // look for existing connections if ((conn->csl == l) && (0 == memcmp(&s, conn->csa, l))) { // found break; } // point on next conn = conn->n; } // not found if(conn == NULL) { // udp 'connection' socket is the listening one csock = lsock; } else { DBG("[+] Got incoming datagram from existing connection.\n"); } } else { ERR("recvfrom(): %s", strerror(errno)); } } // new connection (tcp accept, or udp conn not found) if ((csock)>=0) { int one=1; getnameinfo((struct sockaddr *) &s, l, ipstr, sizeof(ipstr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); printf("[+] Got incoming connection from %s,%s", ipstr, portstr); conn = malloc(sizeof(struct tracker_s)); if(NULL == conn) error("netsed: unable to malloc() connection tracker struct"); // protocol specific init if (tcp) { setsockopt(csock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int)); conn->csa = NULL; conn->csl = 0; conn->state = ESTABLISHED; } else { conn->csa = malloc(l); if(NULL == conn->csa) error("netsed: unable to malloc() connection tracker sockaddr struct"); memcpy(conn->csa, &s, l); conn->csl = l; conn->state = UNREPLIED; } conn->csock = csock; conn->time = now; conn->live = malloc(rules * sizeof(struct rule_item)); if(NULL == conn->live) error("netsed: unable to malloc() connection tracker sockaddr struct"); memcpy(conn->live, rule_live, rules * sizeof(struct rule_item)); l = sizeof(s); #ifndef LINUX_NETFILTER // was OK for linux 2.2 nat getsockname(csock,(struct sockaddr*)&s,&l); #else // for linux 2.4 and later getsockopt(csock, SOL_IP, SO_ORIGINAL_DST,(struct sockaddr*)&s,&l); #endif getnameinfo((struct sockaddr *) &s, l, ipstr, sizeof(ipstr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); printf(" to %s,%s\n", ipstr, portstr); conpo = get_port((struct sockaddr *) &s); memcpy(&conho, &s, sizeof(conho)); if (fixedport) conpo=fixedport; if (fixedhost.ss_family) memcpy(&conho, &fixedhost, sizeof(conho)); // forward to addr memcpy(&s, &conho, sizeof(s)); set_port((struct sockaddr *) &s, conpo); getnameinfo((struct sockaddr *) &s, l, ipstr, sizeof(ipstr), portstr, sizeof(portstr), NI_NUMERICHOST | NI_NUMERICSERV); printf("[*] Forwarding connection to %s,%s\n", ipstr, portstr); // connect will bind with some dynamic addr/port conn->fsock = socket(s.ss_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0); if (connect(conn->fsock,(struct sockaddr*)&s,l)) { printf("[!] Cannot connect to remote server, dropping connection.\n"); freetracker(conn); conn = NULL; } else { setsockopt(conn->fsock,SOL_SOCKET,SO_OOBINLINE,&one,sizeof(int)); conn->n = connections; connections = conn; } } // udp has data process forwarding if((rd >= 0) && (conn != NULL)) { b2server_sed(conn, rd); } } // lsock is set // all other sockets conn = connections; struct tracker_s ** pconn = &connections; while(conn != NULL) { // incoming data ? if(tcp && FD_ISSET(conn->csock, &rd_set)) { client2server_sed(conn); } if(FD_ISSET(conn->fsock, &rd_set)) { server2client_sed(conn); } // timeout ? udp only DBG("[!] connection last time: %d, now: %d\n", conn->time, now); if(!tcp && ((now - conn->time) >= UDP_TIMEOUT)) { DBG("[!] connection timeout.\n"); conn->state = TIMEOUT; } if(conn->state >= DISCONNECTED) { // remove it (*pconn)=conn->n; freetracker(conn); conn=(*pconn); } else { // point on next pconn = &(conn->n); conn = conn->n; } } } clean_socks(); exit(0); }