⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 socket.c

📁 开放源码的编译器open watcom 1.6.0版的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 *
 *   BSD sockets functionality for Waterloo TCP/IP
 *
 *   Version
 *
 *   0.5 : Dec 18, 1997 : G. Vanem - created
 */

#include "socket.h"
#include "pcdbug.h"

#if defined(USE_BSD_FUNC)

#ifdef __DJGPP__
  #include <sys/resource.h>
  #include <sys/fsext.h>
  #include <dos.h>
  #include <unistd.h>
#endif

#ifdef USE_LIBPCAP
  #include "w32pcap.h"
#endif

static int     sk_block = 0;        /* sock_daemon() semaphore */
static int     sk_last  = SK_FIRST; /* highest socket number */
static Socket *sk_list  = NULL;
static BOOL    sk_init  = 0;

#if 0  /* !!to-do */
  #define SOCK_HASH_SIZE (MAX_SOCKETS / 32)
  static Socket *sk_hashes [SOCK_HASH_SIZE];
#endif

#define FREE_SK(x)  do {           \
                      if (x) {     \
                         free (x); \
                         x = NULL; \
                      }            \
                    } while (0)


/*
 * Memory allocation; print some info if allocation fails
 */
void *_sock_calloc (const char *file, unsigned line, size_t size)
{
  void *ptr;

#if defined(__WATCOM386__) && 0  /* find DOS4GW bug! */
  int rc = _heapset (0xCC);

  if (rc != _HEAPOK && rc != _HEAPEMPTY)
     SOCK_FATAL (("%s (%u) Fatal: heap corrupt\r\n", file, line));
#endif

#if defined(USE_FORTIFY) || defined(USE_BSD_FORTIFY)
  ptr = Fortify_calloc (size, 1, file, line);
#else
  ptr = calloc (size, 1);
#endif

  if (!ptr)
  {
#if defined(__WATCOM386__) && 0  /* find DOS4GW bug! */
    struct _heapinfo hi;
    _heapwalk (&hi);
#endif
    SOCK_FATAL (("%s (%u) Fatal: Allocation failed\r\n", file, line));
  }

#if !defined(USE_BSD_FATAL) && !defined(USE_FORTIFY) && !defined(USE_BSD_FORTIFY)
  ARGSUSED (file);
  ARGSUSED (line);
#endif
  return (ptr);
}

#if defined(USE_BSD_FORTIFY)  /* to detect leaks done here */
  #undef  SOCK_CALLOC
  #define SOCK_CALLOC(x)  Fortify_calloc (x, 1, __FILE__, __LINE__)
#endif


/*
 *  `inuse[]' has a non-zero bit for each socket-descriptor in use.
 *  There can be max `MAX_SOCKETS' allocated at any time.  Dead stream
 *  sockets will be unlinked by `sock_daemon()' in due time.
 *
 *  Non-djgpp targets:
 *    Allocate a new descriptor (handle) by searching through `inuse' for
 *    the first zero bit. Update `sk_last' as needed.
 *
 *  djgpp target:
 *    Allocate a descriptior from the "File System Extension" layer.
 *    `sk_last' is not used (initialised to MAX_SOCKETS).
 */

static fd_set inuse [NUM_SOCK_FDSETS];

static int sock_get_fd (void)
{
#if defined(__DJGPP__) && defined(USE_FSEXT)
  extern int _fsext_demux (__FSEXT_Fnumber func,  /* in fsext.c */
                           int *rv, va_list _args);

  int s = __FSEXT_alloc_fd (_fsext_demux);

  if (s < 0)
  {
    SOCK_FATAL (("%s (%u) Fatal: FSEXT_alloc_fd() failed\r\n",
                __FILE__, __LINE__));
    return (-1);
  }

  if (FD_ISSET(s,&inuse[0]))
  {
    SOCK_FATAL (("%s (%u) Fatal: Reusing existing socket\n",
                __FILE__, __LINE__));
    return (-1);
  }

#else
  int s;

  for (s = SK_FIRST; s < sk_last; s++)
      if (!FD_ISSET(s,&inuse[0]) &&    /* not marked as in-use */
          !_socklist_find(s))      /* don't use a dying socket */
         break;

#endif /* __DJGPP__ && USE_FSEXT */

  if (s < MAX_SOCKETS)
  {
    if (s == sk_last)
       sk_last++;

    FD_SET (s, &inuse[0]);
    return (s);
  }

  /* No vacant bits in 'inuse' array. djgpp (and DOS) could theoretically
   * return a file-handle > 'MAX_SOCKETS-1'.
   */
  return (-1);
}

