static void first_ev_next(struct nevent *nse, gh_lnode_t **first, int nodeq2) { if (!first || !*first) return; if (&nse->nodeq_io == *first || &nse->nodeq_pcap == *first) { gh_lnode_t *next; next = gh_lnode_next(*first); if (next) { struct nevent *newevent; if (nodeq2) newevent = lnode_nevent2(next); else newevent = lnode_nevent(next); if (newevent->iod == nse->iod) *first = next; else *first = NULL; } else { *first = NULL; } } }
void process_iod_events(struct npool *nsp, struct niod *nsi, int ev) { int i = 0; /* store addresses of the pointers to the first elements of each kind instead * of storing the values, as a connect can add a read for instance */ gh_lnode_t **start_elems[] = { &nsi->first_connect, &nsi->first_read, &nsi->first_write, #if HAVE_PCAP &nsi->first_pcap_read, #endif NULL }; gh_list_t *evlists[] = { &nsp->connect_events, &nsp->read_events, &nsp->write_events, #if HAVE_PCAP &nsp->pcap_read_events, #endif NULL }; assert(nsp == nsi->nsp); nsock_log_debug_all("Processing events on IOD %lu (ev=%d)", nsi->id, ev); /* We keep the events separate because we want to handle them in the * order: connect => read => write => timer for several reasons: * * 1) Makes sure we have gone through all the net i/o events before * a timer expires (would be a shame to timeout after the data was * available but before we delivered the events * * 2) The connect() results often lead to a read or write that can be * processed in the same cycle. In the same way, read() often * leads to write(). */ for (i = 0; evlists[i] != NULL; i++) { gh_lnode_t *current, *next, *last; /* for each list, get the last event and don't look past it as an event * could add another event in the same list and so on... */ last = gh_list_last_elem(evlists[i]); for (current = *start_elems[i]; current != NULL && gh_lnode_prev(current) != last; current = next) { struct nevent *nse; #if HAVE_PCAP if (evlists[i] == &nsi->nsp->pcap_read_events) nse = lnode_nevent2(current); else #endif nse = lnode_nevent(current); /* events are grouped by IOD. Break if we're done with the events for the * current IOD */ if (nse->iod != nsi) break; process_event(nsp, evlists[i], nse, ev); next = gh_lnode_next(current); if (nse->event_done) { /* event is done, remove it from the event list and update IOD pointers * to the first events of each kind */ update_first_events(nse); gh_list_remove(evlists[i], current); gh_list_append(&nsp->free_events, &nse->nodeq_io); if (nse->timeout.tv_sec) gh_heap_remove(&nsp->expirables, &nse->expire); } } } }
/* If nsock_iod_new returned success, you must free the iod when you are done with * it to conserve memory (and in some cases, sockets). After this call, * nsockiod may no longer be used -- you need to create a new one with * nsock_iod_new(). pending_response tells what to do with any events that are * pending on this nsock_iod. This can be NSOCK_PENDING_NOTIFY (send a KILL * notification to each event), NSOCK_PENDING_SILENT (do not send notification * to the killed events), or NSOCK_PENDING_ERROR (print an error message and * quit the program) */ void nsock_iod_delete(nsock_iod nsockiod, enum nsock_del_mode pending_response) { struct niod *nsi = (struct niod *)nsockiod; gh_lnode_t *evlist_ar[3]; gh_list_t *corresp_list[3]; int i; gh_lnode_t *current, *next; assert(nsi); if (nsi->state == NSIOD_STATE_DELETED) { /* This nsi is already marked as deleted, will probably be removed from the * list very soon. Just return to avoid breaking reentrancy. */ return; } nsock_log_info("nsock_iod_delete (IOD #%lu)", nsi->id); if (nsi->events_pending > 0) { /* shit -- they killed the struct niod while an event was still pending on it. * Maybe I should store the pending events in the iod. On the other hand, * this should be a pretty rare occurrence and so I'll save space and hassle * by just locating the events here by searching through the active events * list */ if (pending_response == NSOCK_PENDING_ERROR) fatal("nsock_iod_delete called with argument NSOCK_PENDING_ERROR on a nsock_iod that has %d pending event(s) associated with it", nsi->events_pending); assert(pending_response == NSOCK_PENDING_NOTIFY || pending_response == NSOCK_PENDING_SILENT); evlist_ar[0] = nsi->first_connect; evlist_ar[1] = nsi->first_read; evlist_ar[2] = nsi->first_write; corresp_list[0] = &nsi->nsp->connect_events; corresp_list[1] = &nsi->nsp->read_events; corresp_list[2] = &nsi->nsp->write_events; for (i = 0; i < 3 && nsi->events_pending > 0; i++) { for (current = evlist_ar[i]; current != NULL; current = next) { struct nevent *nse; next = gh_lnode_next(current); nse = lnode_nevent(current); /* we're done with this list of events for the current IOD */ if (nse->iod != nsi) break; nevent_delete(nsi->nsp, nse, corresp_list[i], current, pending_response == NSOCK_PENDING_NOTIFY); } } } if (nsi->events_pending != 0) fatal("Trying to delete NSI, but could not find %d of the purportedly pending events on that IOD.\n", nsi->events_pending); /* Make sure we no longer select on this socket, in case the socket counts * weren't already decremented to zero. */ if (nsi->sd >= 0) socket_count_zero(nsi, nsi->nsp); free(nsi->hostname); #if HAVE_OPENSSL /* Close any SSL resources */ if (nsi->ssl) { /* No longer free session because copy nsi stores is not reference counted */ #if 0 if (nsi->ssl_session) SSL_SESSION_free(nsi->ssl_session); nsi->ssl_session = NULL; #endif if (SSL_shutdown(nsi->ssl) == -1) { nsock_log_info("nsock_iod_delete: SSL shutdown failed (%s) on NSI %li", ERR_reason_error_string(SSL_get_error(nsi->ssl, -1)), nsi->id); } /* I don't really care if the SSL_shutdown() succeeded politely. I could * make the SD blocking temporarily for this, but I'm hoping it will succeed * 95% of the time because we can usually write to a socket. */ SSL_free(nsi->ssl); nsi->ssl = NULL; } #endif if (nsi->sd >= 0 && nsi->sd != STDIN_FILENO) { close(nsi->sd); nsi->sd = -1; } nsi->state = NSIOD_STATE_DELETED; nsi->userdata = NULL; if (nsi->ipoptslen) free(nsi->ipopts); #if HAVE_PCAP if (nsi->pcap){ mspcap *mp = (mspcap *)nsi->pcap; if (mp->pt){ pcap_close(mp->pt); mp->pt = NULL; } if (mp->pcap_desc) { /* pcap_close() will close the associated pcap descriptor */ mp->pcap_desc = -1; } if (mp->pcap_device) { free(mp->pcap_device); mp->pcap_device = NULL; } free(mp); nsi->pcap = NULL; } #endif if (nsi->px_ctx) proxy_chain_context_delete(nsi->px_ctx); }
/* Create a new event structure -- must be deleted later with event_delete, * unless it returns NULL (failure). NULL can be passed in for the struct niod * and the userdata if not available */ struct nevent *event_new(struct npool *nsp, enum nse_type type, struct niod *iod, int timeout_msecs, nsock_ev_handler handler, void *userdata) { struct nevent *nse; gh_lnode_t *lnode; /* Bring us up to date for the timeout calculation. */ gettimeofday(&nsock_tod, NULL); if (iod) { iod->events_pending++; assert(iod->state != NSIOD_STATE_DELETED); } /* First we check if one is available from the free list ... */ lnode = gh_list_pop(&nsp->free_events); if (!lnode) nse = (struct nevent *)safe_malloc(sizeof(*nse)); else nse = lnode_nevent(lnode); memset(nse, 0, sizeof(*nse)); nse->id = get_new_event_id(nsp, type); nse->type = type; nse->status = NSE_STATUS_NONE; gh_hnode_invalidate(&nse->expire); #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(iod != NULL); mp = (mspcap *)iod->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 = iod; nse->handler = handler; nse->userdata = userdata; if (nse->iod == NULL) nsock_log_debug("%s (IOD #NULL) (EID #%li)", __func__, nse->id); else nsock_log_debug("%s (IOD #%li) (EID #%li)", __func__, nse->iod->id, nse->id); return nse; }
/* Cancel an event (such as a timer or read request). If notify is nonzero, the * requester will be sent an event CANCELLED status back to the given handler. * But in some cases there is no need to do this (like if the function deleting * it is the one which created it), in which case 0 can be passed to skip the * step. This function returns zero if the event is not found, nonzero * otherwise. */ int nsock_event_cancel(nsock_pool ms_pool, nsock_event_id id, int notify) { struct npool *nsp = (struct npool *)ms_pool; enum nse_type type; unsigned int i; gh_list_t *event_list = NULL, *event_list2 = NULL; gh_lnode_t *current, *next; struct nevent *nse = NULL; assert(nsp); type = get_event_id_type(id); nsock_log_info("Event #%li (type %s) cancelled", id, nse_type2str(type)); /* First we figure out what list it is in */ switch (type) { case NSE_TYPE_CONNECT: case NSE_TYPE_CONNECT_SSL: event_list = &nsp->connect_events; break; case NSE_TYPE_READ: event_list = &nsp->read_events; break; case NSE_TYPE_WRITE: event_list = &nsp->write_events; break; case NSE_TYPE_TIMER: for (i = 0; i < gh_heap_count(&nsp->expirables); i++) { gh_hnode_t *hnode; hnode = gh_heap_find(&nsp->expirables, i); nse = container_of(hnode, struct nevent, expire); if (nse->id == id) return nevent_delete(nsp, nse, NULL, NULL, notify); } return 0; #if HAVE_PCAP case NSE_TYPE_PCAP_READ: event_list = &nsp->read_events; event_list2 = &nsp->pcap_read_events; break; #endif default: fatal("Bogus event type in nsock_event_cancel"); break; } /* Now we try to find the event in the list */ for (current = gh_list_first_elem(event_list); current != NULL; current = next) { next = gh_lnode_next(current); nse = lnode_nevent(current); if (nse->id == id) break; } if (current == NULL && event_list2) { event_list = event_list2; for (current = gh_list_first_elem(event_list); current != NULL; current = next) { next = gh_lnode_next(current); nse = lnode_nevent2(current); if (nse->id == id) break; } } if (current == NULL) return 0; return nevent_delete(nsp, nse, event_list, current, notify); }