diff --git a/ncat/config.h.in b/ncat/config.h.in index 01ac1db56..3ef265ebf 100644 --- a/ncat/config.h.in +++ b/ncat/config.h.in @@ -119,6 +119,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UN_H + /* Define to 1 if you have the `vprintf' function. */ #undef HAVE_VPRINTF diff --git a/ncat/configure b/ncat/configure index d7781ee54..edcf08de9 100755 --- a/ncat/configure +++ b/ncat/configure @@ -3770,7 +3770,7 @@ fi done -for ac_header in fcntl.h limits.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/param.h sys/socket.h sys/time.h sys/timeb.h unistd.h +for ac_header in fcntl.h limits.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/param.h sys/socket.h sys/time.h sys/timeb.h unistd.h sys/un.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" diff --git a/ncat/configure.ac b/ncat/configure.ac index 1a1116c99..8ee523c2c 100644 --- a/ncat/configure.ac +++ b/ncat/configure.ac @@ -39,7 +39,7 @@ AC_PATH_TOOL([STRIP], [strip], [/bin/true]) # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT -AC_CHECK_HEADERS([fcntl.h limits.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/param.h sys/socket.h sys/time.h sys/timeb.h unistd.h]) +AC_CHECK_HEADERS([fcntl.h limits.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/param.h sys/socket.h sys/time.h sys/timeb.h unistd.h sys/un.h]) # Checks for typedefs, structures, and compiler characteristics. AC_HEADER_STAT diff --git a/ncat/docs/ncat.usage.txt b/ncat/docs/ncat.usage.txt index f776b7fca..475ebc987 100644 --- a/ncat/docs/ncat.usage.txt +++ b/ncat/docs/ncat.usage.txt @@ -5,6 +5,7 @@ Options taking a time assume seconds. Append 'ms' for milliseconds, 's' for seconds, 'm' for minutes, or 'h' for hours (e.g. 500ms). -4 Use IPv4 only -6 Use IPv6 only + -U, --unixsock Use Unix domain sockets only -C, --crlf Use CRLF for EOL sequence -c, --sh-exec Executes the given command via /bin/sh -e, --exec Executes the given command diff --git a/ncat/ncat_connect.c b/ncat/ncat_connect.c index d27154dad..3c05d3809 100644 --- a/ncat/ncat_connect.c +++ b/ncat/ncat_connect.c @@ -213,6 +213,7 @@ static void set_ssl_ctx_options(SSL_CTX *ctx) static void connect_report(nsock_iod nsi) { union sockaddr_u peer; + zmem(&peer, sizeof(peer.storage)); nsi_getlastcommunicationinfo(nsi, NULL, NULL, NULL, &peer.sockaddr, sizeof(peer.storage)); @@ -244,10 +245,20 @@ static void connect_report(nsock_iod nsi) assert(ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)) != NULL); loguser("SHA-1 fingerprint: %s\n", digest_buf); } else { - loguser("Connected to %s:%hu.\n", inet_socktop(&peer), nsi_peerport(nsi)); +#if HAVE_SYS_UN_H + if (peer.sockaddr.sa_family == AF_UNIX) + loguser("Connected to %s.\n", peer.un.sun_path); + else +#endif + loguser("Connected to %s:%hu.\n", inet_socktop(&peer), nsi_peerport(nsi)); } #else - loguser("Connected to %s:%hu.\n", inet_socktop(&peer), nsi_peerport(nsi)); +#if HAVE_SYS_UN_H + if (peer.sockaddr.sa_family == AF_UNIX) + loguser("Connected to %s.\n", peer.un.sun_path); + else +#endif + loguser("Connected to %s:%hu.\n", inet_socktop(&peer), nsi_peerport(nsi)); #endif } } @@ -523,6 +534,30 @@ int ncat_connect(void) if (nsi_set_hostname(cs.sock_nsi, o.target) == -1) bye("Failed to set hostname on iod."); +#if HAVE_SYS_UN_H + /* For DGRAM UNIX socket we have to use source socket */ + if (o.af == AF_UNIX && o.udp) + { + if (srcaddr.storage.ss_family == AF_UNIX) { + nsi_set_localaddr(cs.sock_nsi, &srcaddr.storage, SUN_LEN((struct sockaddr_un *)&srcaddr.storage)); + } else { + char *tmp_name = NULL; + /* If no source socket was specified, we have to create temporary one. */ + if ((tmp_name = tempnam(NULL, "ncat.")) == NULL) + bye("Failed to create name for temporary DGRAM source Unix domain socket (tempnam)."); + + srcaddr.un.sun_family = AF_UNIX; + strncpy(srcaddr.un.sun_path, tmp_name, sizeof(srcaddr.un.sun_path)); + free (tmp_name); + + nsi_set_localaddr(cs.sock_nsi, &srcaddr.storage, SUN_LEN((struct sockaddr_un *)&srcaddr.storage)); + } + + if (o.verbose) + loguser("[%s] used as source DGRAM Unix domain socket.\n", srcaddr.un.sun_path); + } + else +#endif if (srcaddr.storage.ss_family != AF_UNSPEC) nsi_set_localaddr(cs.sock_nsi, &srcaddr.storage, sizeof(srcaddr.storage)); @@ -538,6 +573,19 @@ int ncat_connect(void) free(ipopts); /* Nsock has its own copy */ } +#if HAVE_SYS_UN_H + if (o.af == AF_UNIX) { + if (o.udp) { + nsock_connect_unixsock_datagram(mypool, cs.sock_nsi, connect_handler, NULL, + &targetss.sockaddr, + SUN_LEN((struct sockaddr_un *)&targetss.sockaddr)); + } else { + nsock_connect_unixsock_stream(mypool, cs.sock_nsi, connect_handler, o.conntimeout, + NULL, &targetss.sockaddr, + SUN_LEN((struct sockaddr_un *)&targetss.sockaddr)); + } + } else +#endif if (o.udp) { nsock_connect_udp(mypool, cs.sock_nsi, connect_handler, NULL, &targetss.sockaddr, targetsslen, @@ -659,6 +707,11 @@ int ncat_connect(void) nsi_get_read_count(cs.sock_nsi), time); } +#if HAVE_SYS_UN_H + if (o.af == AF_UNIX && o.udp) + unlink(srcaddr.un.sun_path); +#endif + nsp_delete(mypool); return rc == NSOCK_LOOP_ERROR ? 1 : 0; diff --git a/ncat/ncat_listen.c b/ncat/ncat_listen.c index d39320b53..5e80b3fdd 100644 --- a/ncat/ncat_listen.c +++ b/ncat/ncat_listen.c @@ -110,6 +110,10 @@ #include #endif +#if HAVE_SYS_UN_H +#include +#endif + #ifdef HAVE_OPENSSL #include #include @@ -354,8 +358,10 @@ static void handle_connection(int socket_accept) int conn_count; zmem(&s, sizeof(s)); + zmem(&remoteaddr, sizeof(remoteaddr.storage)); ss_len = sizeof(remoteaddr.storage); + errno = 0; s.fd = accept(socket_accept, &remoteaddr.sockaddr, &ss_len); @@ -368,6 +374,11 @@ static void handle_connection(int socket_accept) } if (o.verbose) { +#if HAVE_SYS_UN_H + if (remoteaddr.sockaddr.sa_family == AF_UNIX) + loguser("Connection from a client on Unix domain socket.\n"); + else +#endif if (o.chat) loguser("Connection from %s on file descriptor %d.\n", inet_socktop(&remoteaddr), s.fd); else @@ -383,8 +394,14 @@ static void handle_connection(int socket_accept) } } - if (o.verbose) - loguser("Connection from %s:%hu.\n", inet_socktop(&remoteaddr), inet_port(&remoteaddr)); + if (o.verbose) { +#if HAVE_SYS_UN_H + if (remoteaddr.sockaddr.sa_family == AF_UNIX) + loguser("Connection from %s.\n", remoteaddr.un.sun_path); + else +#endif + loguser("Connection from %s:%hu.\n", inet_socktop(&remoteaddr), inet_port(&remoteaddr)); + } /* Check conditions that might cause us to deny the connection. */ conn_count = get_conn_count(); @@ -776,6 +793,14 @@ static int ncat_listen_dgram(int proto) int ncat_listen() { +#if HAVE_SYS_UN_H + if (o.af == AF_UNIX) + if (o.udp) + return ncat_listen_dgram(0); + else + return ncat_listen_stream(0); + else +#endif if (o.httpserver) return ncat_http_server(); else if (o.udp) diff --git a/ncat/ncat_main.c b/ncat/ncat_main.c index df3b7e847..42d6a14a2 100644 --- a/ncat/ncat_main.c +++ b/ncat/ncat_main.c @@ -229,6 +229,9 @@ int main(int argc, char *argv[]) struct option long_options[] = { {"4", no_argument, NULL, '4'}, {"6", no_argument, NULL, '6'}, +#if HAVE_SYS_UN_H + {"unixsock", no_argument, NULL, 'U'}, +#endif {"crlf", no_argument, NULL, 'C'}, {"g", required_argument, NULL, 'g'}, {"G", required_argument, NULL, 'G'}, @@ -286,7 +289,7 @@ int main(int argc, char *argv[]) while (1) { /* handle command line arguments */ int option_index; - int c = getopt_long(argc, argv, "46Cc:e:g:G:i:km:hp:d:lo:x:ts:uvw:n", + int c = getopt_long(argc, argv, "46UCc:e:g:G:i:km:hp:d:lo:x:ts:uvw:n", long_options, &option_index); /* That's the end of the options. */ @@ -304,6 +307,11 @@ int main(int argc, char *argv[]) bye("-6 chosen when IPv6 wasn't compiled in."); #endif break; +#if HAVE_SYS_UN_H + case 'U': + o.af = AF_UNIX; + break; +#endif case 'C': o.crlf = 1; break; @@ -469,6 +477,9 @@ int main(int argc, char *argv[]) "'s' for seconds, 'm' for minutes, or 'h' for hours (e.g. 500ms).\n" " -4 Use IPv4 only\n" " -6 Use IPv6 only\n" +#if HAVE_SYS_UN_H +" -U, --unixsock Use Unix domain sockets only\n" +#endif " -C, --crlf Use CRLF for EOL sequence\n" " -c, --sh-exec Executes the given command via /bin/sh\n" " -e, --exec Executes the given command\n" @@ -536,7 +547,25 @@ int main(int argc, char *argv[]) else nbase_set_log(loguser, NULL); - /* Will be AF_INET or AF_INET6 when valid */ +#if HAVE_SYS_UN_H + /* Using Unix domain sockets, so do the checks now */ + if (o.af == AF_UNIX) { + if (proxyaddr || o.proxytype) + bye("Proxy option not supported when using Unix domain sockets."); +#ifdef HAVE_OPENSSL + if (o.ssl) + bye("SSL option not supported when using Unix domain sockets."); +#endif + if (o.broker) + bye("Connection brokering not supported when using Unix domain sockets."); + if (srcport != -1) + bye("Specifying source port when using Unix domain sockets doesn't make sense."); + if (o.numsrcrtes > 0) + bye("Loose source routing not allowed when using Unix domain sockets."); + } +#endif /* HAVE_SYS_UN_H */ + + /* 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; @@ -583,12 +612,26 @@ int main(int argc, char *argv[]) /* Resolve the given source address */ if (source) { - int rc; + int rc = 0; if (o.listen) bye("-l and -s are incompatible. Specify the address and port to bind to like you would a host to connect to."); - rc = resolve(source, 0, &srcaddr.storage, &srcaddrlen, o.af); +#if HAVE_SYS_UN_H + /* if using UNIX sockets just copy the path. + * If it's not valid, it will fail later! */ + if (o.af == AF_UNIX) { + if (o.udp) { + srcaddr.un.sun_family = AF_UNIX; + strncpy(srcaddr.un.sun_path, source, sizeof(srcaddr.un.sun_path)); + srcaddrlen = SUN_LEN(&srcaddr.un); + } + else + if (o.verbose) + loguser("Specifying source socket for other than DATAGRAM Unix domain sockets have no effect.\n"); + } else +#endif + rc = resolve(source, 0, &srcaddr.storage, &srcaddrlen, o.af); if (rc != 0) bye("Could not resolve source address \"%s\": %s.", source, gai_strerror(rc)); } @@ -599,10 +642,28 @@ int main(int argc, char *argv[]) host_list_free(deny_host_list); if (optind == argc) { +#if HAVE_SYS_UN_H + if (o.af == AF_UNIX) { + if (!o.listen) + bye("You have to specify path to a socket to connect to."); + else + bye("You have to specify path to a socket to listen on."); + } +#endif /* Listen defaults to any address and DEFAULT_NCAT_PORT */ if (!o.listen) bye("You must specify a host to connect to."); } 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); + o.target = argv[optind]; + optind++; + } else +#endif /* Resolve hostname if we're given one */ if (strspn(argv[optind], "0123456789") != strlen(argv[optind])) { int rc; @@ -620,6 +681,12 @@ int main(int argc, char *argv[]) } /* Whatever's left is the port number; there should be at most one. */ +#if HAVE_SYS_UN_H + /* We do not use ports with Unix domain sockets. */ + if (o.af == AF_UNIX && optind > argc) + bye("Using Unix domain sockets and specifying port doesn't make sense."); +#endif + if (optind + 1 < argc || (o.listen && srcport != -1 && optind + 1 == argc)) { loguser("Got more than one port specification:"); if (o.listen && srcport != -1) @@ -644,6 +711,11 @@ int main(int argc, char *argv[]) #ifdef HAVE_IPV6 else if (targetss.storage.ss_family == AF_INET6) targetss.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. */ #endif else if (targetss.storage.ss_family == AF_UNSPEC) ; /* Leave unspecified. */ diff --git a/ncat/sockaddr_u.h b/ncat/sockaddr_u.h index 92430f259..e9cfd38ba 100644 --- a/ncat/sockaddr_u.h +++ b/ncat/sockaddr_u.h @@ -91,11 +91,16 @@ /* $Id:$ */ +#include "ncat_config.h" + #ifndef SOCKADDR_U_H_ #define SOCKADDR_U_H_ union sockaddr_u { struct sockaddr_storage storage; +#ifdef HAVE_SYS_UN_H + struct sockaddr_un un; +#endif struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr sockaddr; diff --git a/ncat/util.c b/ncat/util.c index b292bc9b3..643ae8ad4 100644 --- a/ncat/util.c +++ b/ncat/util.c @@ -406,21 +406,39 @@ int do_listen(int type, int proto, const union sockaddr_u *srcaddr_u) #endif #endif -#ifdef HAVE_SOCKADDR_SA_LEN - sa_len = srcaddr_u->sockaddr.sa_len; -#else - sa_len = sizeof(*srcaddr_u); +#ifdef HAVE_SYS_UN_H + if (srcaddr_u->storage.ss_family == AF_UNIX) + sa_len = SUN_LEN(&srcaddr_u->un); + else #endif +#ifdef HAVE_SOCKADDR_SA_LEN + sa_len = srcaddr_u->sockaddr.sa_len; +#else + sa_len = sizeof(*srcaddr_u); +#endif + if (bind(sock, &srcaddr_u->sockaddr, sa_len) < 0) { - bye("bind to %s:%hu: %s.", inet_socktop(srcaddr_u), - inet_port(srcaddr_u), socket_strerror(socket_errno())); +#ifdef HAVE_SYS_UN_H + if (srcaddr_u->storage.ss_family == AF_UNIX) + bye("bind to %s: %s.", srcaddr_u->un.sun_path, + socket_strerror(socket_errno())); + else +#endif + bye("bind to %s:%hu: %s.", inet_socktop(srcaddr_u), + inet_port(srcaddr_u), socket_strerror(socket_errno())); } if (type == SOCK_STREAM) Listen(sock, BACKLOG); - if (o.verbose) - loguser("Listening on %s:%hu\n", inet_socktop(srcaddr_u), inet_port(srcaddr_u)); + if (o.verbose) { +#ifdef HAVE_SYS_UN_H + if (srcaddr_u->storage.ss_family == AF_UNIX) + loguser("Listening on %s\n", srcaddr_u->un.sun_path); + else +#endif + loguser("Listening on %s:%hu\n", inet_socktop(srcaddr_u), inet_port(srcaddr_u)); + } return sock; } diff --git a/ncat/util.h b/ncat/util.h index b5244f6f1..bf9b42eba 100644 --- a/ncat/util.h +++ b/ncat/util.h @@ -101,6 +101,10 @@ #include #endif +#if HAVE_SYS_UN_H +#include +#endif + #ifdef HAVE_OPENSSL #include #endif