/*
 *  _sock_dos_fd -
 *    Return TRUE if `s' is a valid DOS handle.
 *    Used to differentiate EBADF from ENOTSOCK.
 *
 *    Note: for non-djgpp targets 's' may have same value as a
 *          DOS-handle. This function should only be used when 's'
 *          isn't found in 'sk_list'.
 */
int _sock_dos_fd (int s)
{
  if (s >= fileno(stdin) && s <= fileno(stderr)) /* 0..2 (redirected) */
     return (1);

  if (s > fileno(stderr) && isatty(s))
     return (1);
  return (0);
}

/*
 *  Setup a bigger receive buffer, the default in Wattcp
 *  is only 2k.
 *  Note: If calloc() fails, sock_setbuf() reverts to default
 *        2kB socket buffer.
 *  to-do: allow user to define size using SO_RCVBUF/SO_SNDBUF
 *         before calling connect().
 */
static __inline void set_rcv_buf (sock_type *p)
{
  int len = DEFAULT_RCV_WIN;
  sock_setbuf (p, calloc(len,1), len);
}

/*
 *  Free receive buffer associated with udp/tcp sockets
 */
static __inline void free_rcv_buf (sock_type *p)
{
  if (p->udp.rdata != &p->udp.rddata[0])
  {
    free (p->udp.rdata);
    p->udp.rdata = &p->udp.rddata[0];
    p->udp.rdatalen = 0;
  }
}

/*
 *  sk_list_del
 *    Deletes the list element associated with a socket.
 *    Return pointer to next node.
 *    Return NULL if no next or sock not found.
 */
static __inline Socket *sk_list_del (int s)
{
  Socket *sock, *next, *last;

  for (sock = last = sk_list; sock; last = sock, sock = sock->next)
  {
    if (sock->fd != s)
       continue;

    if (sock == sk_list)
         sk_list    = sock->next;
    else last->next = sock->next;
    next = sock->next;
    free (sock);
    return (next);
  }
  return (NULL);
}

/*
 *  Traverse socket-list to find other SOCK_STREAM sockets
 *  besides 'this' which are also listening.
 *  Unhook '_tcp_syn_hook' if none found.
 */
static __inline void unset_tcp_syn_hook (Socket *this)
{
  Socket *sock;
  int    num = 0;

  for (sock = sk_list; sock; sock = sock->next)
      if (sock->so_type == SOCK_STREAM      &&
         (sock->so_options & SO_ACCEPTCONN) &&
         sock != this)
        num++;

  if (num == 0)
     _tcp_syn_hook = NULL;
}

/*
 *  Traverse socket-list to find other SOCK_RAW sockets
 *  besides 'this'. Unhook '_raw_ip_hook' if none found.
 */
static __inline void unset_raw_ip_hook (Socket *this)
{
  Socket *sock;
  int    num = 0;

  for (sock = sk_list; sock; sock = sock->next)
      if (sock->so_type == SOCK_RAW && sock != this)
         num++;

  if (num == 0)
     _raw_ip_hook = NULL;
}

/*
 *  _sock_del_fd
 *    Delete the socket from `inuse' array and all memory associated
 *    with it. Also unlink it from the socket list (sk_list).
 *    Return pointer to next node in list or NULL if none/error.
 */
Socket * _sock_del_fd (const char *file, unsigned line, int s)
{
  Socket    *sock, *next = NULL;
  sock_type *sk;

  SOCK_DEBUGF ((NULL, "\n  _sock_del_fd:%d", s));

  if (s < SK_FIRST || s >= sk_last || !FD_ISSET(s,&inuse[0]))
  {
    SOCK_FATAL (("%s (%u) Fatal: socket %d not inuse\r\n", file, line, s));
    return (NULL);
  }

  sock = _socklist_find (s);
  if (!sock)
  {
    SOCK_FATAL (("%s (%u) Fatal: socket %d not in list\r\n", file, line, s));
    goto not_inuse;
  }

  if (sock->cookie != SAFETYTCP)  /* Aaarg! marker destroyed */
  {
    SOCK_FATAL (("%s (%u) fatal: socket %d (%p) overwritten\r\n",
                 file, line, s, sock));
    goto not_inuse;
  }

  switch (sock->so_type)
  {
    case SOCK_STREAM:
         sk = (sock_type*) sock->tcp_sock;
         if (sk)
         {
           reuse_localport (sk->tcp.myport); /* clear 'lport_inuse' bit now */
           sock_abort (sk);
           free_rcv_buf (sk);
         }
         FREE_SK (sock->tcp_sock);
         unset_tcp_syn_hook (sock);
         break;

    case SOCK_DGRAM:
         sk = (sock_type*) sock->udp_sock;
         if (sk)
         {
           reuse_localport (sk->udp.myport);
           sock_abort (sk);
           free_rcv_buf (sk);
         }
         FREE_SK (sock->udp_sock);
         break;

    case SOCK_RAW:
         sock->raw_sock->ip_type = 0;
         sock->raw_sock->next    = NULL;
         FREE_SK (sock->raw_sock);
         unset_raw_ip_hook (sock);
         break;

    default:
         SOCK_DEBUGF ((NULL, "\n  _sock_del_fd(%d): unknown type %d",
                       s, sock->so_type));
         break;
  }

  FREE_SK (sock->local_addr);
  FREE_SK (sock->remote_addr);    
  FREE_SK (sock->ip_opt);
  FREE_SK (sock->bcast_pool);

#if defined(USE_FSEXT) && defined(__DJGPP__)
  /* Free the socket from File-System Extension system.
   * Free the duplicated handle from DOS's System File Table.
   */
  __FSEXT_set_function (s, NULL);
  _close (s);
#endif

  next = sk_list_del (s);  /* delete socket from linked list */

not_inuse:
  if (s == sk_last-1)
     sk_last--;

  FD_CLR (s, &inuse[0]);

#if !defined(USE_DEBUG)
  ARGSUSED (file);
  ARGSUSED (line);
#endif

  return (next);
}

