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); 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); } else { assert(status == NSE_STATUS_SUCCESS); } #ifdef HAVE_OPENSSL if (nsi_checkssl(cs.sock_nsi)) { /* Check the domain name. ssl_post_connect_check prints an error message if appropriate. */ if (!ssl_post_connect_check((SSL *) nsi_getssl(cs.sock_nsi), o.target)) bye("Certificate verification error."); } #endif connect_report(cs.sock_nsi); /* Create IOD for nsp->stdin */ if ((cs.stdin_nsi = nsi_new2(nsp, 0, NULL)) == NULL) bye("Failed to create stdin nsiod."); post_connect(nsp, nse_iod(evt)); }
/* This callback is called for every certificate in a chain. ok is true if OpenSSL's internal verification has verified the certificate. We don't change anything about the verification, we only need access to the certificates to provide diagnostics. */ static int verify_callback(int ok, X509_STORE_CTX *store) { X509 *cert = X509_STORE_CTX_get_current_cert(store); int err = X509_STORE_CTX_get_error(store); /* Print the subject, issuer, and fingerprint depending on the verbosity level. */ if ((!ok && o.verbose) || o.debug > 1) { char digest_buf[SHA1_STRING_LENGTH + 1]; loguser("Subject: "); X509_NAME_print_ex_fp(stderr, X509_get_subject_name(cert), 0, XN_FLAG_COMPAT); loguser_noprefix("\n"); loguser("Issuer: "); X509_NAME_print_ex_fp(stderr, X509_get_issuer_name(cert), 0, XN_FLAG_COMPAT); loguser_noprefix("\n"); assert(ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)) != NULL); loguser("SHA-1 fingerprint: %s\n", digest_buf); } if (!ok && o.verbose) { loguser("Certificate verification failed (%s).\n", X509_verify_cert_error_string(err)); } return ok; }
static void read_stdin_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); char *buf, *tmp = NULL; int nbytes; assert(type == NSE_TYPE_READ); if (status == NSE_STATUS_EOF) { if (o.sendonly) { /* In --send-only mode, exit after EOF on stdin. */ nsock_loop_quit(nsp); } else { shutdown(nsi_getsd(cs.sock_nsi), SHUT_WR); } return; } else 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); } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { return; } else { assert(status == NSE_STATUS_SUCCESS); } buf = nse_readbuf(evt, &nbytes); /* read from stdin */ if (o.linedelay) ncat_delay_timer(o.linedelay); if (o.crlf) { if (fix_line_endings(buf, &nbytes, &tmp, &cs.crlf_state)) buf = tmp; } nsock_write(nsp, cs.sock_nsi, write_socket_handler, -1, NULL, buf, nbytes); ncat_log_send(buf, nbytes); if (tmp) free(tmp); refresh_idle_timer(nsp); }
static void read_socket_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); char *buf; int nbytes; ncat_assert(type == NSE_TYPE_READ); if (status == NSE_STATUS_EOF) { Close(STDOUT_FILENO); /* In --recv-only mode or non-TCP mode, exit after EOF on the socket. */ if (o.proto != IPPROTO_TCP || (o.proto == IPPROTO_TCP && o.recvonly)) nsock_loop_quit(nsp); return; } else 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); } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { return; } else { ncat_assert(status == NSE_STATUS_SUCCESS); } buf = nse_readbuf(evt, &nbytes); if (o.linedelay) ncat_delay_timer(o.linedelay); if (o.telnet) dotelnet(nsi_getsd(nse_iod(evt)), (unsigned char *) buf, nbytes); /* Write socket data to stdout */ Write(STDOUT_FILENO, buf, nbytes); ncat_log_recv(buf, nbytes); nsock_readbytes(nsp, cs.sock_nsi, read_socket_handler, -1, NULL, 0); refresh_idle_timer(nsp); }
void rx_server_remove(conn_t *c) { c->stop_data = TRUE; c->mc = NULL; if (c->type == STREAM_SOUND) loguser(c, "(LEAVING)"); webserver_connection_cleanup(c); if (c->user) wrx_free("user", c->user); if (c->geo) wrx_free("geo", c->geo); int task = c->task; conn_init(c, c->type, c->rx_channel); TaskRemove(task); panic("shouldn't return"); }
static void write_socket_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); assert(type == NSE_TYPE_WRITE); 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); } else if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) { return; } else { assert(status == NSE_STATUS_SUCCESS); } /* The write to the socket was successful. Allow reading more from stdin now. */ nsock_readbytes(nsp, cs.stdin_nsi, read_stdin_handler, -1, NULL, 0); }
static void idle_timer_handler(nsock_pool nsp, nsock_event evt, void *data) { enum nse_status status = nse_status(evt); enum nse_type type = nse_type(evt); assert(type == NSE_TYPE_TIMER); if (status == NSE_STATUS_CANCELLED || status == NSE_STATUS_KILL) return; assert(status == NSE_STATUS_SUCCESS); loguser("Idle timeout expired (%d ms).\n", o.idletimeout); exit(1); }
/* Depending on verbosity, print a message that a connection was established. */ 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)); if (o.verbose) { #ifdef HAVE_OPENSSL if (nsi_checkssl(nsi)) { X509 *cert; X509_NAME *subject; char digest_buf[SHA1_STRING_LENGTH + 1]; char *fp; loguser("SSL connection to %s:%hu.", inet_socktop(&peer), nsi_peerport(nsi)); cert = SSL_get_peer_certificate((SSL *) nsi_getssl(nsi)); ncat_assert(cert != NULL); subject = X509_get_subject_name(cert); if (subject != NULL) { char buf[256]; int n; n = X509_NAME_get_text_by_NID(subject, NID_organizationName, buf, sizeof(buf)); if (n >= 0 && n <= sizeof(buf) - 1) loguser_noprefix(" %s", buf); } loguser_noprefix("\n"); fp = ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)); ncat_assert(fp == digest_buf); loguser("SHA-1 fingerprint: %s\n", digest_buf); } else { #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 #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 } }
void webserver_print_stats() { int nusers=0; conn_t *c; for (c=conns; c<&conns[RX_CHANS*2]; c++) { if (!c->arrived) continue; #if 0 if (c->type == STREAM_WATERFALL) { printf("WF%d %2d fps %7.3f %7.3f %7.3f\n", c->rx_channel, c->wf_frames, (float) c->wf_get / 1000, (float) c->wf_lock / 1000, (float) c->wf_loop / 1000); c->wf_frames = 0; } #endif if (c->type == STREAM_SOUND) { if ((c->freqHz != c->last_freqHz) || (c->mode != c->last_mode)) loguser(c, NULL); nusers++; } } static const float k = 1.0/1000.0/10.0; // kbytes/sec every 10 secs audio_kbps = audio_bytes*k; waterfall_kbps = waterfall_bytes*k; waterfall_fps = waterfall_frames/10.0; http_kbps = http_bytes*k; audio_bytes = waterfall_bytes = waterfall_frames = http_bytes = 0; time_t t; time(&t); struct tm tm; localtime_r(&t, &tm); if (tm.tm_hour != last_hour) { lprintf("(%d %s)\n", nusers, (nusers==1)? "user":"******"); last_hour = tm.tm_hour; } }
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; }
/* Return a usable socket descriptor after proxy negotiation, or -1 on any error. If any bytes are received through the proxy after negotiation, they are written to stdout. */ static int do_proxy_http(void) { struct socket_buffer sockbuf; char *request; char *status_line, *header; char *remainder; size_t len; int sd, code; int n; sd = do_connect(SOCK_STREAM); if (sd == -1) { loguser("Proxy connection failed: %s.\n", socket_strerror(socket_errno())); return -1; } status_line = NULL; header = NULL; /* First try a request with no authentication. */ request = http_connect_request(&httpconnect, &n); if (send(sd, request, n, 0) < 0) { loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); free(request); return -1; } free(request); socket_buffer_init(&sockbuf, sd); if (http_read_status_line(&sockbuf, &status_line) != 0) { loguser("Error reading proxy response Status-Line.\n"); goto bail; } code = http_parse_status_line_code(status_line); logdebug("Proxy returned status code %d.\n", code); free(status_line); status_line = NULL; if (http_read_header(&sockbuf, &header) != 0) { loguser("Error reading proxy response header.\n"); goto bail; } if (code == 407 && o.proxy_auth != NULL) { struct http_header *h; struct http_challenge challenge; close(sd); sd = -1; if (http_parse_header(&h, header) != 0) { loguser("Error parsing proxy response header.\n"); goto bail; } free(header); header = NULL; if (http_header_get_proxy_challenge(h, &challenge) == NULL) { loguser("Error getting Proxy-Authenticate challenge.\n"); http_header_free(h); goto bail; } http_header_free(h); sd = do_connect(SOCK_STREAM); if (sd == -1) { loguser("Proxy reconnection failed: %s.\n", socket_strerror(socket_errno())); goto bail; } request = http_connect_request_auth(&httpconnect, &n, &challenge); if (request == NULL) { loguser("Error building Proxy-Authorization header.\n"); http_challenge_free(&challenge); goto bail; } logdebug("Reconnection header:\n%s", request); if (send(sd, request, n, 0) < 0) { loguser("Error sending proxy request: %s.\n", socket_strerror(socket_errno())); free(request); http_challenge_free(&challenge); goto bail; } free(request); http_challenge_free(&challenge); socket_buffer_init(&sockbuf, sd); if (http_read_status_line(&sockbuf, &status_line) != 0) { loguser("Error reading proxy response Status-Line.\n"); goto bail; } code = http_parse_status_line_code(status_line); logdebug("Proxy returned status code %d.\n", code); free(status_line); status_line = NULL; if (http_read_header(&sockbuf, &header) != 0) { loguser("Error reading proxy response header.\n"); goto bail; } } free(header); header = NULL; if (code != 200) { loguser("Proxy returned status code %d.\n", code); return -1; } remainder = socket_buffer_remainder(&sockbuf, &len); Write(STDOUT_FILENO, remainder, len); return sd; bail: if (sd != -1) close(sd); if (status_line != NULL) free(status_line); if (header != NULL) free(header); return -1; }
SSL_CTX *setup_ssl_listen(void) { const SSL_METHOD *method; if (sslctx) goto done; SSL_library_init(); OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); SSL_load_error_strings(); /* RAND_status initializes the random number generator through a variety of platform-dependent methods, then returns 1 if there is enough entropy or 0 otherwise. This seems to be a good platform-independent way of seeding the generator, as well as of refusing to continue without enough entropy. */ if (!RAND_status()) bye("Failed to seed OpenSSL PRNG (RAND_status returned false)."); if (!(method = SSLv23_server_method())) bye("SSLv23_server_method(): %s.", ERR_error_string(ERR_get_error(), NULL)); if (!(sslctx = SSL_CTX_new(method))) bye("SSL_CTX_new(): %s.", ERR_error_string(ERR_get_error(), NULL)); SSL_CTX_set_options(sslctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); /* Secure ciphers list taken from Nsock. */ if (!SSL_CTX_set_cipher_list(sslctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH")) bye("Unable to set OpenSSL cipher list: %s", ERR_error_string(ERR_get_error(), NULL)); if (o.sslcert == NULL && o.sslkey == NULL) { X509 *cert; EVP_PKEY *key; char digest_buf[SHA1_STRING_LENGTH + 1]; if (o.verbose) loguser("Generating a temporary %d-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.\n", DEFAULT_KEY_BITS); if (ssl_gen_cert(&cert, &key) == 0) bye("ssl_gen_cert(): %s.", ERR_error_string(ERR_get_error(), NULL)); if (o.verbose) { char *fp; fp = ssl_cert_fp_str_sha1(cert, digest_buf, sizeof(digest_buf)); ncat_assert(fp == digest_buf); loguser("SHA-1 fingerprint: %s\n", digest_buf); } if (SSL_CTX_use_certificate(sslctx, cert) != 1) bye("SSL_CTX_use_certificate(): %s.", ERR_error_string(ERR_get_error(), NULL)); if (SSL_CTX_use_PrivateKey(sslctx, key) != 1) bye("SSL_CTX_use_PrivateKey(): %s.", ERR_error_string(ERR_get_error(), NULL)); X509_free(cert); EVP_PKEY_free(key); } else { if (o.sslcert == NULL || o.sslkey == NULL) bye("The --ssl-key and --ssl-cert options must be used together."); if (SSL_CTX_use_certificate_chain_file(sslctx, o.sslcert) != 1) bye("SSL_CTX_use_certificate_chain_file(): %s.", ERR_error_string(ERR_get_error(), NULL)); if (SSL_CTX_use_PrivateKey_file(sslctx, o.sslkey, SSL_FILETYPE_PEM) != 1) bye("SSL_CTX_use_Privatekey_file(): %s.", ERR_error_string(ERR_get_error(), NULL)); } done: return sslctx; }
/* userpass is a user:pass string (the argument to --proxy-auth). value is the value of the Proxy-Authorization header field. Returns 0 on authentication failure and nonzero on success. *stale is set to 1 if HTTP Digest credentials are valid but out of date. */ static int check_auth(const struct http_request *request, const struct http_credentials *credentials, int *stale) { if (o.proxy_auth == NULL) return 1; *stale = 0; if (credentials->scheme == AUTH_BASIC) { char *expected; int cmp; if (credentials->u.basic == NULL) return 0; /* We don't decode the received password, we encode the expected password and compare the encoded strings. */ expected = b64enc((unsigned char *) o.proxy_auth, strlen(o.proxy_auth)); cmp = strcmp(expected, credentials->u.basic); free(expected); return cmp == 0; } #if HAVE_HTTP_DIGEST else if (credentials->scheme == AUTH_DIGEST) { char *username, *password; char *proxy_auth; struct timeval nonce_tv, now; int nonce_age; int ret; /* Split up the proxy auth argument. */ proxy_auth = Strdup(o.proxy_auth); username = strtok(proxy_auth, ":"); password = strtok(NULL, ":"); if (password == NULL) { free(proxy_auth); return 0; } ret = http_digest_check_credentials(username, "Ncat", password, request->method, credentials); free(proxy_auth); if (!ret) return 0; /* The nonce checks out as one we issued and it matches what we expect given the credentials. Now check if it's too old. */ if (credentials->u.digest.nonce == NULL || http_digest_nonce_time(credentials->u.digest.nonce, &nonce_tv) == -1) return 0; gettimeofday(&now, NULL); if (TIMEVAL_AFTER(nonce_tv, now)) return 0; nonce_age = TIMEVAL_SEC_SUBTRACT(now, nonce_tv); if (nonce_age > HTTP_DIGEST_NONCE_EXPIRY) { if (o.verbose) loguser("Nonce is %d seconds old; rejecting.\n", nonce_age); *stale = 1; return 0; } /* To prevent replays, here we should additionally check against a list of recently used nonces, where "recently used nonce" is one that has been used to successfully authenticate within the last HTTP_DIGEST_NONCE_EXPIRY seconds. (Older than that and we don't need to keep it in the list, because the expiry test above will catch it. This isn't supported because the fork-and-process architecture of the proxy server makes it hard for us to change state in the parent process from here in the child. */ return 1; } #endif else { return 0; } }
static void http_server_handler(int c) { int code; struct socket_buffer sock; struct http_request request; char *buf; socket_buffer_init(&sock, c); #if HAVE_OPENSSL if (o.ssl) { sock.fdn.ssl = new_ssl(sock.fdn.fd); if (SSL_accept(sock.fdn.ssl) != 1) { loguser("Failed SSL connection: %s\n", ERR_error_string(ERR_get_error(), NULL)); fdinfo_close(&sock.fdn); return; } } #endif code = http_read_request_line(&sock, &buf); if (code != 0) { if (o.verbose) logdebug("Error reading Request-Line.\n"); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } if (o.debug > 1) logdebug("Request-Line: %s", buf); code = http_parse_request_line(buf, &request); free(buf); if (code != 0) { if (o.verbose) logdebug("Error parsing Request-Line.\n"); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } if (!method_is_known(request.method)) { if (o.debug > 1) logdebug("Bad method: %s.\n", request.method); http_request_free(&request); send_string(&sock.fdn, http_code2str(405)); fdinfo_close(&sock.fdn); return; } code = http_read_header(&sock, &buf); if (code != 0) { if (o.verbose) logdebug("Error reading header.\n"); http_request_free(&request); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } if (o.debug > 1) logdebug("Header:\n%s", buf); code = http_request_parse_header(&request, buf); free(buf); if (code != 0) { if (o.verbose) logdebug("Error parsing header.\n"); http_request_free(&request); send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } /* Check authentication. */ if (o.proxy_auth) { struct http_credentials credentials; int ret, stale; if (http_header_get_proxy_credentials(request.header, &credentials) == NULL) { /* No credentials or a parsing error. */ send_proxy_authenticate(&sock.fdn, 0); http_request_free(&request); fdinfo_close(&sock.fdn); return; } ret = check_auth(&request, &credentials, &stale); http_credentials_free(&credentials); if (!ret) { /* Password doesn't match. */ /* RFC 2617, section 1.2: "If a proxy does not accept the credentials sent with a request, it SHOULD return a 407 (Proxy Authentication Required). */ send_proxy_authenticate(&sock.fdn, stale); http_request_free(&request); fdinfo_close(&sock.fdn); return; } } if (strcmp(request.method, "CONNECT") == 0) { code = handle_connect(&sock, &request); } else if (strcmp(request.method, "GET") == 0 || strcmp(request.method, "HEAD") == 0 || strcmp(request.method, "POST") == 0) { code = handle_method(&sock, &request); } else { code = 500; } http_request_free(&request); if (code != 0) { send_string(&sock.fdn, http_code2str(code)); fdinfo_close(&sock.fdn); return; } fdinfo_close(&sock.fdn); }
static void account(u_char *pak) { struct acct *acct_pak; u_char *p, *argsizep; struct acct_rec rec; struct identity identity; char **cmd_argp; int i, errors = 0, status; acct_pak = (struct acct *) (pak + TAC_PLUS_HDR_SIZE); /* Fill out accounting record structure */ memset(&rec, 0, sizeof(struct acct_rec)); if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_WATCHDOG) rec.acct_type = ACCT_TYPE_UPDATE; if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_START) rec.acct_type = ACCT_TYPE_START; if (acct_pak->flags & TAC_PLUS_ACCT_FLAG_STOP) rec.acct_type = ACCT_TYPE_STOP; rec.authen_method = acct_pak->authen_method; rec.authen_type = acct_pak->authen_type; rec.authen_service = acct_pak->authen_service; /* start of variable length data is here */ p = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE; /* skip arg cnts */ p += acct_pak->arg_cnt; /* zero out identity struct */ memset(&identity, 0, sizeof(struct identity)); identity.username = tac_make_string(p, (int)acct_pak->user_len); p += acct_pak->user_len; identity.NAS_name = tac_strdup(session.peer); identity.NAS_port = tac_make_string(p, (int)acct_pak->port_len); p += acct_pak->port_len; if (acct_pak->port_len <= 0) { strcpy(session.port, "unknown-port"); } else { strcpy(session.port, identity.NAS_port); } identity.NAC_address = tac_make_string(p, (int)acct_pak->rem_addr_len); p += acct_pak->rem_addr_len; identity.priv_lvl = acct_pak->priv_lvl; rec.identity = &identity; /* Now process cmd args */ argsizep = pak + TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE; cmd_argp = (char **)tac_malloc(acct_pak->arg_cnt * sizeof(char *)); for (i = 0; i < (int)acct_pak->arg_cnt; i++) { cmd_argp[i] = tac_make_string(p, *argsizep); p += *argsizep++; } rec.args = cmd_argp; rec.num_args = acct_pak->arg_cnt; #ifdef MAXSESS /* Tally for MAXSESS counting */ loguser(&rec); #endif /* Do accounting */ if (wtmpfile) errors = do_wtmp(&rec); if (session.acctfile != NULL) errors += do_acct_file(&rec); if (session.flags & SESS_FLAG_ACCTSYSL) errors += do_acct_syslog(&rec); if (errors) { status = TAC_PLUS_ACCT_STATUS_ERROR; } else { status = TAC_PLUS_ACCT_STATUS_SUCCESS; } send_acct_reply(status, rec.msg, rec.admin_msg); free(identity.username); free(identity.NAS_name); free(identity.NAS_port); free(identity.NAC_address); for (i = 0; i < (int)acct_pak->arg_cnt; i++) { free(cmd_argp[i]); } free(cmd_argp); if (rec.msg) free(rec.msg); if (rec.admin_msg) free(rec.admin_msg); }
/* 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); }
/* This is sufficiently different from the TCP code (wrt SSL, etc) that it * resides in its own simpler function */ static int ncat_listen_dgram(int proto) { struct { int fd; union sockaddr_u addr; } sockfd[NUM_LISTEN_ADDRS]; int i, fdn = -1; int fdmax, nbytes, n, fds_ready; char buf[DEFAULT_UDP_BUF_LEN] = { 0 }; char *tempbuf = NULL; fd_set read_fds; union sockaddr_u remotess; socklen_t sslen = sizeof(remotess.storage); struct timeval tv; struct timeval *tvp = NULL; unsigned int num_sockets; for (i = 0; i < NUM_LISTEN_ADDRS; i++) { sockfd[i].fd = -1; sockfd[i].addr.storage.ss_family = AF_UNSPEC; } FD_ZERO(&read_fds); /* Initialize remotess struct so recvfrom() doesn't hit the fan.. */ zmem(&remotess.storage, sizeof(remotess.storage)); remotess.storage.ss_family = o.af; #ifdef WIN32 set_pseudo_sigchld_handler(decrease_conn_count); #else /* Reap on SIGCHLD */ Signal(SIGCHLD, sigchld_handler); /* Ignore the SIGPIPE that occurs when a client disconnects suddenly and we send data to it before noticing. */ Signal(SIGPIPE, SIG_IGN); #endif /* Not sure if this problem exists on Windows, but fcntl and /dev/null don't */ #ifndef WIN32 /* Check whether stdin is closed. Because we treat this fd specially, we * can't risk it being reopened for an incoming connection, so we'll hold * it open instead. */ if (fcntl(STDIN_FILENO, F_GETFD) == -1 && errno == EBADF) { logdebug("stdin is closed, attempting to reserve STDIN_FILENO\n"); i = open("/dev/null", O_RDONLY); if (i >= 0 && i != STDIN_FILENO) { /* Oh well, we tried */ logdebug("Couldn't reserve STDIN_FILENO\n"); close(i); } } #endif /* set for selecting udp listening sockets */ fd_set listen_fds; fd_list_t listen_fdlist; FD_ZERO(&listen_fds); init_fdlist(&listen_fdlist, num_listenaddrs); num_sockets = 0; for (i = 0; i < num_listenaddrs; i++) { /* create the UDP listen sockets */ sockfd[num_sockets].fd = do_listen(SOCK_DGRAM, proto, &listenaddrs[i]); if (sockfd[num_sockets].fd == -1) { if (o.debug > 0) logdebug("do_listen(\"%s\"): %s\n", inet_ntop_ez(&listenaddrs[i].storage, sizeof(listenaddrs[i].storage)), socket_strerror(socket_errno())); continue; } FD_SET(sockfd[num_sockets].fd, &listen_fds); add_fd(&listen_fdlist, sockfd[num_sockets].fd); sockfd[num_sockets].addr = listenaddrs[i]; num_sockets++; } if (num_sockets == 0) { if (num_listenaddrs == 1) bye("Unable to open listening socket on %s: %s", inet_ntop_ez(&listenaddrs[0].storage, sizeof(listenaddrs[0].storage)), socket_strerror(socket_errno())); else bye("Unable to open any listening sockets."); } if (o.idletimeout > 0) tvp = &tv; while (1) { int i, j, conn_count, socket_n; if (fdn != -1) { /*remove socket descriptor which is burnt */ FD_CLR(sockfd[fdn].fd, &listen_fds); rm_fd(&listen_fdlist, sockfd[fdn].fd); /* Rebuild the udp socket which got burnt */ sockfd[fdn].fd = do_listen(SOCK_DGRAM, proto, &sockfd[fdn].addr); if (sockfd[fdn].fd == -1) bye("do_listen: %s", socket_strerror(socket_errno())); FD_SET(sockfd[fdn].fd, &listen_fds); add_fd(&listen_fdlist, sockfd[fdn].fd); } fdn = -1; socket_n = -1; fd_set fds; FD_ZERO(&fds); while (1) { /* * We just select to get a list of sockets which we can talk to */ if (o.debug > 1) logdebug("selecting, fdmax %d\n", listen_fdlist.fdmax); fds = listen_fds; if (o.idletimeout > 0) ms_to_timeval(tvp, o.idletimeout); fds_ready = fselect(listen_fdlist.fdmax + 1, &fds, NULL, NULL, tvp); if (o.debug > 1) logdebug("select returned %d fds ready\n", fds_ready); if (fds_ready == 0) bye("Idle timeout expired (%d ms).", o.idletimeout); /* * Figure out which listening socket got a connection. This loop should * really call a function for each ready socket instead of breaking on * the first one. */ for (i = 0; i <= listen_fdlist.fdmax && fds_ready > 0; i++) { /* Loop through descriptors until there is something ready */ if (!FD_ISSET(i, &fds)) continue; /* Check each listening socket */ for (j = 0; j < num_sockets; j++) { if (i == sockfd[j].fd) { if (o.debug > 1) logdebug("Valid descriptor %d \n", i); fdn = j; socket_n = i; break; } } /* if we found a valid socket break */ if (fdn != -1) { fds_ready--; break; } } /* Make sure someone connected */ if (fdn == -1) continue; /* * We just peek so we can get the client connection details without * removing anything from the queue. Sigh. */ nbytes = recvfrom(socket_n, buf, sizeof(buf), MSG_PEEK, &remotess.sockaddr, &sslen); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } /* 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); } else if (!allow_access(&remotess)) { if (o.verbose) loguser("New connection denied: not allowed\n"); } else { /* Good to go. */ break; } /* Dump the current datagram */ nbytes = recv(socket_n, buf, sizeof(buf), 0); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } ncat_log_recv(buf, nbytes); } if (o.debug > 1) logdebug("Valid Connection from %d\n", socket_n); conn_inc++; /* * We're using connected udp. This has the down side of only * being able to handle one udp client at a time */ Connect(socket_n, &remotess.sockaddr, sslen); /* clean slate for buf */ zmem(buf, sizeof(buf)); /* are we executing a command? then do it */ if (o.cmdexec) { struct fdinfo info = { 0 }; info.fd = socket_n; if (o.keepopen) netrun(&info, o.cmdexec); else netexec(&info, o.cmdexec); continue; } FD_SET(socket_n, &read_fds); FD_SET(STDIN_FILENO, &read_fds); fdmax = socket_n; /* stdin -> socket and socket -> stdout */ while (1) { fd_set fds; fds = read_fds; if (o.debug > 1) logdebug("udp select'ing\n"); if (o.idletimeout > 0) ms_to_timeval(tvp, o.idletimeout); fds_ready = fselect(fdmax + 1, &fds, NULL, NULL, tvp); if (fds_ready == 0) bye("Idle timeout expired (%d ms).", o.idletimeout); if (FD_ISSET(STDIN_FILENO, &fds)) { nbytes = Read(STDIN_FILENO, buf, sizeof(buf)); if (nbytes <= 0) { if (nbytes < 0 && o.verbose) { logdebug("Error reading from stdin: %s\n", strerror(errno)); } else if (nbytes == 0 && o.debug) { logdebug("EOF on stdin\n"); } FD_CLR(STDIN_FILENO, &read_fds); if (nbytes < 0) return 1; continue; } if (o.crlf) fix_line_endings((char *) buf, &nbytes, &tempbuf, &crlf_state); if (!o.recvonly) { if (tempbuf != NULL) n = send(socket_n, tempbuf, nbytes, 0); else n = send(socket_n, buf, nbytes, 0); if (n < nbytes) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } ncat_log_send(buf, nbytes); } if (tempbuf != NULL) { free(tempbuf); tempbuf = NULL; } } if (FD_ISSET(socket_n, &fds)) { nbytes = recv(socket_n, buf, sizeof(buf), 0); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } ncat_log_recv(buf, nbytes); if (!o.sendonly) Write(STDOUT_FILENO, buf, nbytes); } zmem(buf, sizeof(buf)); } } return 0; }
/* 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; }
/* This is sufficiently different from the TCP code (wrt SSL, etc) that it * resides in its own simpler function */ static int ncat_listen_dgram(int proto) { int sockfd[NUM_LISTEN_ADDRS]; int i, fdn = -1; int fdmax, nbytes, fds_ready; char buf[DEFAULT_UDP_BUF_LEN] = {0}; char *tempbuf = NULL; fd_set read_fds; union sockaddr_u remotess; socklen_t sslen = sizeof(remotess.storage); for (i = 0; i < NUM_LISTEN_ADDRS; i++) { sockfd[i] = -1; } FD_ZERO(&read_fds); /* Initialize remotess struct so recvfrom() doesn't hit the fan.. */ zmem(&remotess.storage, sizeof(remotess.storage)); remotess.storage.ss_family = o.af; #ifdef WIN32 set_pseudo_sigchld_handler(decrease_conn_count); #else /* Reap on SIGCHLD */ Signal(SIGCHLD, sigchld_handler); /* Ignore the SIGPIPE that occurs when a client disconnects suddenly and we send data to it before noticing. */ Signal(SIGPIPE, SIG_IGN); #endif /* set for selecting udp listening sockets */ fd_set listen_fds; fd_list_t listen_fdlist; FD_ZERO(&listen_fds); init_fdlist(&listen_fdlist, num_listenaddrs); for (i = 0; i < num_listenaddrs; i++) { /* create the UDP listen sockets */ sockfd[i] = do_listen(SOCK_DGRAM, proto, &listenaddrs[i]); FD_SET(sockfd[i],&listen_fds); add_fd(&listen_fdlist, sockfd[i]); } while (1) { int i, j, conn_count, socket_n; if (fdn != -1) { /*remove socket descriptor which is burnt */ FD_CLR(sockfd[fdn], &listen_fds); rm_fd(&listen_fdlist, sockfd[fdn]); /* Rebuild the udp socket which got burnt */ sockfd[fdn] = do_listen(SOCK_DGRAM, proto, &listenaddrs[fdn]); FD_SET(sockfd[fdn],&listen_fds); add_fd(&listen_fdlist, sockfd[fdn]); } fdn = -1; socket_n = -1; fd_set fds; FD_ZERO(&fds); while (1) { /* * We just select to get a list of sockets which we can talk to */ if (o.debug > 1) logdebug("selecting, fdmax %d\n", listen_fdlist.fdmax); fds = listen_fds; fds_ready = fselect(listen_fdlist.fdmax + 1, &fds, NULL, NULL, NULL); if (o.debug > 1) logdebug("select returned %d fds ready\n", fds_ready); /* * Figure out which listening socket got a connection. This loop should * really call a function for each ready socket instead of breaking on * the first one. */ for (i = 0; i <= listen_fdlist.fdmax && fds_ready >0; i++) { /* Loop through descriptors until there is something ready */ if (!FD_ISSET(i, &fds)) continue; /* Check each listening socket */ for (j = 0; j < num_listenaddrs; j++) { if (i == sockfd[j]) { if (o.debug >1) logdebug("Valid descriptor %d \n", i); fdn = j; socket_n = i; break; } } /* if we found a valid socket break */ if (fdn != -1) { fds_ready--; break; } } /* Make sure someone connected */ if (fdn == -1) continue; /* * We just peek so we can get the client connection details without * removing anything from the queue. Sigh. */ nbytes = Recvfrom(socket_n, buf, sizeof(buf), MSG_PEEK, &remotess.sockaddr, &sslen); /* 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); } else if (!allow_access(&remotess)) { if (o.verbose) loguser("New connection denied: not allowed\n"); } else { /* Good to go. */ break; } /* Dump the current datagram */ Recv(socket_n, buf, sizeof(buf), 0); } if (o.debug > 1) logdebug("Valid Connection from %d\n", socket_n); conn_inc++; /* * We're using connected udp. This has the down side of only * being able to handle one udp client at a time */ Connect(socket_n, &remotess.sockaddr, sslen); /* clean slate for buf */ zmem(buf, sizeof(buf)); /* are we executing a command? then do it */ if (o.cmdexec) { struct fdinfo info = { 0 }; info.fd = socket_n; if (o.keepopen) netrun(&info, o.cmdexec); else netexec(&info, o.cmdexec); continue; } FD_SET(socket_n, &read_fds); FD_SET(STDIN_FILENO, &read_fds); fdmax = socket_n; /* stdin -> socket and socket -> stdout */ while (1) { fd_set fds; fds = read_fds; if (o.debug > 1) logdebug("udp select'ing\n"); fds_ready = fselect(fdmax + 1, &fds, NULL, NULL, NULL); if (FD_ISSET(STDIN_FILENO, &fds)) { nbytes = Read(STDIN_FILENO, buf, sizeof(buf)); if (nbytes < 0) { loguser("%s.\n", strerror(errno)); return 1; } else if (nbytes == 0) { return 0; } if (o.crlf) fix_line_endings((char *) buf, &nbytes, &tempbuf, &crlf_state); if (!o.recvonly) { if (tempbuf != NULL) send(socket_n, tempbuf, nbytes, 0); else send(socket_n, buf, nbytes, 0); } if (tempbuf != NULL) { free(tempbuf); tempbuf = NULL; } } if (FD_ISSET(socket_n, &fds)) { nbytes = recv(socket_n, buf, sizeof(buf), 0); if (nbytes < 0) { loguser("%s.\n", socket_strerror(socket_errno())); close(socket_n); return 1; } if (!o.sendonly) Write(STDOUT_FILENO, buf, nbytes); } zmem(buf, sizeof(buf)); } } return 0; }