/* Create the actual socket (nse->iod->sd) underlying the iod. This unblocks the * socket, binds to the localaddr address, sets IP options, and sets the * broadcast flag. Trying to change these functions after making this call will * not have an effect. This function needs to be called before you try to read * or write on the iod. */ static int nsock_make_socket(struct npool *ms, struct niod *iod, int family, int type, int proto) { /* inheritable_socket is from nbase */ iod->sd = (int)inheritable_socket(family, type, proto); if (iod->sd == -1) { nsock_log_error("Socket trouble: %s", socket_strerror(socket_errno())); return -1; } unblock_socket(iod->sd); iod->lastproto = proto; if (iod->locallen) mksock_bind_addr(ms, iod); if (iod->ipoptslen && family == AF_INET) mksock_set_ipopts(ms, iod); if (ms->device) mksock_bind_device(ms, iod); if (ms->broadcast && type != SOCK_STREAM) mksock_set_broadcast(ms, iod); /* mksock_* functions can raise warnings/errors * but we don't let them stop us for now. */ return iod->sd; }
int nsock_pool_set_proxychain(nsock_pool nspool, nsock_proxychain chain) { struct npool *nsp = (struct npool *)nspool; if (nsp && nsp->px_chain) { nsock_log_error("Invalid call. Existing proxychain on this nsock_pool"); return -1; } nsp->px_chain = (struct proxy_chain *)chain; return 1; }
static int nsock_pcap_try_open(struct npool *nsp, mspcap *mp, const char *dev, int snaplen, int promisc, int timeout_ms, char *errbuf) { mp->pt = pcap_open_live(dev, snaplen, promisc, timeout_ms, errbuf); if (!mp->pt) { nsock_log_error(nsp, "pcap_open_live(%s, %d, %d, %d) failed with error: %s", dev, snaplen, promisc, timeout_ms, errbuf); return -1; } return 0; }
static int nsock_pcap_set_filter(struct npool *nsp, pcap_t *pt, const char *device, const char *bpf) { struct bpf_program fcode; int rc; rc = pcap_compile(pt, &fcode, (char *)bpf, 1, 0); if (rc) { nsock_log_error(nsp, "Error compiling pcap filter: %s", pcap_geterr(pt)); return rc; } rc = pcap_setfilter(pt, &fcode); if (rc) { nsock_log_error(nsp, "Failed to set the pcap filter: %s", pcap_geterr(pt)); return rc; } pcap_freecode(&fcode); return 0; }
static int mksock_set_ipopts(struct npool *ms, struct niod *iod) { int rc; errno = 0; rc = setsockopt(iod->sd, IPPROTO_IP, IP_OPTIONS, (const char *)iod->ipopts, iod->ipoptslen); if (rc == -1) { int err = socket_errno(); nsock_log_error("Setting of IP options failed (IOD #%li): %s (%d)", iod->id, socket_strerror(err), err); } return 0; }
static int mksock_bind_addr(struct npool *ms, struct niod *iod) { int rc; int one = 1; rc = setsockopt(iod->sd, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(one)); if (rc == -1) { int err = socket_errno(); nsock_log_error("Setting of SO_REUSEADDR failed (#%li): %s (%d)", iod->id, socket_strerror(err), err); } nsock_log_info("Binding to %s (IOD #%li)", get_localaddr_string(iod), iod->id); rc = bind(iod->sd, (struct sockaddr *)&iod->local, (int) iod->locallen); if (rc == -1) { int err = socket_errno(); nsock_log_error("Bind to %s failed (IOD #%li): %s (%d)", get_localaddr_string(iod), iod->id, socket_strerror(err), err); } return 0; }
static int mksock_set_broadcast(struct npool *ms, struct niod *iod) { int rc; int one = 1; rc = setsockopt(iod->sd, SOL_SOCKET, SO_BROADCAST, (const char *)&one, sizeof(one)); if (rc == -1) { int err = socket_errno(); nsock_log_error("Setting of SO_BROADCAST failed (IOD #%li): %s (%d)", iod->id, socket_strerror(err), err); } return 0; }
static int mksock_bind_device(struct npool *ms, struct niod *iod) { int rc; rc = socket_bindtodevice(iod->sd, ms->device); if (!rc) { int err = socket_errno(); if (err != EPERM) nsock_log_error("Setting of SO_BINDTODEVICE failed (IOD #%li): %s (%d)", iod->id, socket_strerror(err), err); else nsock_log_debug_all("Setting of SO_BINDTODEVICE failed (IOD #%li): %s (%d)", iod->id, socket_strerror(err), err); } return 0; }
/* handle_connect_results assumes that select or poll have already shown the * descriptor to be active */ void handle_connect_result(struct npool *ms, struct nevent *nse, enum nse_status status) { int optval; socklen_t optlen = sizeof(int); struct niod *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: /* I'd like for someone to report it */ fatal("Strange connect error from %s (%d): %s", inet_ntop_ez(&iod->peer, iod->peerlen), optval, socket_strerror(optval)); } /* 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 HAVE_SSL_SET_TLSEXT_HOST_NAME if (iod->hostname != NULL) { 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 { fatal("Unknown status (%d)", status); } /* At this point the TCP connection is done, whether successful or not. * Therefore decrease the read/write listen counts that were incremented in * nsock_pool_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) nsock_log_error("Uh-oh: SSL_set_session() failed - please tell [email protected]"); 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 { nsock_log_error("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 nsock_pool_ssl_init_max_speed. Try reconnecting * with SSL_OP_NO_SSLv2. Never downgrade a NO_SSLv2 connection to one * that might use SSLv2. */ nsock_log_info("EID %li reconnecting with SSL_OP_NO_SSLv2", nse->id); saved_ev = iod->watched_events; nsock_engine_iod_unregister(ms, iod); close(iod->sd); nsock_connect_internal(ms, nse, SOCK_STREAM, iod->lastproto, &iod->peer, iod->peerlen, nsock_iod_get_peerport(iod)); nsock_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 { nsock_log_info("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 }
int kqueue_loop(mspool *nsp, int msec_timeout) { int results_left = 0; int event_msecs; /* msecs before an event goes off */ int combined_msecs; struct timespec ts, *ts_p; int sock_err = 0; struct kqueue_engine_info *kinfo = (struct kqueue_engine_info *)nsp->engine_data; assert(msec_timeout >= -1); if (nsp->events_pending == 0) return 0; /* No need to wait on 0 events ... */ if (gh_list_count(&nsp->active_iods) > kinfo->evlen) { kinfo->evlen = gh_list_count(&nsp->active_iods) * 2; kinfo->events = (struct kevent *)safe_realloc(kinfo->events, kinfo->evlen * sizeof(struct kevent)); } do { msevent *nse; nsock_log_debug_all(nsp, "wait for events"); nse = next_expirable_event(nsp); if (!nse) event_msecs = -1; /* None of the events specified a timeout */ else event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod)); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT /* Force a low timeout when capturing packets on systems where * the pcap descriptor is not select()able. */ if (gh_list_count(&nsp->pcap_read_events) > 0) if (event_msecs > PCAP_POLL_INTERVAL) event_msecs = PCAP_POLL_INTERVAL; #endif #endif /* We cast to unsigned because we want -1 to be very high (since it means no * timeout) */ combined_msecs = MIN((unsigned)event_msecs, (unsigned)msec_timeout); /* Set up the timeval pointer we will give to kevent() */ memset(&ts, 0, sizeof(struct timespec)); if (combined_msecs >= 0) { ts.tv_sec = combined_msecs / 1000; ts.tv_nsec = (combined_msecs % 1000) * 1000000L; ts_p = &ts; } else { ts_p = NULL; } #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT /* do non-blocking read on pcap devices that doesn't support select() * If there is anything read, just leave this loop. */ if (pcap_read_on_nonselect(nsp)) { /* okay, something was read. */ } else #endif #endif { results_left = kevent(kinfo->kqfd, NULL, 0, kinfo->events, kinfo->evlen, ts_p); if (results_left == -1) sock_err = socket_errno(); } gettimeofday(&nsock_tod, NULL); /* Due to kevent delay */ } while (results_left == -1 && sock_err == EINTR); /* repeat only if signal occurred */ if (results_left == -1 && sock_err != EINTR) { nsock_log_error(nsp, "nsock_loop error %d: %s", sock_err, socket_strerror(sock_err)); nsp->errnum = sock_err; return -1; } iterate_through_event_lists(nsp, results_left); return 1; }
int select_loop(mspool *nsp, int msec_timeout) { int results_left = 0; int event_msecs; /* msecs before an event goes off */ int combined_msecs; int sock_err = 0; struct timeval select_tv; struct timeval *select_tv_p; struct select_engine_info *sinfo = (struct select_engine_info *)nsp->engine_data; assert(msec_timeout >= -1); if (nsp->events_pending == 0) return 0; /* No need to wait on 0 events ... */ do { nsock_log_debug_all(nsp, "wait for events"); if (nsp->next_ev.tv_sec == 0) event_msecs = -1; /* None of the events specified a timeout */ else event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nsp->next_ev, nsock_tod)); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT /* Force a low timeout when capturing packets on systems where * the pcap descriptor is not select()able. */ if (GH_LIST_COUNT(&nsp->pcap_read_events)) if (event_msecs > PCAP_POLL_INTERVAL) event_msecs = PCAP_POLL_INTERVAL; #endif #endif /* We cast to unsigned because we want -1 to be very high (since it means no * timeout) */ combined_msecs = MIN((unsigned)event_msecs, (unsigned)msec_timeout); /* Set up the timeval pointer we will give to select() */ memset(&select_tv, 0, sizeof(select_tv)); if (combined_msecs > 0) { select_tv.tv_sec = combined_msecs / 1000; select_tv.tv_usec = (combined_msecs % 1000) * 1000; select_tv_p = &select_tv; } else if (combined_msecs == 0) { /* we want the tv_sec and tv_usec to be zero but they already are from bzero */ select_tv_p = &select_tv; } else { assert(combined_msecs == -1); select_tv_p = NULL; } #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT /* do non-blocking read on pcap devices that doesn't support select() * If there is anything read, just leave this loop. */ if (pcap_read_on_nonselect(nsp)) { /* okay, something was read. */ } else #endif #endif { /* Set up the descriptors for select */ sinfo->fds_results_r = sinfo->fds_master_r; sinfo->fds_results_w = sinfo->fds_master_w; sinfo->fds_results_x = sinfo->fds_master_x; results_left = fselect(sinfo->max_sd + 1, &sinfo->fds_results_r, &sinfo->fds_results_w, &sinfo->fds_results_x, select_tv_p); if (results_left == -1) sock_err = socket_errno(); } gettimeofday(&nsock_tod, NULL); /* Due to select delay */ } while (results_left == -1 && sock_err == EINTR); /* repeat only if signal occurred */ if (results_left == -1 && sock_err != EINTR) { nsock_log_error(nsp, "nsock_loop error %d: %s", sock_err, socket_strerror(sock_err)); nsp->errnum = sock_err; return -1; } iterate_through_event_lists(nsp); return 1; }
int poll_loop(struct npool *nsp, int msec_timeout) { int results_left = 0; int event_msecs; /* msecs before an event goes off */ int combined_msecs; int sock_err = 0; struct poll_engine_info *pinfo = (struct poll_engine_info *)nsp->engine_data; assert(msec_timeout >= -1); if (nsp->events_pending == 0) return 0; /* No need to wait on 0 events ... */ do { struct nevent *nse; nsock_log_debug_all(nsp, "wait for events"); nse = next_expirable_event(nsp); if (!nse) event_msecs = -1; /* None of the events specified a timeout */ else event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod)); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT /* Force a low timeout when capturing packets on systems where * the pcap descriptor is not select()able. */ if (gh_list_count(&nsp->pcap_read_events) > 0) if (event_msecs > PCAP_POLL_INTERVAL) event_msecs = PCAP_POLL_INTERVAL; #endif #endif /* We cast to unsigned because we want -1 to be very high (since it means no * timeout) */ combined_msecs = MIN((unsigned)event_msecs, (unsigned)msec_timeout); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT /* do non-blocking read on pcap devices that doesn't support select() * If there is anything read, just leave this loop. */ if (pcap_read_on_nonselect(nsp)) { /* okay, something was read. */ } else #endif #endif { results_left = Poll(pinfo->events, pinfo->max_fd + 1, combined_msecs); if (results_left == -1) sock_err = socket_errno(); } gettimeofday(&nsock_tod, NULL); /* Due to poll delay */ } while (results_left == -1 && sock_err == EINTR); /* repeat only if signal occurred */ if (results_left == -1 && sock_err != EINTR) { nsock_log_error(nsp, "nsock_loop error %d: %s", sock_err, socket_strerror(sock_err)); nsp->errnum = sock_err; return -1; } iterate_through_event_lists(nsp); return 1; }
/* Convert new nsiod to pcap descriptor. Other parameters have * the same meaning as for pcap_open_live in pcap(3). * device : pcap-style device name * snaplen : size of packet to be copied to handler * promisc : whether to open device in promiscuous mode * bpf_fmt : berkeley filter * return value: NULL if everything was okay, or error string * if error occurred. */ int nsock_pcap_open(nsock_pool nsp, nsock_iod nsiod, const char *pcap_device, int snaplen, int promisc, const char *bpf_fmt, ...) { struct niod *nsi = (struct niod *)nsiod; struct npool *ms = (struct npool *)nsp; mspcap *mp = (mspcap *)nsi->pcap; char errbuf[PCAP_ERRBUF_SIZE]; char bpf[4096]; va_list ap; int failed, datalink; int rc; #ifdef PCAP_CAN_DO_SELECT #if PCAP_BSD_SELECT_HACK /* MacOsX reports error if to_ms is too big (like INT_MAX) with error * FAILED. Reported error: BIOCSRTIMEOUT: Invalid argument * INT_MAX/6 (=357913941) seems to be working... */ int to_ms = 357913941; #else int to_ms = 200; #endif /* PCAP_BSD_SELECT_HACK */ #else int to_ms = 1; #endif gettimeofday(&nsock_tod, NULL); if (mp) { nsock_log_error(ms, "This nsi already has pcap device opened"); return -1; } mp = (mspcap *)safe_zalloc(sizeof(mspcap)); nsi->pcap = (void *)mp; va_start(ap, bpf_fmt); rc = Vsnprintf(bpf, sizeof(bpf), bpf_fmt, ap); va_end(ap); if (rc >= (int)sizeof(bpf)) { nsock_log_error(ms, "Too-large bpf filter argument"); return -1; } nsock_log_info(ms, "PCAP requested on device '%s' with berkeley filter '%s' " "(promisc=%i snaplen=%i to_ms=%i) (IOD #%li)", pcap_device,bpf, promisc, snaplen, to_ms, nsi->id); failed = 0; do { rc = nsock_pcap_try_open(ms, mp, pcap_device, snaplen, promisc, to_ms, errbuf); if (rc) { failed++; nsock_log_error(ms, "Will wait %d seconds then retry.", 4 * failed); sleep(4 * failed); } } while (rc && failed < PCAP_OPEN_MAX_RETRIES); if (rc) { nsock_log_error(ms, "pcap_open_live(%s, %d, %d, %d) failed %d times.", pcap_device, snaplen, promisc, to_ms, failed); nsock_log_error(ms, PCAP_FAILURE_EXPL_MESSAGE); nsock_log_error(ms, "Can't open pcap! Are you root?"); return -1; } rc = nsock_pcap_set_filter(ms, mp->pt, pcap_device, bpf); if (rc) return rc; #ifdef WIN32 /* We want any responses back ASAP */ pcap_setmintocopy(mp->pt, 1); #endif mp->l3_offset = nsock_pcap_get_l3_offset(mp->pt, &datalink); mp->snaplen = snaplen; mp->datalink = datalink; mp->pcap_device = strdup(pcap_device); #ifdef PCAP_CAN_DO_SELECT mp->pcap_desc = pcap_get_selectable_fd(mp->pt); #else mp->pcap_desc = -1; #endif mp->readsd_count = 0; /* Without setting this ioctl, some systems (BSDs, though it depends on the * release) will buffer packets in non-blocking mode and only return them in a * bunch when the buffer is full. Setting the ioctl makes each one be * delivered immediately. This is how Linux works by default. See the comments * surrounding the setting of BIOCIMMEDIATE in libpcap/pcap-bpf.c. */ #ifdef BIOCIMMEDIATE if (mp->pcap_desc != -1) { int immediate = 1; if (ioctl(mp->pcap_desc, BIOCIMMEDIATE, &immediate) < 0) fatal("Cannot set BIOCIMMEDIATE on pcap descriptor"); } #endif /* Set device non-blocking */ rc = pcap_setnonblock(mp->pt, 1, errbuf); if (rc) { /* I can't do select() on pcap! * blocking + no_select is fatal */ #ifndef PCAP_BSD_SELECT_HACK if (mp->pcap_desc < 0) #endif { nsock_log_error(ms, "Failed to set pcap descriptor on device %s " "to nonblocking mode: %s", pcap_device, errbuf); return -1; } /* in other case, we can accept blocking pcap */ nsock_log_info(ms, "Failed to set pcap descriptor on device %s " "to nonblocking state: %s", pcap_device, errbuf); } if (ms->loglevel <= NSOCK_LOG_INFO) { #if PCAP_BSD_SELECT_HACK int bsd_select_hack = 1; #else int bsd_select_hack = 0; #endif #if PCAP_RECV_TIMEVAL_VALID int recv_timeval_valid = 1; #else int recv_timeval_valid = 0; #endif nsock_log_info(ms, "PCAP created successfully on device '%s' " "(pcap_desc=%i bsd_hack=%i to_valid=%i l3_offset=%i) (IOD #%li)", pcap_device, mp->pcap_desc, bsd_select_hack, recv_timeval_valid, mp->l3_offset, nsi->id); } return 0; }