#ifdef NOT_USED
/*
 *  sock_find_fd
 *    Finds the 'fd' associated with pointer 'socket'.
 *    Return -1 if not found.
 */
static int sock_find_fd (const Socket *socket)
{
  Socket *sock;

  for (sock = sk_list; sock; sock = sock->next)
      if (sock == socket)
         return (sock->fd);
  return (-1);
}

/*
 *  sock_find_udp
 *    Finds the 'Socket' associated with udp-socket 'udp'.
 *    Return NULL if not found.
 */
static Socket *sock_find_udp (const udp_Socket *udp)
{
  Socket *sock;

  for (sock = sk_list; sock; sock = sock->next)
      if (sock->udp_sock == udp)
         return (sock);
  return (NULL);
}
#endif

/*
 *  sock_find_tcp
 *    Finds the 'Socket' associated with tcp-socket 'tcp'.
 *    Return NULL if not found.
 */
static void *sock_find_tcp (const tcp_Socket *tcp)
{
  Socket *sock;

  for (sock = sk_list; sock; sock = sock->next)
      if (sock->tcp_sock == tcp)
         return (void*)sock;
  return (NULL);
}


/*
 *  sock_raw_recv - Called from _ip_handler() via `_raw_ip_hook'.
 *    IP-header is already checked in _ip_handler().
 *    Finds all 'Socket' associated with raw IP-packet 'ip'.
 *    Enqueue to 'sock->raw_sock'.
 *    Return >=1 if 'ip' is consumed, 0 otherwise.
 *
 *  Fix-me: This routine will steal all packets destined for
 *          SOCK_STREAM/SOCK_DGRAM sockets if those sockets are
 *          allocated after the SOCK_RAW socket (behind in sk_list).
 */
static int sock_raw_recv (const in_Header *ip)
{
  Socket *sock;
  int     num_enqueued = 0;
  int     num_dropped  = 0;
  int     hlen = in_GetHdrLen (ip);
  DWORD   dst  = ntohl (ip->destination);
  size_t  len  = ntohs (ip->length);

  /* Jumbo packets won't match any raw-sockets
   */
  if (len > sizeof(sock->raw_sock->data))
     return (0);

  /* Not addressed to us or not (limited) broadcast
   */
  if (!is_local_addr(dst) && !is_ip_brdcast(ip))
     return (0);

  for (sock = sk_list; sock; sock = sock->next)
  {
#if 0 /* !! to-do */
    if (sock->so_type == SOCK_RAW && sock->so_proto == IPPROTO_RAW)
       ; /* socket matches every IP-protocol, enqueue */
#endif

    if (ip->proto == IPPROTO_TCP && sock->so_type == SOCK_STREAM)
       return (0);

    if (ip->proto == IPPROTO_UDP && sock->so_type == SOCK_DGRAM)
       return (0);

    if (sock->so_type != SOCK_RAW ||
        (ip->proto != sock->so_proto && sock->so_proto != IPPROTO_IP))
       continue;

    /* !!to-do: follow the 'sock->raw_sock->next' pointer to first
     *          vacant buffer.
     * assumes sock->raw_sock is non-NULL
     */
    if (sock->raw_sock->used)  
    {
      num_dropped++;
      SOCK_DEBUGF ((sock, "\n  socket %d dropped IP, proto %d",
                    sock->fd, ip->proto));
    }
    else
    {
      /* Copy IP-header to raw_sock.ip
       */
      memcpy (&sock->raw_sock->ip, ip, sizeof(*ip));

      /* Copy any IP-options
       */
      if (hlen > sizeof(*ip) && sock->ip_opt)
      {
        int olen = min (sock->ip_opt_len, hlen - sizeof(*ip));
        memcpy (&sock->ip_opt, ip+1, olen);
      }

      /* Copy rest of IP-packet
       */
      memcpy (&sock->raw_sock->data, (BYTE*)ip+hlen, len);
      sock->raw_sock->used = TRUE;
      num_enqueued++;
    }
  }

  if (num_enqueued > 0)     /* both enqueued and dropped is possible */
     STAT (ipstats.ips_delivered++);

  if (num_dropped > 0)
     STAT (ipstats.ips_idropped++);

#ifdef USE_DEBUG
  if (num_dropped > 0 || num_enqueued > 0)
     DEBUG_RX (NULL, ip);
#endif

  return (num_enqueued);
}

