int do_connect(int type) { int sock = 0; if (type != SOCK_STREAM && type != SOCK_DGRAM) return -1; /* 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); if (srcaddr.storage.ss_family != AF_UNSPEC) { size_t sa_len; #ifdef HAVE_SOCKADDR_SA_LEN sa_len = srcaddr.sockaddr.sa_len; #else sa_len = sizeof(srcaddr); #endif if (bind(sock, &srcaddr.sockaddr, sa_len) < 0) { bye("bind to %s:%hu: %s.", inet_socktop(&srcaddr), inet_port(&srcaddr), socket_strerror(socket_errno())); } } if (sock != -1) { if (connect(sock, &targetss.sockaddr, (int) targetsslen) != -1) return sock; else if (socket_errno() == EINPROGRESS || socket_errno() == EAGAIN) return sock; } return -1; }
/* Just like inet_socktop, but it puts IPv6 addresses in square brackets. */ static const char *sock_to_url(const union sockaddr_u *su) { static char buf[INET6_ADDRSTRLEN + 32]; const char *host_str; unsigned short port; host_str = inet_socktop(su); port = inet_port(su); if (su->storage.ss_family == AF_INET) Snprintf(buf, sizeof(buf), "%s:%hu", host_str, port); else if (su->storage.ss_family == AF_INET6) Snprintf(buf, sizeof(buf), "[%s]:%hu]", host_str, port); else bye("Unknown address family in sock_to_url_host."); return buf; }
/* Return a listening socket after setting various characteristics on it. Returns -1 on error. */ int do_listen(int type, int proto, const union sockaddr_u *srcaddr_u) { int sock = 0, option_on = 1; size_t sa_len; if (type != SOCK_STREAM && type != SOCK_DGRAM) return -1; /* 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(srcaddr_u->storage.ss_family, type, proto); if (sock < 0) return -1; Setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(int)); /* IPPROTO_IPV6 is defined in Visual C++ only when _WIN32_WINNT >= 0x501. Nbase's nbase_winunix.h defines _WIN32_WINNT to a lower value for compatibility with older versions of Windows. This code disables IPv6 sockets that also receive IPv4 connections. This is the default on Windows anyway so it doesn't make a difference. http://support.microsoft.com/kb/950688 http://msdn.microsoft.com/en-us/library/bb513665 */ #ifdef IPPROTO_IPV6 #ifdef IPV6_V6ONLY if (srcaddr_u->storage.ss_family == AF_INET6) { int set = 1; /* Tell it to not try and bind to IPV4 */ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &set, sizeof(set)) == -1) die("Unable to set IPV6 socket to bind only to IPV6"); } #endif #endif #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) { #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) { #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)); } if (o.test) logtest("LISTEN\n"); return sock; }
int ncat_connect(void) { nsock_pool mypool; int rc; /* Unless explicitely asked not to do so, ncat uses the * fallback nsock engine to maximize compatibility between * operating systems and the different use cases. */ if (!o.nsock_engine) nsock_set_default_engine("select"); /* Create an nsock pool */ if ((mypool = nsp_new(NULL)) == NULL) bye("Failed to create nsock_pool."); if (o.debug >= 6) nsock_set_loglevel(mypool, NSOCK_LOG_DBG_ALL); else if (o.debug >= 3) nsock_set_loglevel(mypool, NSOCK_LOG_DBG); else if (o.debug >= 1) nsock_set_loglevel(mypool, NSOCK_LOG_INFO); else nsock_set_loglevel(mypool, NSOCK_LOG_ERROR); /* Allow connections to broadcast addresses. */ nsp_setbroadcast(mypool, 1); #ifdef HAVE_OPENSSL set_ssl_ctx_options((SSL_CTX *) nsp_ssl_init(mypool)); #endif if (httpconnect.storage.ss_family == AF_UNSPEC && socksconnect.storage.ss_family == AF_UNSPEC) { /* A non-proxy connection. Create an iod for a new socket. */ cs.sock_nsi = nsi_new(mypool, NULL); if (cs.sock_nsi == NULL) bye("Failed to create nsock_iod."); 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) { 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)); if (o.numsrcrtes) { unsigned char *ipopts = NULL; size_t ipoptslen = 0; 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); nsi_set_ipoptions(cs.sock_nsi, ipopts, ipoptslen); 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, inet_port(&targetss)); } #ifdef HAVE_OPENSSL else if (o.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.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; int len; char *line; size_t n; if (httpconnect.storage.ss_family != AF_UNSPEC) { connect_socket = do_proxy_http(); if (connect_socket == -1) return 1; } else if (socksconnect.storage.ss_family != AF_UNSPEC) { struct socket_buffer stateful_buf; struct socks4_data socks4msg; char socksbuf[8]; connect_socket = do_connect(SOCK_STREAM); if (connect_socket == -1) { loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno())); return 1; } socket_buffer_init(&stateful_buf, connect_socket); if (o.verbose) { loguser("Connected to proxy %s:%hu\n", inet_socktop(&targetss), inet_port(&targetss)); } /* Fill the socks4_data struct */ zmem(&socks4msg, sizeof(socks4msg)); socks4msg.version = SOCKS4_VERSION; socks4msg.type = SOCKS_CONNECT; socks4msg.port = socksconnect.in.sin_port; socks4msg.address = socksconnect.in.sin_addr.s_addr; if (o.proxy_auth) Strncpy(socks4msg.username, (char *) o.proxy_auth, sizeof(socks4msg.username)); len = 8 + strlen(socks4msg.username) + 1; if (send(connect_socket, (char *) &socks4msg, len, 0) < 0) { loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); return 1; } /* The size of the socks4 response is 8 bytes. So read exactly 8 bytes from the buffer */ if (socket_buffer_readcount(&stateful_buf, socksbuf, 8) < 0) { loguser("Error: short reponse from proxy.\n"); return 1; } if (socksbuf[1] != 90) { loguser("Proxy connection failed.\n"); return 1; } /* Clear out whatever is left in the socket buffer which may be already sent by proxy server along with http response headers. */ line = socket_buffer_remainder(&stateful_buf, &n); /* Write the leftover data to stdout. */ Write(STDOUT_FILENO, line, n); } /* Once the proxy negotiation is done, Nsock takes control of the socket. */ cs.sock_nsi = nsi_new2(mypool, connect_socket, NULL); /* Create IOD for nsp->stdin */ if ((cs.stdin_nsi = nsi_new2(mypool, 0, NULL)) == NULL) bye("Failed to create stdin nsiod."); post_connect(mypool, cs.sock_nsi); } /* connect */ rc = nsock_loop(mypool, -1); if (o.verbose) { struct timeval end_time; double time; gettimeofday(&end_time, NULL); time = TIMEVAL_MSEC_SUBTRACT(end_time, start_time) / 1000.0; loguser("%lu bytes sent, %lu bytes received in %.2f seconds.\n", nsi_get_write_count(cs.sock_nsi), nsi_get_read_count(cs.sock_nsi), time); } #if HAVE_SYS_UN_H if (o.af == AF_UNIX && o.udp) { if (o.verbose) loguser("Deleting source DGRAM Unix domain socket. [%s]\n", srcaddr.un.sun_path); unlink(srcaddr.un.sun_path); } #endif nsp_delete(mypool); return rc == NSOCK_LOOP_ERROR ? 1 : 0; }
/* Accept a connection on a listening socket. Allow or deny the connection. Fork a command if o.cmdexec is set. Otherwise, add the new socket to the watch set. */ static void handle_connection(int socket_accept) { union sockaddr_u remoteaddr; socklen_t ss_len; struct fdinfo s = { 0 }; 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); if (s.fd < 0) { if (o.debug) logdebug("Error in accept: %s\n", strerror(errno)); close(s.fd); return; } 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 loguser("Connection from %s.\n", inet_socktop(&remoteaddr)); } if (!o.keepopen && !o.broker) { int i; for (i = 0; i < num_listenaddrs; i++) { Close(listen_socket[i]); FD_CLR(listen_socket[i], &master_readfds); rm_fd(&client_fdlist, listen_socket[i]); } } 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(); if (conn_count >= o.conn_limit) { if (o.verbose) loguser("New connection denied: connection limit reached (%d)\n", conn_count); Close(s.fd); return; } if (!allow_access(&remoteaddr)) { if (o.verbose) loguser("New connection denied: not allowed\n"); Close(s.fd); return; } s.remoteaddr = remoteaddr; conn_inc++; unblock_socket(s.fd); #ifdef HAVE_OPENSSL if (o.ssl) { /* Add the socket to the necessary descriptor lists. */ FD_SET(s.fd, &sslpending_fds); FD_SET(s.fd, &master_readfds); FD_SET(s.fd, &master_writefds); /* Add it to our list of fds too for maintaining maxfd. */ if (add_fdinfo(&client_fdlist, &s) < 0) bye("add_fdinfo() failed."); } else #endif post_handle_connection(s); }