Index: ncat/ncat_connect.c =================================================================== --- ncat/ncat_connect.c (revision 32430) +++ ncat/ncat_connect.c (working copy) @@ -165,6 +165,7 @@ 0 }; +static void add_connection(nsock_pool nsp, struct sockaddr_list *conn_addr); static void connect_handler(nsock_pool nsp, nsock_event evt, void *data); static void post_connect(nsock_pool nsp, nsock_iod iod); static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data); @@ -583,7 +584,7 @@ if (o.af != AF_INET) bye("Sorry, -g can only currently be used with IPv4."); - ipopts = buildsrcrte(targetss.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen); + ipopts = buildsrcrte(targetaddrs->addr.in.sin_addr, o.srcrtes, o.numsrcrtes, o.srcrteptr, &ipoptslen); nsi_set_ipoptions(cs.sock_nsi, ipopts, ipoptslen); free(ipopts); /* Nsock has its own copy */ @@ -593,50 +594,19 @@ if (o.af == AF_UNIX) { if (o.proto == IPPROTO_UDP) { nsock_connect_unixsock_datagram(mypool, cs.sock_nsi, connect_handler, NULL, - &targetss.sockaddr, - SUN_LEN((struct sockaddr_un *)&targetss.sockaddr)); + &targetaddrs->addr.sockaddr, + SUN_LEN((struct sockaddr_un *)&targetaddrs->addr.sockaddr)); } else { nsock_connect_unixsock_stream(mypool, cs.sock_nsi, connect_handler, o.conntimeout, - NULL, &targetss.sockaddr, - SUN_LEN((struct sockaddr_un *)&targetss.sockaddr)); + NULL, &targetaddrs->addr.sockaddr, + SUN_LEN((struct sockaddr_un *)&targetaddrs->addr.sockaddr)); } } else #endif - if (o.proto == IPPROTO_UDP) { - nsock_connect_udp(mypool, cs.sock_nsi, connect_handler, - NULL, &targetss.sockaddr, targetsslen, - inet_port(&targetss)); + { + /* Add connection to first resolved address. */ + add_connection(mypool, targetaddrs); } -#ifdef HAVE_OPENSSL - else if (o.proto == IPPROTO_SCTP && o.ssl) { - nsock_connect_ssl(mypool, cs.sock_nsi, connect_handler, - o.conntimeout, NULL, - &targetss.sockaddr, targetsslen, - IPPROTO_SCTP, inet_port(&targetss), - NULL); - } -#endif - else if (o.proto == IPPROTO_SCTP) { - nsock_connect_sctp(mypool, cs.sock_nsi, connect_handler, - o.conntimeout, NULL, - &targetss.sockaddr, targetsslen, - inet_port(&targetss)); - } -#ifdef HAVE_OPENSSL - else if (o.ssl) { - nsock_connect_ssl(mypool, cs.sock_nsi, connect_handler, - o.conntimeout, NULL, - &targetss.sockaddr, targetsslen, - IPPROTO_TCP, inet_port(&targetss), - NULL); - } -#endif - else { - nsock_connect_tcp(mypool, cs.sock_nsi, connect_handler, - o.conntimeout, NULL, - &targetss.sockaddr, targetsslen, - inet_port(&targetss)); - } } else { /* A proxy connection. */ static int connect_socket; @@ -662,8 +632,8 @@ socket_buffer_init(&stateful_buf, connect_socket); if (o.verbose) { - loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetss), - inet_port(&targetss)); + loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetaddrs->addr), + inet_port(&targetaddrs->addr)); } /* Fill the socks4_data struct */ @@ -713,6 +683,8 @@ /* connect */ rc = nsock_loop(mypool, -1); + free_sockaddr_list(targetaddrs->next); + if (o.verbose) { struct timeval end_time; double time; @@ -736,19 +708,73 @@ return rc == NSOCK_LOOP_ERROR ? 1 : 0; } +static void add_connection(nsock_pool nsp, struct sockaddr_list *conn_addr) +{ + if (o.proto == IPPROTO_UDP) { + nsock_connect_udp(nsp, cs.sock_nsi, connect_handler, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + inet_port(&conn_addr->addr)); + } +#ifdef HAVE_OPENSSL + else if (o.proto == IPPROTO_SCTP && o.ssl) { + nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + IPPROTO_SCTP, inet_port(&conn_addr->addr), + NULL); + } +#endif + else if (o.proto == IPPROTO_SCTP) { + nsock_connect_sctp(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + inet_port(&conn_addr->addr)); + } +#ifdef HAVE_OPENSSL + else if (o.ssl) { + nsock_connect_ssl(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + IPPROTO_TCP, inet_port(&conn_addr->addr), + NULL); + } +#endif + else { + nsock_connect_tcp(nsp, cs.sock_nsi, connect_handler, + o.conntimeout, (void *)conn_addr->next, + &conn_addr->addr.sockaddr, conn_addr->addrlen, + inet_port(&conn_addr->addr)); + } +} + static void connect_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); + struct sockaddr_list *next_addr = (struct sockaddr_list *)data; ncat_assert(type == NSE_TYPE_CONNECT || type == NSE_TYPE_CONNECT_SSL); - if (status == NSE_STATUS_ERROR) { - loguser("%s.\n", socket_strerror(nse_errorcode(evt))); - exit(1); - } else if (status == NSE_STATUS_TIMEOUT) { - loguser("%s.\n", socket_strerror(ETIMEDOUT)); - exit(1); + if (status == NSE_STATUS_ERROR || status == NSE_STATUS_TIMEOUT) { + int errcode = (status == NSE_STATUS_TIMEOUT)?ETIMEDOUT:nse_errorcode(evt); + /* If there are more resolved addresses, try connecting to next one */ + if (next_addr != NULL) { + if (o.verbose) { + union sockaddr_u peer; + zmem(&peer, sizeof(peer.storage)); + nsi_getlastcommunicationinfo(cs.sock_nsi, NULL, NULL, NULL, + &peer.sockaddr, sizeof(peer.storage)); + loguser("Connection to %s failed: %s.\n", inet_socktop(&peer), socket_strerror(errcode)); + loguser("Trying next address...\n"); + } + add_connection(nsp, next_addr); + return; + } + else { + free_sockaddr_list(targetaddrs->next); + loguser("%s.\n", socket_strerror(errcode)); + exit(1); + } } else { ncat_assert(status == NSE_STATUS_SUCCESS); } Index: ncat/ncat_core.c =================================================================== --- ncat/ncat_core.c (revision 32430) +++ ncat/ncat_core.c (working copy) @@ -147,8 +147,7 @@ union sockaddr_u srcaddr; size_t srcaddrlen; -union sockaddr_u targetss; -size_t targetsslen; +struct sockaddr_list *targetaddrs; union sockaddr_u httpconnect, socksconnect; size_t httpconnectlen, socksconnectlen; @@ -270,6 +269,67 @@ return resolve_internal(hostname, port, ss, sslen, af, flags); } +/* Resolves the given hostname or IP address with getaddrinfo, and stores + all results into a linked list. + The rest of the behavior is same as resolve(). */ +int resolve_multi(const char *hostname, unsigned short port, + struct sockaddr_list *sl, int af) +{ + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *next; + struct sockaddr_list **item_ptr = &sl; + struct sockaddr_list *new_item; + char portbuf[16]; + int flags; + int rc; + + flags = 0; + if (o.nodns) + flags |= AI_NUMERICHOST; + + ncat_assert(hostname != NULL); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = af; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags |= flags; + + /* Make the port number a string to give to getaddrinfo. */ + rc = Snprintf(portbuf, sizeof(portbuf), "%hu", port); + ncat_assert(rc >= 0 && (size_t) rc < sizeof(portbuf)); + + rc = getaddrinfo(hostname, portbuf, &hints, &result); + if (rc != 0) + return rc; + if (result == NULL) + return EAI_NONAME; + ncat_assert(result->ai_addrlen > 0 && result->ai_addrlen <= (int) sizeof(struct sockaddr_storage)); + for (next = result; next != NULL; next = next->ai_next) { + if (*item_ptr == NULL) + { + *item_ptr = (struct sockaddr_list *)safe_malloc(sizeof(struct sockaddr_list)); + } + new_item = *item_ptr; + new_item->addrlen = next->ai_addrlen; + memcpy(&new_item->addr.storage, next->ai_addr, next->ai_addrlen); + item_ptr = &new_item->next; + } + freeaddrinfo(result); + + return 0; +} + +void free_sockaddr_list(struct sockaddr_list *sl) +{ + struct sockaddr_list *current, *next = sl; + while (next != NULL) { + current = next; + next = current->next; + free(current); + } +} + int fdinfo_close(struct fdinfo *fdn) { #ifdef HAVE_OPENSSL Index: ncat/ncat_core.h =================================================================== --- ncat/ncat_core.h (revision 32430) +++ ncat/ncat_core.h (working copy) @@ -130,14 +130,20 @@ a IPV4 INADDR_ANY and a IPV6 in6addr_any at most or a user defined address */ #define NUM_LISTEN_ADDRS 2 +/* Structure to store a linked list of resolved addresses. */ +struct sockaddr_list { + union sockaddr_u addr; + size_t addrlen; + struct sockaddr_list* next; +}; + extern union sockaddr_u listenaddrs[NUM_LISTEN_ADDRS]; extern int num_listenaddrs; extern union sockaddr_u srcaddr; extern size_t srcaddrlen; -extern union sockaddr_u targetss; -extern size_t targetsslen; +extern struct sockaddr_list *targetaddrs; extern union sockaddr_u httpconnect, socksconnect; extern size_t httpconnectlen, socksconnectlen; @@ -225,6 +231,14 @@ int resolve(const char *hostname, unsigned short port, struct sockaddr_storage *ss, size_t *sslen, int af); +/* Resolves the given hostname or IP address with getaddrinfo, and stores + all results into a linked list. + The rest of behavior is same as resolve(). */ +int resolve_multi(const char *hostname, unsigned short port, + struct sockaddr_list *sl, int af); + +void free_sockaddr_list(struct sockaddr_list *sl); + int fdinfo_close(struct fdinfo *fdn); int fdinfo_recv(struct fdinfo *fdn, char *buf, size_t size); int fdinfo_send(struct fdinfo *fdn, const char *buf, size_t size); Index: ncat/ncat_main.c =================================================================== --- ncat/ncat_main.c (revision 32430) +++ ncat/ncat_main.c (working copy) @@ -649,15 +649,20 @@ } #endif /* HAVE_SYS_UN_H */ + /* Create a static target address, because at least one target address must be always allocated */ + static struct sockaddr_list statictargetaddr; + statictargetaddr.next = NULL; /* List with one item */ + targetaddrs = &statictargetaddr; + /* Will be AF_INET or AF_INET6 or AF_UNIX when valid */ - memset(&targetss.storage, 0, sizeof(targetss.storage)); - targetss.storage.ss_family = AF_UNSPEC; - httpconnect.storage = socksconnect.storage = srcaddr.storage = targetss.storage; + memset(&srcaddr.storage, 0, sizeof(srcaddr.storage)); + srcaddr.storage.ss_family = AF_UNSPEC; + targetaddrs->addr.storage = httpconnect.storage = socksconnect.storage = srcaddr.storage; /* Clear the listenaddrs array */ int i; for (i = 0; i < NUM_LISTEN_ADDRS; i++) { - listenaddrs[i].storage = targetss.storage; + listenaddrs[i].storage = srcaddr.storage; } if (proxyaddr) { @@ -740,10 +745,10 @@ } else { #if HAVE_SYS_UN_H if (o.af == AF_UNIX) { - memset(&targetss.storage, 0, sizeof(struct sockaddr_un)); - targetss.un.sun_family = AF_UNIX; - strncpy(targetss.un.sun_path, argv[optind], sizeof(targetss.un.sun_path)); - targetsslen = SUN_LEN(&targetss.un); + memset(&targetaddrs->addr.storage, 0, sizeof(struct sockaddr_un)); + targetaddrs->addr.un.sun_family = AF_UNIX; + strncpy(targetaddrs->addr.un.sun_path, argv[optind], sizeof(targetaddrs->addr.un.sun_path)); + targetaddrs->addrlen = SUN_LEN(&targetaddrs->addr.un); o.target = argv[optind]; optind++; } else @@ -754,7 +759,7 @@ o.target = argv[optind]; /* resolve hostname */ - rc = resolve(o.target, 0, &targetss.storage, &targetsslen, o.af); + rc = resolve_multi(o.target, 0, targetaddrs, o.af); if (rc != 0) bye("Could not resolve hostname \"%s\": %s.", o.target, gai_strerror(rc)); optind++; @@ -790,21 +795,26 @@ o.portno = (unsigned short) long_port; } - if (targetss.storage.ss_family == AF_INET) - targetss.in.sin_port = htons(o.portno); + struct sockaddr_list *targetaddrs_item = targetaddrs; + while (targetaddrs_item != NULL) + { + if (targetaddrs_item->addr.storage.ss_family == AF_INET) + targetaddrs_item->addr.in.sin_port = htons(o.portno); #ifdef HAVE_IPV6 - else if (targetss.storage.ss_family == AF_INET6) - targetss.in6.sin6_port = htons(o.portno); + else if (targetaddrs_item->addr.storage.ss_family == AF_INET6) + targetaddrs_item->addr.in6.sin6_port = htons(o.portno); #endif #if HAVE_SYS_UN_H - /* If we use Unix domain sockets, we have to count with them. */ - else if (targetss.storage.ss_family == AF_UNIX) - ; /* Do nothing. */ + /* If we use Unix domain sockets, we have to count with them. */ + else if (targetaddrs_item->addr.storage.ss_family == AF_UNIX) + ; /* Do nothing. */ #endif - else if (targetss.storage.ss_family == AF_UNSPEC) - ; /* Leave unspecified. */ - else - bye("Unknown address family %d.", targetss.storage.ss_family); + else if (targetaddrs_item->addr.storage.ss_family == AF_UNSPEC) + ; /* Leave unspecified. */ + else + bye("Unknown address family %d.", targetaddrs_item->addr.storage.ss_family); + targetaddrs_item = targetaddrs_item->next; + } if (srcport != -1) { if (o.listen) { @@ -816,7 +826,7 @@ /* We have a source port but not an explicit source address; fill in an unspecified address of the same family as the target. */ - srcaddr.storage.ss_family = targetss.storage.ss_family; + srcaddr.storage.ss_family = targetaddrs->addr.storage.ss_family; if (srcaddr.storage.ss_family == AF_INET) srcaddr.in.sin_addr.s_addr = INADDR_ANY; else if (srcaddr.storage.ss_family == AF_INET6) @@ -835,17 +845,17 @@ * need to reverse these address structures to avoid any further confusion */ if (httpconnect.storage.ss_family != AF_UNSPEC) { - union sockaddr_u tmp = targetss; - size_t tmplen = targetsslen; - targetss = httpconnect; - targetsslen = httpconnectlen; + union sockaddr_u tmp = targetaddrs->addr; + size_t tmplen = targetaddrs->addrlen; + targetaddrs->addr = httpconnect; + targetaddrs->addrlen = httpconnectlen; httpconnect = tmp; httpconnectlen = tmplen; } else if (socksconnect.storage.ss_family != AF_UNSPEC) { - union sockaddr_u tmp = targetss; - size_t tmplen = targetsslen; - targetss = socksconnect; - targetsslen = socksconnectlen; + union sockaddr_u tmp = targetaddrs->addr; + size_t tmplen = targetaddrs->addrlen; + targetaddrs->addr = socksconnect; + targetaddrs->addrlen = socksconnectlen; socksconnect = tmp; socksconnectlen = tmplen; } @@ -927,8 +937,8 @@ bye("/bin/sh is not executable, so `-c' won't work."); #endif - if (targetss.storage.ss_family != AF_UNSPEC) { - listenaddrs[num_listenaddrs++] = targetss; + if (targetaddrs->addr.storage.ss_family != AF_UNSPEC) { + listenaddrs[num_listenaddrs++] = targetaddrs->addr; } else { size_t ss_len; int rc; Index: ncat/test/ncat-test.pl =================================================================== --- ncat/test/ncat-test.pl (revision 32430) +++ ncat/test/ncat-test.pl (working copy) @@ -440,6 +440,7 @@ NCAT_LOCAL_PORT=.+\n NCAT_PROTO=.+ /x; +#/ } # Ignore broken pipe signals that result when trying to read from a terminated @@ -492,6 +493,78 @@ }; kill_children; +($s_pid, $s_out, $s_in) = ncat("-4", "-lk"); +test "Server -4 default listen address and port, Client connects to 'localhost'", +sub { + my ($pid, $code); + local $SIG{CHLD} = sub { }; + + my ($c_pid, $c_out, $c_in) = ncat("localhost"); + syswrite($c_in, "abc\n"); + close($c_in); + do { + $pid = waitpid($c_pid, 0); + } while ($pid > 0 && $pid != $c_pid); + $pid == $c_pid or die; + $code = $? >> 8; + $code == 0 or die "Exit code was $code, not 0"; +}; +kill_children; + +($s_pid, $s_out, $s_in) = ncat("-6", "-lk"); +test "Server -6 default listen address and port, Client connects to 'localhost'", +sub { + my ($pid, $code); + local $SIG{CHLD} = sub { }; + + my ($c_pid, $c_out, $c_in) = ncat("localhost"); + syswrite($c_in, "abc\n"); + close($c_in); + do { + $pid = waitpid($c_pid, 0); + } while ($pid > 0 && $pid != $c_pid); + $pid == $c_pid or die; + $code = $? >> 8; + $code == 0 or die "Exit code was $code, not 0"; +}; +kill_children; + +($s_pid, $s_out, $s_in) = ncat("-lk", $HOST); +test "Server default port, Client connects to 'localhost'", +sub { + my ($pid, $code); + local $SIG{CHLD} = sub { }; + + my ($c_pid, $c_out, $c_in) = ncat("localhost"); + syswrite($c_in, "abc\n"); + close($c_in); + do { + $pid = waitpid($c_pid, 0); + } while ($pid > 0 && $pid != $c_pid); + $pid == $c_pid or die; + $code = $? >> 8; + $code == 0 or die "Exit code was $code, not 0"; +}; +kill_children; + +($s_pid, $s_out, $s_in) = ncat("-lk", $IPV6_ADDR); +test "Server IPv6 listen address and default port, Client connects to 'localhost'", +sub { + my ($pid, $code); + local $SIG{CHLD} = sub { }; + + my ($c_pid, $c_out, $c_in) = ncat("localhost"); + syswrite($c_in, "abc\n"); + close($c_in); + do { + $pid = waitpid($c_pid, 0); + } while ($pid > 0 && $pid != $c_pid); + $pid == $c_pid or die; + $code = $? >> 8; + $code == 0 or die "Exit code was $code, not 0"; +}; +kill_children; + # Test server with no hostname. ($s_pid, $s_out, $s_in) = ncat("-l", $HOST); test "Server default port", Index: ncat/util.c =================================================================== --- ncat/util.c (revision 32430) +++ ncat/util.c (working copy) @@ -490,7 +490,7 @@ /* We need a socket that can be inherited by child processes in ncat_exec_win.c, for --exec and --sh-exec. inheritable_socket is from nbase. */ - sock = inheritable_socket(targetss.storage.ss_family, type, 0); + sock = inheritable_socket(targetaddrs->addr.storage.ss_family, type, 0); if (srcaddr.storage.ss_family != AF_UNSPEC) { size_t sa_len; @@ -507,7 +507,7 @@ } if (sock != -1) { - if (connect(sock, &targetss.sockaddr, (int) targetsslen) != -1) + if (connect(sock, &targetaddrs->addr.sockaddr, (int) targetaddrs->addrlen) != -1) return sock; else if (socket_errno() == EINPROGRESS || socket_errno() == EAGAIN) return sock;