/*
 *  tcp_sock_daemon -
 *    Called by sock_daemon() to handle SOCK_STREAM sockets.
 *
 *    Unlink the socket from the linked list if application has
 *    read all data and tcp_state has become CLOSED and the linger
 *    period has expired.
 *
 */
static Socket *tcp_sock_daemon (Socket *sock, tcp_Socket *tcp)
{
  Socket *next  = sock->next;
  int     s     = sock->fd;
  int     state = tcp->state;

  if ((sock->so_options & SO_KEEPALIVE) && chk_timeout(sock->keepalive))
  {
    sock_keepalive ((sock_type*)tcp);
    if (tcp_keepalive)
         sock->keepalive = set_timeout (1000 * tcp_keepalive);
    else sock->keepalive = 0;
  }
  
  if (state == tcp_StateSYNSENT)        /* opening active tcp session */
  {
    sock->so_state |= SS_ISCONNECTING;
  }
  else if (state == tcp_StateESTAB)        /* established tcp session */
  {
    sock->so_state |=  SS_ISCONNECTED;
    sock->so_state &= ~SS_ISCONNECTING;
    sock->so_state &= ~SS_ISDISCONNECTING;
  }
  else if (state >= tcp_StateTIMEWT)             /* dying tcp session */
  {
    sock_type *sk = (sock_type*)tcp;
    int  closing  = sock->so_state & (SS_ISDISCONNECTING | SS_CANTSENDMORE);

    sock->so_state &= ~(SS_ISCONNECTED | SS_ISCONNECTING);

    if (sock->close_time && (sock->so_state & SS_CANTRCVMORE))
    {
      /* Flush any remaining Rx data received after shutdown(0) called.
       */
      sock_fastread (sk, NULL, -1);
    }

    if (closing && tcp->ip_type == 0) /* fully closed, refused or aborted */
    {
      int expired = 0;

      if (!sock_rbused(sk))
      {
        free_rcv_buf (sk);         /* free memory not needed anymore */
        FREE_SK (sock->ip_opt);
      }

      if (sock->close_time)        /* close_s() called */
         expired = (time(NULL) - sock->close_time >= sock->linger_time);

      /* If linger-period expired and fully closed, delete the TCB
       */
      if (expired && state == tcp_StateCLOSED)  
      {
        SOCK_DEBUGF ((sock, "\n  tcp_sock_daemon del:%d, lport %d",
                      s, tcp->myport));
        next = SOCK_DEL_FD (s);
      }
    }
  }
  return (next);
}


/*
 * Called by sock_daemon() for SOCK_DGRAM sockets.
 *
 * Unlink the socket from the linked list if application have read all
 * data and if "state" is disconnecting.
 *
 * Note: Setting 'SS_ISDISCONNECTING' is really a mis-nomer, but
 * should indicate socket is closed/aborted with Rx-data remaining.
 */
static Socket *udp_sock_daemon (Socket *sock, udp_Socket *udp)
{
  Socket *next = sock->next;

  if ((sock->so_state & (SS_ISDISCONNECTING | SS_CANTSENDMORE)) &&
      (udp->rdatalen == 0 || udp->ip_type == 0))
  {
    SOCK_DEBUGF ((sock, "\n  udp_sock_daemon del:%d", sock->fd));
    next = SOCK_DEL_FD (sock->fd);
  }
  return (next);
}

/*
 * Called from tcp_tick(), but not more than once every 55msec
 */
static void sock_daemon (void)
{
  Socket *sock, *next = NULL;

  /* If we're in a critical region (e.g. select_s()) where we don't
   * want our socket-list to change, do this later.
   */
  if (sk_block)
     return;

⌨️ 快捷键说明

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