nsock_core.c

来自「Ubuntu packages of security software。 相」· C语言 代码 · 共 1,208 行 · 第 1/3 页

C
1,208
字号
/*************************************************************************** * nsock_core.c -- This contains the core engine routines for the nsock    * * parallel socket event library.                                          * *                                                                         * ***********************IMPORTANT NSOCK LICENSE TERMS*********************** *                                                                         * * The nsock parallel socket event library is (C) 1999-2008 Insecure.Com   * * LLC This library is free software; you may redistribute and/or          * * modify it under the terms of the GNU General Public License as          * * published by the Free Software Foundation; Version 2.  This guarantees  * * your right to use, modify, and redistribute this software under certain * * conditions.  If this license is unacceptable to you, Insecure.Com LLC   * * may be willing to sell alternative licenses (contact                    * * sales@insecure.com ).                                                   * *                                                                         * * As a special exception to the GPL terms, Insecure.Com LLC grants        * * permission to link the code of this program with any version of the     * * OpenSSL library which is distributed under a license identical to that  * * listed in the included Copying.OpenSSL file, and distribute linked      * * combinations including the two. You must obey the GNU GPL in all        * * respects for all of the code used other than OpenSSL.  If you modify    * * this file, you may extend this exception to your version of the file,   * * but you are not obligated to do so.                                     * *                                                                         *  * If you received these files with a written license agreement stating    * * terms other than the (GPL) terms above, then that alternative license   * * agreement takes precedence over this comment.                          * *                                                                         * * Source is provided to this software because we believe users have a     * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes (none     * * have been found so far).                                                * *                                                                         * * Source code also allows you to port Nmap to new platforms, fix bugs,    * * and add new features.  You are highly encouraged to send your changes   * * to fyodor@insecure.org for possible incorporation into the main         * * distribution.  By sending these changes to Fyodor or one the            * * insecure.org development mailing lists, it is assumed that you are      * * offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right * * to reuse, modify, and relicense the code.  Nmap will always be          * * available Open Source, but this is important because the inability to   * * relicense code has caused devastating problems for other Free Software  * * projects (such as KDE and NASM).  We also occasionally relicense the    * * code to third parties as discussed above.  If you wish to specify       * * special license conditions of your contributions, just say so when you  * * send them.                                                              * *                                                                         * * This program is distributed in the hope that it will be useful, but     * * WITHOUT ANY WARRANTY; without even the implied warranty of              * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       * * General Public License for more details (                               * * http://www.gnu.org/copyleft/gpl.html ).                                 * *                                                                         * ***************************************************************************//* $Id: nsock_core.c 6635 2007-12-22 06:32:18Z fyodor $ */#include "nsock_internal.h"#include "gh_list.h"#include "filespace.h"#include <assert.h>#if HAVE_ERRNO_H#include <errno.h>#endif#if HAVE_SYS_TYPES_H#include <sys/types.h>#endif#if HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif#if HAVE_NETINET_IN_H#include <netinet/in.h>#endif#if HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#if HAVE_STRING_H#include <string.h>#endif#include "netutils.h"#if HAVE_PCAP#include "nsock_pcap.h"static int pcap_read_on_nonselect(mspool *nsp);#endif/* Msock time of day -- we update this at least once per   nsock_loop round (and after most calls that are likely to block).     Other nsock files should grab this */struct timeval nsock_tod;/* Returns -1 (and sets ms->errno if there is an error so severe that   we might as well quit waiting and have nsock_loop() return an error */static int wait_for_events(mspool *ms, int msec_timeout) {  int event_msecs; /* Msecs before an event goes off */  int combined_msecs;  int sock_err = 0;  int socketno = 0;  struct timeval select_tv;  struct timeval *select_tv_p;  assert(msec_timeout >= -1);  if (ms->evl.events_pending == 0)    return 0; /* No need to wait on 0 events ... */  do {    if (ms->tracelevel > 3)        nsock_trace(ms, "wait_for_events");            if (ms->evl.next_ev.tv_sec == 0) {      event_msecs = -1; /* None of the events specified a timeout */    } else {      event_msecs = MAX(0, TIMEVAL_MSEC_SUBTRACT(ms->evl.next_ev, nsock_tod));    }    /* 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);        /* printf("wait_for_events: starting wait -- combined_msecs=%d\n", combined_msecs); */    /* 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;    }            /* Figure out whether there are any FDs in the sets, as @$@!$#       Windows returns WSAINVAL (10022) if you call a select() with no       FDs, even though the Linux man page says that doing so is a       good, reasonably portable way to sleep with subsecond       precision.  Sigh */    for(socketno = ms->mioi.max_sd; socketno >= 0; socketno--) {      if(FD_ISSET(socketno, &ms->mioi.fds_master_r) ||	 FD_ISSET(socketno, &ms->mioi.fds_master_w) ||	 FD_ISSET(socketno, &ms->mioi.fds_master_x))	break;      else ms->mioi.max_sd--;    }#if HAVE_PCAP    /* do non-blocking read on pcap devices that doesn't support select()      * If there is anything read, don't do usleep() or select(), just leave this loop */    if (pcap_read_on_nonselect(ms)) {    	/* okay, something was read. */    } else #endif      if (ms->mioi.max_sd < 0) {        ms->mioi.results_left = 0;        if (combined_msecs > 0)	  usleep(combined_msecs * 1000);      } else {        /* Set up the descriptors for select */        ms->mioi.fds_results_r = ms->mioi.fds_master_r;        ms->mioi.fds_results_w = ms->mioi.fds_master_w;        ms->mioi.fds_results_x = ms->mioi.fds_master_x;              ms->mioi.results_left = select(ms->mioi.max_sd + 1, &ms->mioi.fds_results_r, &ms->mioi.fds_results_w, &ms->mioi.fds_results_x, select_tv_p);        if (ms->mioi.results_left == -1)  	  sock_err = socket_errno();      }    gettimeofday(&nsock_tod, NULL); /* Due to usleep or select delay */  } while (ms->mioi.results_left == -1 && sock_err == EINTR);// repeat only if signal occured    if (ms->mioi.results_left == -1 && sock_err != EINTR) {    ms->errnum = sock_err;    return -1;  }   return 0;}  /* A handler function is defined for each of the main event types     (read, write, connect, timer, etc) -- the handler is called when     new information is available for the event.  The handler makes     any neccessary updates to the event based on any new information     available.  If the event becomes ready for delivery, the handler     sets nse->event_done and fills out the relevant event fields     (status, errnum) as applicable.  The handlers also take care of     event type specific teardown (such as clearing socket descriptors     from select/poll lists).  If event_done is not set, the handler     will be called again in the case of more information or an event     timeout */  /* The event type handlers -- the first three arguments of each are the same:     mspool *ms     msevent *nse -- the event we have new info on     enum nse_status -- The reason for the call, usually NSE_STATUS_SUCCESS                         (which generally means a successful I/O call or 			NSE_STATUS_TIMEOUT or NSE_STATUS_CANCELLED     Some of the event type handlers have other parameters, specific     to their needs.  All the handlers can assume that the calling     function has checked that select or poll said their descriptors     were readable/writeable (as appropriate).     The idea is that each handler will take care of the stuff that is      specific to it and the calling function will handle the stuff that     can be generalized to dispatching/deleting/etc. all events.  But the     calling function may use type-specific info to determine whether     the handler should be called at all (to save CPU time).  *//* 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);  char buf[1024];  msiod *iod = nse->iod;#if HAVE_OPENSSL  struct NsockSSLInfo *sslnfo;  int sslerr;  int sslconnect_inprogress = nse->type == NSE_TYPE_CONNECT_SSL && iod->ssl;#else  int sslconnect_inprogress = 0;#endif  int rc;  rc = 0;  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:#ifdef LINUX      if (!FD_ISSET(iod->sd, &ms->mioi.fds_results_r)) {	/* Linux goofiness -- We need to actually test that it is writeable */	rc = send(iod->sd, "", 0, 0);		if (rc < 0 ) {	  nse->status = NSE_STATUS_ERROR;	  nse->errnum = ECONNREFUSED;	} else {	  nse->status = NSE_STATUS_SUCCESS;	}      } else {	nse->status = NSE_STATUS_SUCCESS;      }#else      nse->status = NSE_STATUS_SUCCESS;#endif      break;    case ECONNREFUSED:    case EHOSTUNREACH:      nse->status = NSE_STATUS_ERROR;      nse->errnum = optval;      break;    case ENETDOWN:    case ENETUNREACH:    case ENETRESET:    case ECONNABORTED:    case ETIMEDOUT:    case EHOSTDOWN:    case ECONNRESET:      nse->status = NSE_STATUS_ERROR;      nse->errnum = optval;      break;    default:      Snprintf(buf, sizeof(buf), "Strange connect error from %s (%d)", inet_ntop_ez(&iod->peer, iod->peerlen), optval);      perror(buf);      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      sslnfo = Nsock_SSLGetInfo();      iod->ssl = SSL_new(sslnfo->ctx);      if (!iod->ssl)	fatal("SSL_new failed: %s", ERR_error_string(ERR_get_error(), NULL));      /* 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 */  }  /* Clear the socket descriptors from all the lists -- we might     put it back on some of them in the SSL case */  if (iod->sd != -1) {      FD_CLR(iod->sd, &ms->mioi.fds_master_r);    FD_CLR(iod->sd, &ms->mioi.fds_master_w);    FD_CLR(iod->sd, &ms->mioi.fds_master_x);    FD_CLR(iod->sd, &ms->mioi.fds_results_r);    FD_CLR(iod->sd, &ms->mioi.fds_results_w);    FD_CLR(iod->sd, &ms->mioi.fds_results_x);        if (ms->mioi.max_sd == iod->sd)      ms->mioi.max_sd--;  }#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) { printf("Uh-oh: SSL_set_session() failed - please tell Fyodor\n"); }     iod->ssl_session = NULL; /* No need for this any more */    }    rc = SSL_connect(iod->ssl);    /* printf("DBG: SSL_connect()=%d", rc); */    if (rc == 1) {      /* Woop!  Connect is done! */      nse->event_done = 1;      nse->status = NSE_STATUS_SUCCESS;    } else {      sslerr = SSL_get_error(iod->ssl, rc);      if (rc == -1 && sslerr == SSL_ERROR_WANT_READ) {	nse->sslinfo.ssl_desire = sslerr;	FD_SET(iod->sd, &ms->mioi.fds_master_r);	ms->mioi.max_sd = MAX(ms->mioi.max_sd, iod->sd);      } else if  (rc == -1 && sslerr == SSL_ERROR_WANT_WRITE) {	nse->sslinfo.ssl_desire = sslerr;	FD_SET(iod->sd, &ms->mioi.fds_master_w);	ms->mioi.max_sd = MAX(ms->mioi.max_sd, iod->sd);      } else {	/* Unexpected error */	nse->event_done = 1;	nse->status = NSE_STATUS_ERROR;	nse->errnum = EIO;      }    }  }#endif  return;}void handle_write_result(mspool *ms, msevent *nse, 			       enum nse_status status)  {  int bytesleft;  char *str;  int res;  int err;  msiod *iod = nse->iod;  if (status == NSE_STATUS_TIMEOUT || status == NSE_STATUS_CANCELLED) {    nse->event_done = 1;    nse->status = status;  } else if (status == NSE_STATUS_SUCCESS) {    str = FILESPACE_STR(&nse->iobuf) + nse->writeinfo.written_so_far;    bytesleft = FILESPACE_LENGTH(&nse->iobuf) - nse->writeinfo.written_so_far;    assert(bytesleft > 0);#if HAVE_OPENSSL    if (iod->ssl)      res = SSL_write(iod->ssl, str, bytesleft);    else#endif      res = send(nse->iod->sd, str, bytesleft, 0);    if (res == bytesleft) {      nse->event_done = 1;      nse->status = NSE_STATUS_SUCCESS;    } else if (res >= 0) {      nse->writeinfo.written_so_far += res;          } else {      assert(res == -1);      if (iod->ssl) {#if HAVE_OPENSSL

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?