/* 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 } }
/* Same as above, except it tries to read at least 'nbytes' instead of 'nlines'. */ nsock_event_id nsock_readbytes(nsock_pool nsp, nsock_iod ms_iod, nsock_ev_handler handler, int timeout_msecs, void *userdata, int nbytes) { msiod *nsi = (msiod *)ms_iod; mspool *ms = (mspool *)nsp; msevent *nse; nse = msevent_new(ms, NSE_TYPE_READ, nsi, timeout_msecs, handler, userdata); assert(nse); if (ms->tracelevel > 0) { if (nsi->peerlen > 0) nsock_trace(ms, "Read request for %d bytes from IOD #%li [%s:%d] EID %li", nbytes, nsi->id, inet_ntop_ez(&nsi->peer, nsi->peerlen), nsi_peerport(nsi), nse->id); else nsock_trace(ms, "Read request for %d bytes from IOD #%li (peer unspecified) EID %li", nbytes, nsi->id, nse->id); } nse->readinfo.read_type = NSOCK_READBYTES; nse->readinfo.num = nbytes; nsp_add_event(ms, nse); return nse->id; }
/* Get the peer/host address string. * In case we have support for UNIX domain sockets, function returns * string containing path to UNIX socket if the address family is AF_UNIX, * otherwise it returns string containing "<address>:<port>". */ char *get_peeraddr_string(const msiod *iod) { static char buffer[PEER_STR_LEN]; #if HAVE_SYS_UN_H if (iod->peer.ss_family == AF_UNIX) { sprintf(buffer, "%s", get_unixsock_path(&iod->peer)); return buffer; } #endif sprintf(buffer, "%s:%d", inet_ntop_ez(&iod->peer, iod->peerlen), nsi_peerport((msiod *) iod)); return buffer; }
/* Write some data to the socket. If the write is not COMPLETED within timeout_msecs , NSE_STATUS_TIMEOUT will be returned. If you are supplying NUL-terminated data, you can optionally pass -1 for datalen and nsock_write will figure out the length itself */ nsock_event_id nsock_write(nsock_pool ms_pool, nsock_iod ms_iod, nsock_ev_handler handler, int timeout_msecs, void *userdata, const char *data, int datalen) { mspool *nsp = (mspool *) ms_pool; msiod *nsi = (msiod *) ms_iod; msevent *nse; char displaystr[256]; nse = msevent_new(nsp, NSE_TYPE_WRITE, nsi, timeout_msecs, handler, userdata); assert(nse); nse->writeinfo.dest.ss_family = AF_UNSPEC; if (datalen < 0) datalen = (int) strlen(data); if (nsp->tracelevel > 0) { if (nsp->tracelevel > 1 && datalen < 80) { memcpy(displaystr, ": ", 2); memcpy(displaystr + 2, data, datalen); displaystr[2 + datalen] = '\0'; replacenonprintable(displaystr + 2, datalen, '.'); } else displaystr[0] = '\0'; if (nsi->peerlen > 0) nsock_trace(nsp, "Write request for %d bytes to IOD #%li EID %li [%s:%hu]%s", datalen, nsi->id, nse->id, inet_ntop_ez(&nsi->peer, nsi->peerlen), nsi_peerport(nsi), displaystr); else nsock_trace(nsp, "Write request for %d bytes to IOD #%li EID %li (peer unspecified)%s", datalen, nsi->id, nse->id, displaystr); } fscat(&nse->iobuf, data, datalen); nsp_add_event(nsp, nse); return nse->id; }
/* handle_connect_results assumes that select or poll have already shown the * descriptor to be active */ void handle_connect_result(mspool *ms, msevent *nse, enum nse_status status) { int optval; socklen_t optlen = sizeof(int); msiod *iod = nse->iod; #if HAVE_OPENSSL int sslerr; int rc = 0; int sslconnect_inprogress = nse->type == NSE_TYPE_CONNECT_SSL && nse->iod && (nse->sslinfo.ssl_desire == SSL_ERROR_WANT_READ || nse->sslinfo.ssl_desire == SSL_ERROR_WANT_WRITE); #else int sslconnect_inprogress = 0; #endif if (status == NSE_STATUS_TIMEOUT || status == NSE_STATUS_CANCELLED) { nse->status = status; nse->event_done = 1; } else if (sslconnect_inprogress) { /* Do nothing */ } else if (status == NSE_STATUS_SUCCESS) { /* First we want to determine whether the socket really is connected */ if (getsockopt(iod->sd, SOL_SOCKET, SO_ERROR, (char *)&optval, &optlen) != 0) optval = socket_errno(); /* Stupid Solaris */ switch(optval) { case 0: nse->status = NSE_STATUS_SUCCESS; break; /* EACCES can be caused by ICMPv6 dest-unreach-admin, or when a port is blocked by Windows Firewall (WSAEACCES). */ case EACCES: case ECONNREFUSED: case EHOSTUNREACH: case ENETDOWN: case ENETUNREACH: case ENETRESET: case ECONNABORTED: case ETIMEDOUT: case EHOSTDOWN: case ECONNRESET: #ifdef WIN32 case WSAEADDRINUSE: case WSAEADDRNOTAVAIL: #endif #ifndef WIN32 case EPIPE: /* Has been seen after connect on Linux. */ case ENOPROTOOPT: /* Also seen on Linux, perhaps in response to protocol unreachable. */ #endif nse->status = NSE_STATUS_ERROR; nse->errnum = optval; break; default: fprintf(stderr, "Strange connect error from %s (%d): %s", inet_ntop_ez(&iod->peer, iod->peerlen), optval, socket_strerror(optval)); assert(0); /* I'd like for someone to report it */ break; } /* Now special code for the SSL case where the TCP connection was successful. */ if (nse->type == NSE_TYPE_CONNECT_SSL && nse->status == NSE_STATUS_SUCCESS) { #if HAVE_OPENSSL assert(ms->sslctx != NULL); /* Reuse iod->ssl if present. If set, this is the second try at connection without the SSL_OP_NO_SSLv2 option set. */ if (iod->ssl == NULL) { iod->ssl = SSL_new(ms->sslctx); if (!iod->ssl) fatal("SSL_new failed: %s", ERR_error_string(ERR_get_error(), NULL)); } if (iod->hostname != NULL) { #if HAVE_SSL_SET_TLSEXT_HOST_NAME if (SSL_set_tlsext_host_name(iod->ssl, iod->hostname) != 1) fatal("SSL_set_tlsext_host_name failed: %s", ERR_error_string(ERR_get_error(), NULL)); #endif } /* Associate our new SSL with the connected socket. It will inherit the * non-blocking nature of the sd */ if (SSL_set_fd(iod->ssl, iod->sd) != 1) { fatal("SSL_set_fd failed: %s", ERR_error_string(ERR_get_error(), NULL)); } /* Event not done -- need to do SSL connect below */ nse->sslinfo.ssl_desire = SSL_ERROR_WANT_CONNECT; #endif } else { /* This is not an SSL connect (in which case we are always done), or the * TCP connect() underlying the SSL failed (in which case we are also done */ nse->event_done = 1; } } else { assert(0); /* Currently we only know about TIMEOUT and SUCCESS callbacks */ } /* At this point the TCP connection is done, whether successful or not. * Therefore decrease the read/write listen counts that were incremented in * nsp_add_event. In the SSL case, we may increase one of the counts depending * on whether SSL_connect returns an error of SSL_ERROR_WANT_READ or * SSL_ERROR_WANT_WRITE. In that case we will re-enter this function, but we * don't want to execute this block again. */ if (iod->sd != -1 && !sslconnect_inprogress) { int ev = EV_NONE; ev |= socket_count_read_dec(iod); ev |= socket_count_write_dec(iod); ev |= EV_EXCEPT; update_events(iod, ms, EV_NONE, ev); } #if HAVE_OPENSSL if (nse->type == NSE_TYPE_CONNECT_SSL && !nse->event_done) { /* Lets now start/continue/finish the connect! */ if (iod->ssl_session) { rc = SSL_set_session(iod->ssl, iod->ssl_session); if (rc == 0) fprintf(stderr, "Uh-oh: SSL_set_session() failed - please tell Fyodor\n"); iod->ssl_session = NULL; /* No need for this any more */ } /* If this is a reinvocation of handle_connect_result, clear out the listen * bits that caused it, based on the previous SSL desire. */ if (sslconnect_inprogress) { int ev; ev = socket_count_dec_ssl_desire(nse); update_events(iod, ms, EV_NONE, ev); } rc = SSL_connect(iod->ssl); if (rc == 1) { /* Woop! Connect is done! */ nse->event_done = 1; /* Check that certificate verification was okay, if requested. */ if (nsi_ssl_post_connect_verify(iod)) { nse->status = NSE_STATUS_SUCCESS; } else { if (ms->tracelevel > 0) nsock_trace(ms, "certificate verification error for EID %li: %s", nse->id, ERR_error_string(ERR_get_error(), NULL)); nse->status = NSE_STATUS_ERROR; } } else { long options = SSL_get_options(iod->ssl); sslerr = SSL_get_error(iod->ssl, rc); if (rc == -1 && sslerr == SSL_ERROR_WANT_READ) { nse->sslinfo.ssl_desire = sslerr; socket_count_read_inc(iod); update_events(iod, ms, EV_READ, EV_NONE); } else if (rc == -1 && sslerr == SSL_ERROR_WANT_WRITE) { nse->sslinfo.ssl_desire = sslerr; socket_count_write_inc(iod); update_events(iod, ms, EV_WRITE, EV_NONE); } else if (!(options & SSL_OP_NO_SSLv2)) { int saved_ev; /* SSLv3-only and TLSv1-only servers can't be connected to when the * SSL_OP_NO_SSLv2 option is not set, which is the case when the pool * was initialized with nsp_ssl_init_max_speed. Try reconnecting with * SSL_OP_NO_SSLv2. Never downgrade a NO_SSLv2 connection to one that * might use SSLv2. */ if (ms->tracelevel > 0) nsock_trace(ms, "EID %li reconnecting with SSL_OP_NO_SSLv2", nse->id); saved_ev = iod->watched_events; ms->engine->iod_unregister(ms, iod); close(iod->sd); nsock_connect_internal(ms, nse, SOCK_STREAM, iod->lastproto, &iod->peer, iod->peerlen, nsi_peerport(iod)); ms->engine->iod_register(ms, iod, saved_ev); SSL_clear(iod->ssl); if(!SSL_clear(iod->ssl)) fatal("SSL_clear failed: %s", ERR_error_string(ERR_get_error(), NULL)); SSL_set_options(iod->ssl, options | SSL_OP_NO_SSLv2); socket_count_read_inc(nse->iod); socket_count_write_inc(nse->iod); update_events(iod, ms, EV_READ|EV_WRITE, EV_NONE); nse->sslinfo.ssl_desire = SSL_ERROR_WANT_CONNECT; } else { if (ms->tracelevel > 0) nsock_trace(ms, "EID %li %s", nse->id, ERR_error_string(ERR_get_error(), NULL)); nse->event_done = 1; nse->status = NSE_STATUS_ERROR; nse->errnum = EIO; } } } #endif return; }
/* An event has been completed and the handler is about to be called. This * function writes out tracing data about the event if necessary */ void nsock_trace_handler_callback(mspool *ms, msevent *nse) { msiod *nsi; char *str; int strlength = 0; char displaystr[256]; char errstr[256]; if (ms->tracelevel == 0) return; nsi = nse->iod; if (nse->status == NSE_STATUS_ERROR) Snprintf(errstr, sizeof(errstr), "[%s (%d)] ", strerror(nse->errnum), nse->errnum); else errstr[0] = '\0'; /* Some types have special tracing treatment */ switch(nse->type) { case NSE_TYPE_CONNECT: case NSE_TYPE_CONNECT_SSL: nsock_trace(ms, "Callback: %s %s %sfor EID %li [%s:%d]", nse_type2str(nse->type), nse_status2str(nse->status), errstr, nse->id, inet_ntop_ez(&nsi->peer, nsi->peerlen), nsi_peerport(nsi)); break; case NSE_TYPE_READ: if (nse->status != NSE_STATUS_SUCCESS) { if (nsi->peerlen > 0) { nsock_trace(ms, "Callback: %s %s %sfor EID %li [%s:%d]", nse_type2str(nse->type), nse_status2str(nse->status), errstr, nse->id, inet_ntop_ez(&nsi->peer, nsi->peerlen), nsi_peerport(nsi)); } else { nsock_trace(ms, "Callback: %s %s %sfor EID %li (peer unspecified)", nse_type2str(nse->type), nse_status2str(nse->status), errstr, nse->id); } } else { str = nse_readbuf(nse, &strlength); if (ms->tracelevel > 1 && strlength < 80) { memcpy(displaystr, ": ", 2); memcpy(displaystr + 2, str, strlength); displaystr[2 + strlength] = '\0'; replacenonprintable(displaystr + 2, strlength, '.'); } else { displaystr[0] = '\0'; } if (nsi->peerlen > 0) { nsock_trace(ms, "Callback: %s %s for EID %li [%s:%d] %s(%d bytes)%s", nse_type2str(nse->type), nse_status2str(nse->status), nse->id, inet_ntop_ez(&nsi->peer, nsi->peerlen), nsi_peerport(nsi), nse_eof(nse)? "[EOF]" : "", strlength, displaystr); } else { nsock_trace(ms, "Callback %s %s for EID %li (peer unspecified) %s(%d bytes)%s", nse_type2str(nse->type), nse_status2str(nse->status), nse->id, nse_eof(nse)? "[EOF]" : "", strlength, displaystr); } } break; case NSE_TYPE_WRITE: nsock_trace(ms, "Callback: %s %s %sfor EID %li [%s:%d]", nse_type2str(nse->type), nse_status2str(nse->status), errstr, nse->id, inet_ntop_ez(&nsi->peer, nsi->peerlen), nsi_peerport(nsi)); break; case NSE_TYPE_TIMER: nsock_trace(ms, "Callback: %s %s %sfor EID %li", nse_type2str(nse->type), nse_status2str(nse->status), errstr, nse->id); break; #if HAVE_PCAP case NSE_TYPE_PCAP_READ: nsock_trace(ms, "Callback: %s %s %sfor EID %li ", nse_type2str(nse->type), nse_status2str(nse->status), errstr, nse->id); break; #endif default: assert(0); break; } }
/* Same as nsock_write except you can use a printf-style format and you can only use this for ASCII strings */ nsock_event_id nsock_printf(nsock_pool ms_pool, nsock_iod ms_iod, nsock_ev_handler handler, int timeout_msecs, void *userdata, char *format, ... ) { mspool *nsp = (mspool *) ms_pool; msiod *nsi = (msiod *) ms_iod; msevent *nse; char buf[4096]; char *buf2 = NULL; int res, res2; int strlength = 0; char displaystr[256]; va_list ap; va_start(ap,format); nse = msevent_new(nsp, NSE_TYPE_WRITE, nsi, timeout_msecs, handler, userdata); assert(nse); res = Vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); if (res != -1) { if (res > sizeof(buf)) { buf2 = (char * ) safe_malloc(res + 16); res2 = Vsnprintf(buf2, sizeof(buf), format, ap); if (res2 == -1 || res2 > res) { free(buf2); buf2 = NULL; } else strlength = res2; } else { buf2 = buf; strlength = res; } } if (!buf2) { nse->event_done = 1; nse->status = NSE_STATUS_ERROR; nse->errnum = EMSGSIZE; } else { if (strlength == 0) { nse->event_done = 1; nse->status = NSE_STATUS_SUCCESS; } else { fscat(&nse->iobuf, buf2, strlength); } } if (nsp->tracelevel > 0) { if (nsp->tracelevel > 1 && nse->status != NSE_STATUS_ERROR && strlength < 80) { memcpy(displaystr, ": ", 2); memcpy(displaystr + 2, buf2, strlength); displaystr[2 + strlength] = '\0'; replacenonprintable(displaystr + 2, strlength, '.'); } else displaystr[0] = '\0'; if (nsi->peerlen > 0) nsock_trace(nsp, "Write request for %d bytes to IOD #%li EID %li [%s:%hu]%s", strlength, nsi->id, nse->id, inet_ntop_ez(&nsi->peer, nsi->peerlen), nsi_peerport(nsi), displaystr); else nsock_trace(nsp, "Write request for %d bytes to IOD #%li EID %li (peer unspecified)%s", strlength, nsi->id, nse->id, displaystr); } if (buf2 != buf) { free(buf2); } nsp_add_event(nsp, nse); return nse->id; }