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

📄 sockopt.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
 *   0.6 : Apr 07, 2000 : Added multicast options IP_ADD_MEMBERSHIP and
 *                        IP_DROP_MEMBERSHIP.
 *                        Contributed by Vlad Erochine <vlad@paragon.ru>
 *   0.7 : Jun 06, 2000 : Added support for SO_SNDLOWAT and SO_RCVLOWAT
 */

#include "socket.h"

#if defined(USE_BSD_FUNC)

static int set_sol_opt (Socket *s, int opt, const void *val, int len);
static int set_raw_opt (Socket *s, int opt, const void *val, int len);
static int get_sol_opt (Socket *s, int opt, void *val, int *len);
static int get_raw_opt (Socket *s, int opt, void *val, int *len);

static int set_tcp_opt (tcp_Socket *tcp, int opt, const void *val, int len);
static int set_udp_opt (udp_Socket *udp, int opt, const void *val, int len);
static int get_tcp_opt (tcp_Socket *tcp, int opt, void *val, int *len);
static int get_udp_opt (udp_Socket *udp, int opt, void *val, int *len);

static int udp_rx_buf  (udp_Socket *udp, unsigned size);
static int tcp_rx_buf  (tcp_Socket *tcp, unsigned size);
static int raw_rx_buf  (raw_Socket *raw, unsigned size);
static int udp_tx_buf  (udp_Socket *udp, unsigned size);
static int tcp_tx_buf  (tcp_Socket *tcp, unsigned size);
static int raw_tx_buf  (raw_Socket *raw, unsigned size);
static int set_tx_lowat (Socket *s, unsigned  size);
static int set_rx_lowat (Socket *s, unsigned  size);
static int get_tx_lowat (const Socket *s, unsigned *size);
static int get_rx_lowat (const Socket *s, unsigned *size);

#if defined(USE_DEBUG)
static const char *sockopt_name (int option, int level);

static __inline void do_error (void)
{
  char *err = strerror_s (errno_s);
  char *par = strchr (err, '(');

  if (!par)
     _sock_debugf (NULL, " errno %d", errno_s);
  else
  {
    char *eol = strrchr (par, '\n');
    if (eol)
       *eol = '\0';
    _sock_debugf (NULL, " %s", par);
  }
}
#endif

int setsockopt (int s, int level, int option, const void *optval, int optlen)
{
  Socket *socket = _socklist_find (s);
  int     rc;

  SOCK_DEBUGF ((socket, "\nsetsockopt:%d, %s", s, sockopt_name(option,level)));

  if (!socket)
  {
    if (_sock_dos_fd(s))
    {
      SOCK_DEBUGF ((NULL, ", ENOTSOCK"));
      SOCK_ERR (ENOTSOCK);
      return (-1);
    }
    SOCK_DEBUGF ((NULL, ", EBADF"));
    SOCK_ERR (EBADF);
    return (-1);
  }

  VERIFY_RW (optval, optlen);

  if ((WORD)level == SOL_SOCKET)
     rc = set_sol_opt (socket, option, optval, optlen);

  else if ((level == socket->so_proto) && (level == IPPROTO_TCP))
     rc = set_tcp_opt (socket->tcp_sock, option, optval, optlen);

  else if ((level == socket->so_proto) && (level == IPPROTO_UDP))
     rc = set_udp_opt (socket->udp_sock, option, optval, optlen);

  else if (((level == socket->so_proto) && (level == IPPROTO_IP)) ||
           ((level == socket->so_proto) && (level == IPPROTO_ICMP)))
     rc = set_raw_opt (socket, option, optval, optlen);

  else
  {
    SOCK_ERR (ENOPROTOOPT);
    rc = -1;
  }

#if defined(USE_DEBUG)
  if (rc < 0)
     do_error();
#endif

  return (rc);
}

