예제 #1
0
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;
    }
  }
}
예제 #2
0
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);
      }
    }
  }
}
예제 #3
0
/* 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);
}
예제 #4
0
/* 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;
}
예제 #5
0
/* 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);
}