/* Here is the all important looping function that tells the event engine to * start up and begin processing events. It will continue until all events have * been delivered (including new ones started from event handlers), or the * msec_timeout is reached, or a major error has occurred. Use -1 if you don't * want to set a maximum time for it to run. A timeout of 0 will return after 1 * non-blocking loop. The nsock loop can be restarted again after it returns. * For example you could do a series of 15 second runs, allowing you to do other * stuff between them */ enum nsock_loopstatus nsock_loop(nsock_pool nsp, int msec_timeout) { struct npool *ms = (struct npool *)nsp; struct timeval loop_timeout; int msecs_left; unsigned long loopnum = 0; enum nsock_loopstatus quitstatus = NSOCK_LOOP_ERROR; gettimeofday(&nsock_tod, NULL); if (msec_timeout < -1) { ms->errnum = EINVAL; return NSOCK_LOOP_ERROR; } TIMEVAL_MSEC_ADD(loop_timeout, nsock_tod, msec_timeout); msecs_left = msec_timeout; if (msec_timeout >= 0) nsock_log_debug("nsock_loop() started (timeout=%dms). %d events pending", msec_timeout, ms->events_pending); else nsock_log_debug("nsock_loop() started (no timeout). %d events pending", ms->events_pending); while (1) { if (ms->quit) { /* We've been asked to quit the loop through nsock_loop_quit. */ ms->quit = 0; quitstatus = NSOCK_LOOP_QUIT; break; } if (ms->events_pending == 0) { /* if no events at all are pending, then none can be created until * we quit nsock_loop() -- so we do that now. */ quitstatus = NSOCK_LOOP_NOEVENTS; break; } if (msec_timeout >= 0) { msecs_left = MAX(0, TIMEVAL_MSEC_SUBTRACT(loop_timeout, nsock_tod)); if (msecs_left == 0 && loopnum > 0) { quitstatus = NSOCK_LOOP_TIMEOUT; break; } } if (nsock_engine_loop(ms, msecs_left) == -1) { quitstatus = NSOCK_LOOP_ERROR; break; } gettimeofday(&nsock_tod, NULL); /* we do this at end because there is one * at beginning of function */ loopnum++; } return quitstatus; }
/* Create a new event structure -- must be deleted later with msevent_delete, * unless it returns NULL (failure). NULL can be passed in for the msiod and * the userdata if not available */ msevent *msevent_new(mspool *nsp, enum nse_type type, msiod *msiod, int timeout_msecs, nsock_ev_handler handler, void *userdata) { msevent *nse; /* Bring us up to date for the timeout calculation. */ gettimeofday(&nsock_tod, NULL); if (msiod) { msiod->events_pending++; assert(msiod->state != NSIOD_STATE_DELETED); } /* First we check if one is available from the free list ... */ nse = (msevent *)gh_list_pop(&nsp->free_events); if (!nse) nse = (msevent *)safe_malloc(sizeof(msevent)); memset(nse, 0, sizeof(msevent)); nse->id = get_new_event_id(nsp, type); nse->type = type; nse->status = NSE_STATUS_NONE; #if HAVE_OPENSSL nse->sslinfo.ssl_desire = SSL_ERROR_NONE; #endif if (type == NSE_TYPE_READ || type == NSE_TYPE_WRITE) filespace_init(&(nse->iobuf), 1024); #if HAVE_PCAP if (type == NSE_TYPE_PCAP_READ) { mspcap *mp; int sz; assert(msiod != NULL); mp = (mspcap *)msiod->pcap; assert(mp); sz = mp->snaplen+1 + sizeof(nsock_pcap); filespace_init(&(nse->iobuf), sz); } #endif if (timeout_msecs != -1) { assert(timeout_msecs >= 0); TIMEVAL_MSEC_ADD(nse->timeout, nsock_tod, timeout_msecs); } nse->iod = msiod; nse->handler = handler; nse->userdata = userdata; if (nse->iod == NULL) nsock_log_debug(nsp, "msevent_new (IOD #NULL) (EID #%li)", nse->id); else nsock_log_debug(nsp, "msevent_new (IOD #%li) (EID #%li)", nse->iod->id, nse->id); return nse; }
static int handle_state_tcp_connected(struct npool *nsp, struct nevent *nse, void *udata) { struct proxy_chain_context *px_ctx = nse->iod->px_ctx; char *res; int reslen; res = nse_readbuf(nse, &reslen); /* TODO string check!! */ if (!((reslen >= 15) && strstr(res, "200 OK"))) { struct proxy_node *node = px_ctx->px_current; nsock_log_debug("Connection refused from proxy %s", node->nodestr); return -EINVAL; } px_ctx->px_state = PROXY_STATE_HTTP_TUNNEL_ESTABLISHED; if (proxy_ctx_node_next(px_ctx) == NULL) { forward_event(nsp, nse, udata); } else { px_ctx->px_current = proxy_ctx_node_next(px_ctx); px_ctx->px_state = PROXY_STATE_INITIAL; nsock_proxy_ev_dispatch(nsp, nse, udata); } return 0; }
/* 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->loglevel == NSOCK_LOG_DBG_ALL && datalen < 80) { memcpy(displaystr, ": ", 2); memcpy(displaystr + 2, data, datalen); displaystr[2 + datalen] = '\0'; replacenonprintable(displaystr + 2, datalen, '.'); } else { displaystr[0] = '\0'; } nsock_log_debug(nsp, "Write request for %d bytes to IOD #%li EID %li [%s]%s", datalen, nsi->id, nse->id, get_peeraddr_string(nsi), displaystr); fs_cat(&nse->iobuf, data, datalen); nsp_add_event(nsp, nse); return nse->id; }
void nsock_set_log_function(nsock_logger_t logger) { if (logger != NULL) NsockLogger = logger; else NsockLogger = nsock_stderr_logger; nsock_log_debug("Registered external logging function: %p", NsockLogger); }
nsock_event_id nsock_sendto(nsock_pool ms_pool, nsock_iod ms_iod, nsock_ev_handler handler, int timeout_msecs, void *userdata, struct sockaddr *saddr, size_t sslen, unsigned short port, const char *data, int datalen) { mspool *nsp = (mspool *)ms_pool; msiod *nsi = (msiod *)ms_iod; msevent *nse; char displaystr[256]; struct sockaddr_in *sin = (struct sockaddr_in *)saddr; #if HAVE_IPV6 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr; #endif nse = msevent_new(nsp, NSE_TYPE_WRITE, nsi, timeout_msecs, handler, userdata); assert(nse); if (saddr->sa_family == AF_INET) { sin->sin_port = htons(port); #if HAVE_SYS_UN_H } else if (saddr->sa_family == AF_INET6) { #else } else { #endif assert(saddr->sa_family == AF_INET6); #if HAVE_IPV6 sin6->sin6_port = htons(port); #else fatal("IPv6 address passed to %s call, but nsock was not compiled w/IPv6 support", __func__); #endif } assert(sslen <= sizeof(nse->writeinfo.dest)); memcpy(&nse->writeinfo.dest, saddr, sslen); nse->writeinfo.destlen = sslen; assert(sslen <= sizeof(nse->iod->peer)); memcpy(&nse->iod->peer, saddr, sslen); nse->iod->peerlen = sslen; if (datalen < 0) datalen = (int) strlen(data); if (nsp->loglevel == NSOCK_LOG_DBG_ALL && datalen < 80) { memcpy(displaystr, ": ", 2); memcpy(displaystr + 2, data, datalen); displaystr[2 + datalen] = '\0'; replacenonprintable(displaystr + 2, datalen, '.'); } else { displaystr[0] = '\0'; } nsock_log_debug(nsp, "Sendto request for %d bytes to IOD #%li EID %li [%s]%s", datalen, nsi->id, nse->id, get_peeraddr_string(nse->iod), displaystr); fs_cat(&nse->iobuf, data, datalen); nsp_add_event(nsp, nse); return nse->id; }
/* Free an msevent which was allocated with msevent_new, including all internal * resources. Note -- we assume that nse->iod->events_pending (if it exists) * has ALREADY been decremented (done during msevent_dispatch_and_delete) -- so * remember to do this if you call msevent_delete() directly */ void msevent_delete(mspool *nsp, msevent *nse) { if (nse->iod == NULL) nsock_log_debug(nsp, "msevent_delete (IOD #NULL) (EID #%li)", nse->id); else nsock_log_debug(nsp, "msevent_delete (IOD #%li) (EID #%li)", nse->iod->id, nse->id); /* First free the IOBuf inside it if necessary */ if (nse->type == NSE_TYPE_READ || nse->type == NSE_TYPE_WRITE) { fs_free(&nse->iobuf); } #if HAVE_PCAP if (nse->type == NSE_TYPE_PCAP_READ) { fs_free(&nse->iobuf); nsock_log_debug_all(nsp, "PCAP removed %lu", nse->id); } #endif /* Now we add the event back into the free pool */ nse->event_done = 1; }
/* Adds an event to the appropriate nsp event list, handles housekeeping such as * adjusting the descriptor select/poll lists, registering the timeout value, * etc. */ void nsock_pool_add_event(struct npool *nsp, struct nevent *nse) { nsock_log_debug("NSE #%lu: Adding event (timeout in %ldms)", nse->id, (long)TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod)); nsp->events_pending++; if (!nse->event_done && nse->timeout.tv_sec) { /* This event is expirable, add it to the queue */ gh_heap_push(&nsp->expirables, &nse->expire); } /* Now we do the event type specific actions */ switch (nse->type) { case NSE_TYPE_CONNECT: case NSE_TYPE_CONNECT_SSL: if (!nse->event_done) { assert(nse->iod->sd >= 0); socket_count_read_inc(nse->iod); socket_count_write_inc(nse->iod); update_events(nse->iod, nsp, EV_READ|EV_WRITE|EV_EXCEPT, EV_NONE); } iod_add_event(nse->iod, nse); break; case NSE_TYPE_READ: if (!nse->event_done) { assert(nse->iod->sd >= 0); socket_count_read_inc(nse->iod); update_events(nse->iod, nsp, EV_READ, EV_NONE); #if HAVE_OPENSSL if (nse->iod->ssl) nse->sslinfo.ssl_desire = SSL_ERROR_WANT_READ; #endif } iod_add_event(nse->iod, nse); break; case NSE_TYPE_WRITE: if (!nse->event_done) { assert(nse->iod->sd >= 0); socket_count_write_inc(nse->iod); update_events(nse->iod, nsp, EV_WRITE, EV_NONE); #if HAVE_OPENSSL if (nse->iod->ssl) nse->sslinfo.ssl_desire = SSL_ERROR_WANT_WRITE; #endif } iod_add_event(nse->iod, nse); break; case NSE_TYPE_TIMER: /* nothing to do */ break; #if HAVE_PCAP case NSE_TYPE_PCAP_READ: { mspcap *mp = (mspcap *)nse->iod->pcap; assert(mp); if (mp->pcap_desc >= 0) { /* pcap descriptor present */ if (!nse->event_done) { socket_count_readpcap_inc(nse->iod); update_events(nse->iod, nsp, EV_READ, EV_NONE); } nsock_log_debug_all("PCAP NSE #%lu: Adding event to READ_EVENTS", nse->id); #if PCAP_BSD_SELECT_HACK /* when using BSD hack we must do pcap_next() after select(). * Let's insert this pcap to bot queues, to selectable and nonselectable. * This will result in doing pcap_next_ex() just before select() */ nsock_log_debug_all("PCAP NSE #%lu: Adding event to PCAP_READ_EVENTS", nse->id); #endif } else { /* pcap isn't selectable. Add it to pcap-specific queue. */ nsock_log_debug_all("PCAP NSE #%lu: Adding event to PCAP_READ_EVENTS", nse->id); } iod_add_event(nse->iod, nse); break; } #endif default: fatal("Unknown nsock event type (%d)", nse->type); } /* It can happen that the event already completed. In which case we can * already deliver it, even though we're probably not inside nsock_loop(). */ if (nse->event_done) { event_dispatch_and_delete(nsp, nse, 1); update_first_events(nse); nevent_unref(nsp, nse); } }
/* 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 { fs_cat(&nse->iobuf, buf2, strlength); } } if (nsp->loglevel == NSOCK_LOG_DBG_ALL && 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'; } nsock_log_debug(nsp, "Write request for %d bytes to IOD #%li EID %li [%s]%s", strlength, nsi->id, nse->id, get_peeraddr_string(nsi), displaystr); if (buf2 != buf) free(buf2); nsp_add_event(nsp, nse); return nse->id; }
void nsock_set_log_function(nsock_pool nsp, nsock_logger_t logger) { mspool *ms = (mspool *)nsp; ms->logger = logger; nsock_log_debug(ms, "Registered external logging function: %p", logger); }
void nsock_set_loglevel(nsock_loglevel_t loglevel) { NsockLogLevel = loglevel; nsock_log_debug("Set log level to %s", nsock_loglevel2str(loglevel)); }