int getsockopt (int s, int level, int option, void *optval, int *optlen)
{
  Socket *socket = _socklist_find (s);
  int     rc;

  SOCK_DEBUGF ((socket, "\ngetsockopt:%d, %s", s, sockopt_name(option,level)));

  if (!socket)
  {
    if (_sock_dos_fd(s))
    {
      SOCK_DEBUGF ((NULL, ", ENOTSOCK"));
      SOCK_ERR (ENOTSOCK);
      return (-1);
    }
    SOCK_DEBUGF ((NULL,", EBADF"));
    SOCK_ERR (EBADF);
    return (-1);
  }

  VERIFY_RW (optval, 0);
  VERIFY_RW (optlen, sizeof(u_long));

  if ((WORD)level == SOL_SOCKET)
     rc = get_sol_opt (socket, option, optval, optlen);

  else if (level == socket->so_proto == IPPROTO_TCP)
     rc = get_tcp_opt (socket->tcp_sock, option, optval, optlen);

  else if (level == socket->so_proto == IPPROTO_UDP)
     rc = get_udp_opt (socket->udp_sock, option, optval, optlen);

  else if ((level == socket->so_proto == IPPROTO_IP) ||
           (level == socket->so_proto == IPPROTO_ICMP))
     rc = get_raw_opt (socket, option, optval, optlen);

  else
  {
    SOCK_ERR (ENOPROTOOPT);
    rc = -1;
  }

#if defined(USE_DEBUG)
  if (rc < 0)
     do_error();
#endif

  return (rc);
}


static int set_sol_opt (Socket *s, int opt, const void *val, int len)
{
  struct timeval *tv   = (struct timeval*) val;
  int             on   = *(int*) val;
  unsigned        size = *(unsigned*) val;

  switch (opt)
  {
    case SO_DEBUG:
#if defined(USE_DEBUG)
         if (on)
         {
           s->so_options |= SO_DEBUG;
           _sock_dbug_on();
         }
         else
         {
           s->so_options &= ~SO_DEBUG;
           _sock_dbug_off();
         }
#endif
         break;

    case SO_ACCEPTCONN:
         if (on)
              s->so_options |=  SO_ACCEPTCONN;
         else s->so_options &= ~SO_ACCEPTCONN;
         break;

    case SO_RCVTIMEO:
         if (len != sizeof(*tv) || tv->tv_usec < 0)
         {
           SOCK_ERR (EINVAL);
           return (-1);
         }
         if (tv->tv_sec == 0)        /* i.e. use system default */
              s->timeout = sock_delay;
         else s->timeout = tv->tv_sec + tv->tv_usec/1000000UL;
         break;

    case SO_SNDTIMEO:    /* Don't think we need this */
         break;

    /*
     * SO_REUSEADDR enables local address reuse, used to bind
     * multiple socks to the same port but with different ip-addr.
     */
    case SO_REUSEADDR:
         if (s->tcp_sock && s->so_proto == IPPROTO_TCP)
         {
           reuse_localport (s->tcp_sock->myport);
           return (0);
         }
         if (s->udp_sock && s->so_proto == IPPROTO_UDP)
         {
           reuse_localport (s->udp_sock->myport);
           return (0);
         }
         SOCK_ERR (ENOPROTOOPT);
         return (-1);

    /*
     * SO_REUSEPORT enables duplicate address and port bindings
     * ie, one can bind multiple socks to the same <ip_addr.port> pair
     */
/*  case SO_REUSEPORT:  missing in BSD? */
    case SO_KEEPALIVE:
    case SO_DONTROUTE:
    case SO_DONTLINGER:
    case SO_BROADCAST:
    case SO_USELOOPBACK:
    case SO_OOBINLINE:
         break;

    case SO_SNDLOWAT:
         return set_tx_lowat (s, size);

    case SO_RCVLOWAT:
         return set_rx_lowat (s, size);

    case SO_RCVBUF:
         if (size == 0)
         {
           SOCK_ERR (EINVAL);
           return (-1);
         }
         if (s->udp_sock && s->so_proto == IPPROTO_UDP)
            return udp_rx_buf (s->udp_sock, size);

         if (s->tcp_sock && s->so_proto == IPPROTO_TCP)
            return tcp_rx_buf (s->tcp_sock, size);

         if (s->raw_sock)
            return raw_rx_buf (s->raw_sock, size);

         SOCK_ERR (ENOPROTOOPT);
         return (-1);

    case SO_SNDBUF:
         if (size == 0)
         {
           SOCK_ERR (EINVAL);
           return (-1);
         }
         if (s->udp_sock && s->so_proto == IPPROTO_UDP)
            return udp_tx_buf (s->udp_sock, size);

         if (s->tcp_sock && s->so_proto == IPPROTO_TCP)
            return tcp_tx_buf (s->tcp_sock, size);

         if (s->raw_sock)
            return raw_tx_buf (s->raw_sock, size);

         SOCK_ERR (ENOPROTOOPT);
         return (-1);

    case SO_LINGER:
         {
           struct linger *linger = (struct linger*) val;

           if (len < sizeof(*linger))
           {
             SOCK_ERR (EINVAL);
             return (-1);
           }

           if (s->so_type != SOCK_STREAM || !s->tcp_sock)
           {
             SOCK_ERR (ENOPROTOOPT);
             return (-1);
           }

           if (linger->l_onoff == 0 && linger->l_linger == 0)
           {
             s->tcp_sock->locflags &= ~LF_LINGER;
             s->linger_time = 0;
           }
           else if (linger->l_onoff && linger->l_linger > 0)
           {
             unsigned sec = TCP_LINGERTIME;

             if (linger->l_linger < 100 * TCP_LINGERTIME)
                sec = linger->l_linger / 100;  /* in 10ms units */

             s->linger_time = sec;
             s->tcp_sock->locflags |= LF_LINGER;
           }
         }
         break;

    default:
         SOCK_ERR (ENOPROTOOPT);
         return (-1);
  }
  return (0);
}


