diff --git a/tpws/helpers.c b/tpws/helpers.c index 29427da..c034fa6 100644 --- a/tpws/helpers.c +++ b/tpws/helpers.c @@ -30,23 +30,45 @@ char *strncasestr(const char *s,const char *find, size_t slen) return (char *)s; } -void print_sockaddr(const struct sockaddr *sa) +void ntop46(const struct sockaddr *sa, char *str, size_t len) { - char str[64]; + if (!len) return; + *str=0; switch (sa->sa_family) { case AF_INET: - if (inet_ntop(sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr, str, sizeof(str))) - printf("%s:%d", str, ntohs(((struct sockaddr_in*)sa)->sin_port)); + inet_ntop(sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr, str, len); break; case AF_INET6: - if (inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, sizeof(str))) - printf("%s:%d", str, ntohs(((struct sockaddr_in6*)sa)->sin6_port)); + inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, len); break; default: - printf("UNKNOWN_FAMILY_%d", sa->sa_family); + snprintf(str,len,"UNKNOWN_FAMILY_%d",sa->sa_family); } } +void ntop46_port(const struct sockaddr *sa, char *str, size_t len) +{ + char ip[40]; + ntop46(sa,ip,sizeof(ip)); + switch (sa->sa_family) + { + case AF_INET: + snprintf(str,len,"%s:%u",ip,ntohs(((struct sockaddr_in*)sa)->sin_port)); + break; + case AF_INET6: + snprintf(str,len,"[%s]:%u",ip,ntohs(((struct sockaddr_in6*)sa)->sin6_port)); + break; + default: + snprintf(str,len,"%s",ip); + } +} +void print_sockaddr(const struct sockaddr *sa) +{ + char ip_port[48]; + + ntop46_port(sa,ip_port,sizeof(ip_port)); + printf("%s",ip_port); +} // -1 = error, 0 = not local, 1 = local @@ -128,6 +150,17 @@ bool saconvmapped(struct sockaddr_storage *a) return false; } +bool is_linklocal(const struct sockaddr_in6* a) +{ + // fe80::/10 + return a->sin6_addr.s6_addr[0]==0xFE && (a->sin6_addr.s6_addr[1] & 0xC0)==0x80; +} +bool is_private6(const struct sockaddr_in6* a) +{ + // fdf0::/8 + return a->sin6_addr.s6_addr[0]==0xFD; +} + int set_keepalive(int fd) diff --git a/tpws/helpers.h b/tpws/helpers.h index 25079e0..bbe323d 100644 --- a/tpws/helpers.h +++ b/tpws/helpers.h @@ -8,6 +8,8 @@ char *strncasestr(const char *s,const char *find, size_t slen); +void ntop46(const struct sockaddr *sa, char *str, size_t len); +void ntop46_port(const struct sockaddr *sa, char *str, size_t len); void print_sockaddr(const struct sockaddr *sa); void print_addrinfo(const struct addrinfo *ai); bool check_local_ip(const struct sockaddr *saddr); @@ -19,5 +21,8 @@ uint16_t saport(const struct sockaddr *sa); // true = was converted bool saconvmapped(struct sockaddr_storage *a); +bool is_linklocal(const struct sockaddr_in6* a); +bool is_private6(const struct sockaddr_in6* a); + int set_keepalive(int fd); int get_so_error(int fd); diff --git a/tpws/redirect.c b/tpws/redirect.c index e562d7f..94b22c5 100644 --- a/tpws/redirect.c +++ b/tpws/redirect.c @@ -46,7 +46,7 @@ static bool redir_open_private(const char *fname, int flags) redirector_fd = open(fname, flags); if (redirector_fd < 0) { - perror("redir_openv_private: "); + perror("redir_openv_private"); return false; } DBGPRINT("opened redirector %s",fname); @@ -179,7 +179,7 @@ bool get_dest_addr(int sockfd, const struct sockaddr *accept_sa, struct sockaddr r=getsockname(sockfd, (struct sockaddr*) orig_dst, &addrlen); if (r<0) { - perror("getsockname: "); + perror("getsockname"); return false; } if (orig_dst->ss_family==AF_INET6) diff --git a/tpws/sec.c b/tpws/sec.c index a3308f6..925e7cb 100644 --- a/tpws/sec.c +++ b/tpws/sec.c @@ -88,24 +88,24 @@ bool droproot(uid_t uid, gid_t gid) #ifdef __linux__ if (prctl(PR_SET_KEEPCAPS, 1L)) { - perror("prctl(PR_SET_KEEPCAPS): "); + perror("prctl(PR_SET_KEEPCAPS)"); return false; } #endif // drop all SGIDs if (setgroups(0,NULL)) { - perror("setgroups: "); + perror("setgroups"); return false; } if (setgid(gid)) { - perror("setgid: "); + perror("setgid"); return false; } if (setuid(uid)) { - perror("setuid: "); + perror("setuid"); return false; } #ifdef __linux__ @@ -138,7 +138,7 @@ void daemonize() pid = fork(); if (pid == -1) { - perror("fork: "); + perror("fork"); exit(2); } else if (pid != 0) diff --git a/tpws/tpws.c b/tpws/tpws.c index be11d47..7d9c916 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -35,6 +35,7 @@ #include "params.h" #include "sec.h" #include "redirect.h" +#include "helpers.h" struct params_s params; @@ -496,10 +497,6 @@ void parse_params(int argc, char *argv[]) } -static bool is_linklocal(const struct sockaddr_in6* a) -{ - return a->sin6_addr.s6_addr[0]==0xFE && (a->sin6_addr.s6_addr[1] & 0xC0)==0x80; -} static bool find_listen_addr(struct sockaddr_storage *salisten, const char *bindiface, bool bind_if6, bool bindll, int *if_index) { struct ifaddrs *addrs,*a; @@ -508,8 +505,10 @@ static bool find_listen_addr(struct sockaddr_storage *salisten, const char *bind if (getifaddrs(&addrs)<0) return false; - int maxpass = (bind_if6 && !bindll) ? 2 : 1; - for(int pass=0;passifa_name, bindiface)) && (bindll && is_linklocal((struct sockaddr_in6*)a->ifa_addr) || - !bindll && (pass || !is_linklocal((struct sockaddr_in6*)a->ifa_addr))) + !bindll && (pass==2 || pass==0 && is_private6((struct sockaddr_in6*)a->ifa_addr) || pass==1 && !is_linklocal((struct sockaddr_in6*)a->ifa_addr))) ) { salisten->ss_family = AF_INET6; @@ -639,11 +638,13 @@ struct salisten_s struct sockaddr_storage salisten; socklen_t salisten_len; int ipv6_only; + int bind_wait_ip_left; // how much seconds left from bind_wait_ip }; int main(int argc, char *argv[]) { int i, listen_fd[MAX_BINDS], yes = 1, retval = 0, if_index, exit_v=EXIT_FAILURE; struct salisten_s list[MAX_BINDS]; + char ip_port[48]; srand(time(NULL)); parse_params(argc, argv); @@ -692,6 +693,7 @@ int main(int argc, char *argv[]) goto exiterr; } } + list[i].bind_wait_ip_left = params.binds[i].bind_wait_ip; if (*params.binds[i].bindaddr) { if (inet_pton(AF_INET, params.binds[i].bindaddr, &((struct sockaddr_in*)(&list[i].salisten))->sin_addr)) @@ -747,6 +749,7 @@ int main(int argc, char *argv[]) printf("suitable ip address not found\n"); goto exiterr; } + list[i].bind_wait_ip_left = params.binds[i].bind_wait_ip - sec; list[i].ipv6_only=1; } else @@ -771,7 +774,7 @@ int main(int argc, char *argv[]) if (params.bind_wait_only) { - printf("bind wait condition satisfied. exiting.\n"); + printf("bind wait condition satisfied\n"); exit_v = 0; goto exiterr; } @@ -784,24 +787,28 @@ int main(int argc, char *argv[]) for(i=0;i<=params.binds_last;i++) { - VPRINT("Binding %d",i); + if (params.debug) + { + ntop46_port((struct sockaddr *)&list[i].salisten, ip_port, sizeof(ip_port)); + VPRINT("Binding %d to %s",i,ip_port); + } if ((listen_fd[i] = socket(list[i].salisten.ss_family, SOCK_STREAM, 0)) == -1) { - perror("socket: "); + perror("socket"); goto exiterr; } #ifndef __OpenBSD__ // in OpenBSD always IPV6_ONLY for wildcard sockets if ((list[i].salisten.ss_family == AF_INET6) && setsockopt(listen_fd[i], IPPROTO_IPV6, IPV6_V6ONLY, &list[i].ipv6_only, sizeof(int)) == -1) { - perror("setsockopt (IPV6_ONLY): "); + perror("setsockopt (IPV6_ONLY)"); goto exiterr; } #endif if (setsockopt(listen_fd[i], SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { - perror("setsockopt (SO_REUSEADDR): "); + perror("setsockopt (SO_REUSEADDR)"); goto exiterr; } @@ -812,13 +819,13 @@ int main(int argc, char *argv[]) #ifdef __linux__ if (setsockopt(listen_fd[i], SOL_IP, IP_TRANSPARENT, &yes, sizeof(yes)) == -1) { - perror("setsockopt (IP_TRANSPARENT): "); + perror("setsockopt (IP_TRANSPARENT)"); goto exiterr; } #elif defined(BSD) && defined(SO_BINDANY) if (setsockopt(listen_fd[i], SOL_SOCKET, SO_BINDANY, &yes, sizeof(yes)) == -1) { - perror("setsockopt (SO_BINDANY): "); + perror("setsockopt (SO_BINDANY)"); goto exiterr; } #endif @@ -837,12 +844,35 @@ int main(int argc, char *argv[]) setsockopt(listen_fd[i],SOL_SOCKET,SO_RCVBUF,&v,sizeof(int)); } } - if (bind(listen_fd[i], (struct sockaddr *)&list[i].salisten, list[i].salisten_len) == -1) { - perror("bind: "); - goto exiterr; + bool bBindBug=false; + for(;;) + { + if (bind(listen_fd[i], (struct sockaddr *)&list[i].salisten, list[i].salisten_len) == -1) + { + // in linux strange behaviour was observed + // just after ifup and address assignment there's short window when bind() can't bind to addresses got from getifaddrs() + // it does not happen to transparent sockets because they cant bind to any non-existend ip + // also only ipv6 seem to be buggy this way + if (errno==EADDRNOTAVAIL && params.proxy_type!=CONN_TYPE_TRANSPARENT && list[i].bind_wait_ip_left) + { + if (!bBindBug) + { + ntop46_port((struct sockaddr *)&list[i].salisten, ip_port, sizeof(ip_port)); + printf("address %s is not available. will retry for %d sec\n",ip_port,list[i].bind_wait_ip_left); + bBindBug=true; + } + sleep(1); + list[i].bind_wait_ip_left--; + continue; + } + perror("bind"); + goto exiterr; + } + break; } - if (listen(listen_fd[i], BACKLOG) == -1) { - perror("listen: "); + if (listen(listen_fd[i], BACKLOG) == -1) + { + perror("listen"); goto exiterr; } } diff --git a/tpws/tpws_conn.c b/tpws/tpws_conn.c index ab2b605..89dd7b8 100644 --- a/tpws/tpws_conn.c +++ b/tpws/tpws_conn.c @@ -272,13 +272,13 @@ bool set_socket_buffers(int fd, int rcvbuf, int sndbuf) DBGPRINT("set_socket_buffers fd=%d rcvbuf=%d sndbuf=%d",fd,rcvbuf,sndbuf) if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) <0) { - perror("setsockopt (SO_RCVBUF): "); + perror("setsockopt (SO_RCVBUF)"); close(fd); return false; } if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) <0) { - perror("setsockopt (SO_SNDBUF): "); + perror("setsockopt (SO_SNDBUF)"); close(fd); return false; } @@ -296,20 +296,20 @@ static int connect_remote(const struct sockaddr *remote_addr) if((remote_fd = socket(remote_addr->sa_family, SOCK_STREAM, 0)) < 0) { - perror("socket (connect_remote): "); + perror("socket (connect_remote)"); return -1; } // Use NONBLOCK to avoid slow connects affecting the performance of other connections // separate fcntl call to comply with macos if (fcntl(remote_fd, F_SETFL, O_NONBLOCK)<0) { - perror("socket set O_NONBLOCK (connect_remote): "); + perror("socket set O_NONBLOCK (connect_remote)"); close(remote_fd); return -1; } if(setsockopt(remote_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { - perror("setsockopt (SO_REUSEADDR, connect_remote): "); + perror("setsockopt (SO_REUSEADDR, connect_remote)"); close(remote_fd); return -1; } @@ -317,13 +317,13 @@ static int connect_remote(const struct sockaddr *remote_addr) return -1; if(!set_keepalive(remote_fd)) { - perror("set_keepalive: "); + perror("set_keepalive"); close(remote_fd); return -1; } if (setsockopt(remote_fd, IPPROTO_TCP, TCP_NODELAY, params.skip_nodelay ? &no : &yes, sizeof(int)) <0) { - perror("setsockopt (SO_NODELAY, connect_remote): "); + perror("setsockopt (SO_NODELAY, connect_remote)"); close(remote_fd); return -1; } @@ -331,7 +331,7 @@ static int connect_remote(const struct sockaddr *remote_addr) { if(errno != EINPROGRESS) { - perror("connect (connect_remote): "); + perror("connect (connect_remote)"); close(remote_fd); return -1; } @@ -466,7 +466,7 @@ static tproxy_conn_t* add_tcp_connection(int efd, struct tailhead *conn_list,int if(!set_keepalive(local_fd)) { - perror("set_keepalive: "); + perror("set_keepalive"); close(local_fd); return 0; } @@ -1177,7 +1177,7 @@ int event_loop(int *listen_fd, size_t listen_fd_ct) tmp_fd = accept(conn->fd, (struct sockaddr*)&accept_sa, &accept_salen); if (tmp_fd < 0) { - perror("Failed to accept connection : "); + perror("Failed to accept connection"); } else if (legs_local >= params.maxconn) // each connection has 2 legs - local and remote { @@ -1187,7 +1187,7 @@ int event_loop(int *listen_fd, size_t listen_fd_ct) // separate fcntl call to comply with macos else if (fcntl(tmp_fd, F_SETFL, O_NONBLOCK) < 0) { - perror("socket set O_NONBLOCK (accept): "); + perror("socket set O_NONBLOCK (accept)"); close(tmp_fd); } else if (!(conn=add_tcp_connection(efd, &conn_list, tmp_fd, (struct sockaddr*)&accept_sa, params.port, params.proxy_type)))