static int get_sol_opt (Socket *s, int opt, void *val, int *len)
{
  struct timeval *tv;
  unsigned       *size = (unsigned*)val;

  switch (opt)
  {
    case SO_DEBUG:
    case SO_ACCEPTCONN:
         *(int*)val = (s->so_options & opt);
         *len = sizeof(int);
         break;

    case SO_OOBINLINE:
#if 0
         if (!s->tcp_sock || s->so_proto != IPPROTO_TCP)
         {
           SOCK_ERR (ENOPROTOOPT);
           return (-1);
         }
         if (s->so_options & SO_OOBINLINE)
              *(int*)val = urgent_data ((sock_type*)s->tcp_sock);
         else *(int*)val = 0;
         break;
#endif

    case SO_REUSEADDR:    /* to-do !! */
    case SO_KEEPALIVE:
    case SO_DONTROUTE:
    case SO_DONTLINGER:
    case SO_BROADCAST:
    case SO_USELOOPBACK:
         break;

    case SO_SNDLOWAT:
         return get_tx_lowat (s, size);

    case SO_RCVLOWAT:
         return get_rx_lowat (s, size);

    case SO_RCVBUF:
         if (s->udp_sock && s->so_proto == IPPROTO_UDP)
         {
           *(int*)val = sock_rbsize ((sock_type*)s->udp_sock);
           return (0);
         }
         if (s->tcp_sock && s->so_proto == IPPROTO_TCP)
         {
           *(int*)val = sock_rbsize ((sock_type*)s->tcp_sock);
           return (0);
         }
         if (s->raw_sock)
         {
           *(size_t*)val = sizeof (s->raw_sock->data);
           return (0);
         }
         SOCK_ERR (ENOPROTOOPT);
         return (-1);

    case SO_SNDBUF:
         if (s->udp_sock && s->so_proto == IPPROTO_UDP)
         {
           *(unsigned*)val = 0;
           return (0);
         }
         if (s->tcp_sock && s->so_proto == IPPROTO_TCP)
         {
           *(unsigned*)val = sock_tbsize ((sock_type*)s->tcp_sock);
           return (0);
         }
         if (s->raw_sock)
         {
           *(size_t*)val = sizeof (s->raw_sock->data);
           return (0);
         }
         SOCK_ERR (ENOPROTOOPT);
         return (-1);

    case SO_LINGER:
         {
           struct linger *linger = (struct linger*) val;

           if (!len || *len < sizeof(*linger))
           {
             SOCK_ERR (EINVAL);
             return (-1);
           }
           if (s->so_type != SOCK_STREAM || !s->tcp_sock)
           {
             SOCK_ERR (ENOPROTOOPT);
             return (-1);
           }
           *(size_t*)len    = sizeof(*linger);
           linger->l_onoff  = (s->tcp_sock->locflags & LF_LINGER) ? 1 : 0;
           linger->l_linger = 100 * s->linger_time;
         }
         break;

    case SO_SNDTIMEO:
         break;

    case SO_RCVTIMEO:
         if (*len < sizeof(*tv))
         {
           SOCK_ERR (EINVAL);
           return (-1);
         }
         tv = (struct timeval*)val;
         if (s->timeout == 0)
         {
           tv->tv_usec = LONG_MAX;
           tv->tv_sec  = LONG_MAX;
         }
         else
         {
           tv->tv_usec = 0;
           tv->tv_sec  = s->timeout;
         }
         break;

    case SO_ERROR:
         *(int*)val    = s->so_error;
         *(size_t*)len = sizeof(s->so_error);
         s->so_error = 0;   /* !! should be do this */
         break;

    case SO_TYPE:
         *(int*)val    = s->so_type;
         *(size_t*)len = sizeof(s->so_type);
         break;

    default:
         SOCK_ERR (ENOPROTOOPT);
         return (-1);
  }
  SOCK_ERR (0);
  return (0);
}

/*
 * set/get TCP-layer options
 */
static int set_tcp_opt (tcp_Socket *tcp, int opt, const void *val, int len)
{
  BOOL on = *(BOOL*)val;
  long MSS;

  switch (opt)
  {
    case TCP_NODELAY:
         if (on)     /* disable Nagle's algorithm */
         {
           sock_mode ((sock_type*)tcp, TCP_MODE_NONAGLE);
           tcp->locflags |= LF_NODELAY;
         }
         else        /* turn on Nagle */
         {
           sock_mode ((sock_type*)tcp, TCP_MODE_NAGLE);
           tcp->locflags &= ~LF_NODELAY;
         }
         break;

    case TCP_MAXSEG:
         MSS = *(long*)val;
         if (MSS < 1 || MSS > MAX_WINDOW)
         {
           SOCK_ERR (EINVAL);
           return (-1);
         }
         tcp->max_seg = *(int*)val;
         break;

    case TCP_NOPUSH:
         if (on)
              tcp->locflags |=  LF_NOPUSH;
         else tcp->locflags &= ~LF_NOPUSH;
         break;

    case TCP_NOOPT:
         if (on)
              tcp->locflags |=  LF_NOOPT;
         else tcp->locflags &= ~LF_NOOPT;
         break;

    default:
         SOCK_ERR (ENOPROTOOPT);
         return (-1);
  }
  ARGSUSED (len);
  return (0);
}

static int get_tcp_opt (tcp_Socket *tcp, int opt, void *val, int *len)
{
  switch (opt)
  {
    case TCP_NODELAY:
         if (tcp->sockmode & TCP_MODE_NONAGLE)
              *(int*)val = TCP_NODELAY;
         else *(int*)val = 0;
         *(size_t*)len = sizeof(int);
         break;

    case TCP_MAXSEG:
         *(int*)val    = tcp->max_seg;
         *(size_t*)len = sizeof(int);
         break;

    case TCP_NOPUSH:
         *(int*)val    = (tcp->locflags & LF_NOPUSH);
         *(size_t*)len = sizeof (tcp->locflags);
         break;

⌨️ 快捷键说明

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