示例#1
0
/*-
 * Convert a network address from binary to printable numeric format.
 * This API is copied from INRIA's IPv6 implementation, but it is a
 * bit bogus in two ways:
 *
 *      1) There is no value in passing both an address family and
 *         an address length; either one should imply the other,
 *         or we should be passing sockaddrs instead.
 *      2) There should by contrast be /added/ a length for the buffer
 *         that we pass in, so that programmers are spared the need to
 *         manually calculate (read: ``guess'') the maximum length.
 *
 * Flash: the API is also the same in the NRL implementation, and seems to
 * be some sort of standard, so we appear to be stuck with both the bad
 * naming and the poor choice of arguments.
 */
char *addr2ascii (int af, const void *addrp, int len, char *buf)
{
    static char staticbuf[64];    /* 64 for AF_LINK > 16 for AF_INET */

    if (!buf)
        buf = staticbuf;

    switch (af)
    {
    case AF_INET:
        if (len != sizeof (struct in_addr))
        {
            SOCK_ERR (ENAMETOOLONG);
            return (NULL);
        }
        strcpy (buf, inet_ntoa (*(const struct in_addr *) addrp));
        break;

    case AF_LINK:
        if (len != sizeof (struct sockaddr_dl))
        {
            SOCK_ERR (ENAMETOOLONG);
            return (NULL);
        }
        strcpy (buf, link_ntoa ((const struct sockaddr_dl *) addrp));
        break;

    default:
        SOCK_ERR (EPROTONOSUPPORT);
        return (NULL);
    }
    return (buf);
}
示例#2
0
/*
 * Raise SIGPIPE if signal defined.
 * Return -1 with errno = EPIPE.
 */
int _sock_sig_epipe (void)
{
#if defined(SIGPIPE)
    raise (SIGPIPE);
#endif
    SOCK_ERR (EPIPE);
    return (-1);
}
示例#3
0
int shutdown (int s, int how)
{
  Socket *socket = _socklist_find (s);

#if defined(USE_DEBUG)
  static char fmt[] = "\nshutdown:%d/??";

  if (how == SHUT_RD)
     strcpy (fmt+sizeof(fmt)-3, "r ");
  else if (how == SHUT_WR)
     strcpy (fmt+sizeof(fmt)-3, "w ");
  else if (how == SHUT_RDWR)
     strcpy (fmt+sizeof(fmt)-3, "rw");
#endif

  SOCK_PROLOGUE (socket, fmt, s);

#if 0
  /* if not connected, let close_s() do it
   */
  if (!(socket->so_state & SS_ISCONNECTED))
     return close_s (s);
#endif

  switch (how)
  {
    case SHUT_RD:
         socket->so_state   |=  SS_CANTRCVMORE;
         socket->so_options &= ~SO_ACCEPTCONN;
         return (0);

    case SHUT_WR:
         socket->so_state   |=  SS_CANTSENDMORE;
         socket->so_state   &= ~SS_ISLISTENING;
         socket->so_options &= ~SO_ACCEPTCONN;
         return (0);
      // return close_s (s);

    case SHUT_RDWR:
         socket->so_state |=  SS_CANTRCVMORE;
         socket->so_state |=  SS_CANTSENDMORE;
         socket->so_state &= ~SS_ISLISTENING;
         return close_s (s);
  }
  SOCK_ERR (EINVAL);
  return (-1);
}
示例#4
0
int writev_s (int s, const struct iovec *vector, size_t count)
{
  char   *buffer, *bp;
  size_t  i, to_copy, bytes = 0;

  /* Find the total number of bytes to write
   */
  for (i = 0; i < count; i++)
      bytes += vector[i].iov_len;

  if (bytes == 0)
     return (0);

  /* Allocate a temporary buffer to hold the data
   */
  buffer = alloca (bytes);
  if (!buffer)
  {
    SOCK_ERR (ENOMEM);
    return (-1);
  }

  to_copy = bytes;
  bp = buffer;

  /* Copy the data into buffer.
   */
  for (i = 0; i < count; ++i)
  {
    size_t copy = min (vector[i].iov_len, to_copy);

    memcpy (bp, vector[i].iov_base, copy);
    bp      += copy;
    to_copy -= copy;
    if (to_copy == 0)
       break;
  }
  return transmit ("writev_s", s, (const void*)buffer, bytes, 0, NULL, 0);
}
示例#5
0
/*
 *  Allocate and fill local/remote addresses for 'clone'.
 *  Take local address from 'socket', and remote address from
 *  TCB of clone.
 */
static int alloc_addr (Socket *socket, Socket *clone)
{
  struct in_addr peer;

  clone->local_addr  = SOCK_CALLOC (sizeof(*clone->local_addr));
  clone->remote_addr = SOCK_CALLOC (sizeof(*clone->remote_addr));

  if (!clone->local_addr || !clone->remote_addr)
  {
    SOCK_DEBUGF ((socket, ", ENOMEM"));
    SOCK_ERR (ENOMEM);
    return (-1);
  }

  peer.s_addr = htonl (clone->tcp_sock->hisaddr);
  clone->local_addr->sin_family  = AF_INET;
  clone->local_addr->sin_port    = socket->local_addr->sin_port;
  clone->local_addr->sin_addr    = socket->local_addr->sin_addr;

  clone->remote_addr->sin_family = AF_INET;
  clone->remote_addr->sin_port   = htons (clone->tcp_sock->hisport);
  clone->remote_addr->sin_addr   = peer;
  return (0);
}
示例#6
0
/*
 *  Raw IP transmitter
 */
static int ip_transmit (Socket *socket, const void *tx, int len)
{
  eth_address eth;
  u_long      dest;
  unsigned    tx_len, tx_room;
  sock_type  *sk = (sock_type*)socket->udp_sock;

  struct ip   *ip  = (struct ip*) tx;
  const  BYTE *buf = (const BYTE*) tx;
  WORD   flags     = 0;
  DWORD  offset;
  UINT   h_len, o_len;

  tcp_tick (NULL);        /* process other TCBs too */
  tcp_Retransmitter (1);

  /* This should never happen
   */
  if (ip && (socket->so_state & SS_NBIO) &&
      sock_tbleft(sk) < (len + socket->send_lowat))
  {
    SOCK_DEBUGF ((socket, ", EWOULDBLOCK"));
    SOCK_ERR (EWOULDBLOCK);
    return (-1);
  }

  if (ip)
  {
    offset = ntohs (ip->ip_off);
    flags  = offset & ~IP_OFFMASK;
    offset = (offset & IP_OFFMASK) << 3; /* 0 <= ip_ofs <= 65536-8 */
  }

  SOCK_DEBUGF ((socket, ", %s / Raw",
                inet_ntoa(socket->remote_addr->sin_addr)));

  if (ip && (socket->inp_flags & INP_HDRINCL))
  {
    dest    = ip->ip_dst.s_addr;
    tx_len  = len;
    tx_room = mtu;
  }
  else
  {
    dest    = socket->remote_addr->sin_addr.s_addr;
    tx_len  = len + sizeof (*ip);
    tx_room = mtu + sizeof (*ip);
  }

  if (!dest || !_arp_resolve(ntohl(dest),&eth,0))
  {
    SOCK_DEBUGF ((socket, ", no route"));
    SOCK_ERR (EHOSTUNREACH);
    STAT (ipstats.ips_noroute++);
    return (-1);
  }

#if defined(USE_FRAGMENTS)
  if (!(socket->inp_flags & INP_HDRINCL) &&
      tx_len + socket->ip_opt_len > tx_room)
  {
    sk = (sock_type*)socket->raw_sock;

    if (flags & IP_DF)
    {
      SOCK_DEBUGF ((socket, ", EMSGSIZE"));
      SOCK_ERR (EMSGSIZE);
      STAT (ipstats.ips_toolong++);
      return (-1);
    }
    return SEND_IP_FRAGMENTS (sk, sk->raw.ip_type, dest, buf, len);
  }
#else
  if (!(socket->inp_flags & INP_HDRINCL) &&
      tx_len + socket->ip_opt_len > tx_room)
  {
    SOCK_DEBUGF ((socket, ", EMSGSIZE"));
    SOCK_ERR (EMSGSIZE);
    STAT (ipstats.ips_toolong++);
    return (-1);
  }
#endif

  ip = (struct ip*) _eth_formatpacket (&eth, IP_TYPE);

  if (socket->inp_flags & INP_HDRINCL)
  {
    memcpy (ip, buf, len);
    if (ip->ip_src.s_addr == 0)
    {
      ip->ip_src.s_addr = gethostid();
      ip->ip_sum = 0;
      ip->ip_sum = ~checksum ((void*)ip, ip->ip_hl << 2);
    }
    if (ip->ip_sum == 0)
        ip->ip_sum = ~checksum ((void*)ip, ip->ip_hl << 2);
  }
  else
  {
    if (socket->ip_opt && socket->ip_opt_len > 0)
    {
      BYTE *data;

      o_len = min (socket->ip_opt_len, sizeof(socket->ip_opt->ip_opts));
      h_len = sizeof(*ip) + o_len;
      data  = (BYTE*)ip + h_len;
      memcpy (ip+1, &socket->ip_opt->ip_opts, o_len);
      memcpy (data, buf, len);
      tx_len += o_len;
      if (socket->ip_opt->ip_dst.s_addr)   /* using source routing */
         dest = socket->ip_opt->ip_dst.s_addr;
    }
    else
    {
      if (buf)
         memcpy (ip+1, buf, len);
      h_len = sizeof (*ip);
    }

    ip->ip_v   = IPVERSION;
    ip->ip_hl  = h_len >> 2;
    ip->ip_tos = socket->ip_tos;
    ip->ip_len = htons (tx_len);
    ip->ip_id  = _get_ip_id();
    ip->ip_off = 0;
    ip->ip_ttl = socket->ip_ttl;
    ip->ip_p   = socket->so_proto;

    ip->ip_src.s_addr = gethostid();
    ip->ip_dst.s_addr = dest;

    ip->ip_sum = 0;
    ip->ip_sum = ~checksum (ip, h_len);
  }

  DEBUG_TX (NULL, ip);

  if (!_eth_send(tx_len))
  {
    SOCK_DEBUGF ((socket, ", ENETDOWN"));
    SOCK_ERR (ENETDOWN);
    return (-1);
  }
  if (buf)
     buf += tx_len;

  return (len);
}
示例#7
0
/*
 *  UDP transmitter
 */
static int udp_transmit (Socket *socket, const void *buf, int len)
{
  sock_type *sk   = (sock_type*) socket->udp_sock;
  u_long     dest = socket->remote_addr->sin_addr.s_addr;
  int        tx_room, rc;
  int        is_bcast, is_multi;

  if (!tcp_tick(sk))
  {
    socket->so_state |= SS_CANTSENDMORE;
    SOCK_DEBUGF ((socket, ", ENOTCONN (can't send)")); /* !! or EPIPE */
    SOCK_ERR (ENOTCONN);
    return (-1);
  }
  tcp_Retransmitter (1);

  if ((socket->so_state & SS_NBIO) && check_non_block_tx(socket,&len) < 0)
  {
    SOCK_DEBUGF ((socket, ", EWOULDBLOCK"));
    SOCK_ERR (EWOULDBLOCK);
    return (-1);
  }

  is_bcast = (dest == INADDR_BROADCAST || dest == INADDR_ANY);
  is_multi = IN_MULTICAST (ntohl(socket->remote_addr->sin_addr.s_addr));

  SOCK_DEBUGF ((socket, ", %s (%d) / UDP %s",
                inet_ntoa(socket->remote_addr->sin_addr),
                ntohs(socket->remote_addr->sin_port),
                is_multi ? "(mc)" : ""));

  if (len == 0)   /* 0-byte probe packet */
     return ip_transmit (socket, NULL, 0);

  tx_room = sock_tbleft (sk);  /* always MTU-28 */

  /* Special tests for broadcast messages
   */
  if (is_bcast)
  {
    if (len > tx_room)        /* don't allow fragments */
    {
      SOCK_DEBUGF ((socket, ", EMSGSIZE"));
      SOCK_ERR (EMSGSIZE);
      STAT (ipstats.ips_odropped++);
      return (-1);
    }
    if (_pktserial)           /* Link-layer doesn't allow broadcast */
    {
      SOCK_DEBUGF ((socket, ", EADDRNOTAVAIL"));
      SOCK_ERR (EADDRNOTAVAIL);
      STAT (ipstats.ips_odropped++);
      return (-1);
    }
  }

  /* set new TTL if setsockopt() used before sending to Class-D socket
   */
  if (is_multi)
     sk->udp.ttl = socket->ip_ttl;

#if defined(USE_FRAGMENTS)
  if ((long)len > USHRT_MAX - sizeof(udp_Header))
  {
    SOCK_DEBUGF ((socket, ", EMSGSIZE"));
    SOCK_ERR (EMSGSIZE);
    STAT (ipstats.ips_toolong++);
    return (-1);
  }

  if (len > tx_room)
     return SEND_IP_FRAGMENTS (sk, UDP_PROTO, dest, buf, len);
#endif

  rc = sock_write (sk, (BYTE*)buf, len);
  if (rc <= 0)    /* error in udp_write() */
  {
    SOCK_DEBUGF ((socket, ", ENETDOWN"));
    SOCK_ERR (ENETDOWN);
    return (-1);
  }
  return (rc);
}
示例#8
0
/*
 *  TCP transmitter
 */
static int tcp_transmit (Socket *socket, const void *buf, int len, int flags)
{
  sock_type *sk = (sock_type*)socket->tcp_sock;

  tcp_tick (sk);
  tcp_Retransmitter (1);

  if (sk->tcp.state < tcp_StateESTAB || sk->tcp.state >= tcp_StateLASTACK)
  {
    socket->so_state |= SS_CANTSENDMORE;
    SOCK_DEBUGF ((socket, ", ENOTCONN (%s)",  /* !! or EPIPE */
                 (sk->tcp.locflags & LF_GOT_FIN) ?
                   "got FIN" : "can't send"));
    SOCK_ERR (ENOTCONN);
    return (-1);
  }

  if (socket->so_state & SS_NBIO)
  {
    int in_len = len;

    if (check_non_block_tx(socket,&len) < 0)
    {
      SOCK_DEBUGF ((socket, ", EWOULDBLOCK"));
      SOCK_ERR (EWOULDBLOCK);
      return (-1);
    }
    if (in_len != len)
       SOCK_DEBUGF ((socket, " [%d]", len)); /* trace "len=x [y]" */
  }

  SOCK_DEBUGF ((socket, ", %s (%d) / TCP",
                inet_ntoa(socket->remote_addr->sin_addr),
                ntohs(socket->remote_addr->sin_port)));

#if 0
  /* Must wait for room in send buffer
   */
  if ((flags & MSG_WAITALL) || len > sock_tbleft(sk))
       len = sock_write (sk, (BYTE*)buf, len);
  else len = sock_fastwrite (sk, (BYTE*)buf, len);

#else
  /* This is more efficient. The above sock_fastwrite() would
   * effectively turn off Nagle's algorithm.
   */
  ARGSUSED (flags);
  len = sock_write (sk, (BYTE*)buf, len);
#endif

  if (len <= 0)    /* error in tcp_write() */
  {
    if (sk->tcp.state != tcp_StateESTAB)
    {
      SOCK_DEBUGF ((socket, ", ENOTCONN"));
      SOCK_ERR (ENOTCONN);   /* maybe EPIPE? */
    }
    else
    {
      SOCK_DEBUGF ((socket, ", ENETDOWN"));
      SOCK_ERR (ENETDOWN);
    }
    return (-1);
  }
  return (len);
}
示例#9
0
/*
 * transmit() flags:
 *   MSG_DONTROUTE                                     (not supported)
 *   MSG_EOR       Close sending side after data sent
 *   MSG_TRUNC                                         (not supported)
 *   MSG_CTRUNC                                        (not supported)
 *   MSG_OOB                                           (not supported)
 *   MSG_WAITALL   Wait till room in tx-buffer         (not supported)
 */
static int transmit (const char *func, int s, const void *buf, int len,
                     int flags, const struct sockaddr *to, int tolen)
{
  Socket *socket = _socklist_find (s);
  int     rc;

  SOCK_DEBUGF ((socket, "\n%s:%d, len=%d", func, s, len));

  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);
  }

  if (socket->so_type == SOCK_STREAM ||      /* TCP-socket or */
      (socket->so_state & SS_ISCONNECTED))   /* "connected" udp/raw */
  {
    /* Note: SOCK_RAW doesn't really need a local address/port, but
     * makes the code more similar for all socket-types.
     * Disadvantage is that SOCK_RAW ties up a local port and a bit
     * more memory.
     */

    if (!socket->local_addr)
    {
      SOCK_DEBUGF ((socket, ", no local_addr"));
      SOCK_ERR (ENOTCONN);
      return (-1);
    }

    if (!socket->remote_addr)
    {
      SOCK_DEBUGF ((socket, ", no remote_addr"));
      SOCK_ERR (ENOTCONN);
      return (-1);
    }

    if (socket->so_state & SS_CONN_REFUSED)
    {
      if (socket->so_error == ECONNRESET)  /* set in tcp_sockreset() */
      {
        SOCK_DEBUGF ((socket, ", ECONNRESET"));
        SOCK_ERR (ECONNRESET);
      }
      else
      {
        SOCK_DEBUGF ((socket, ", ECONNREFUSED"));
        SOCK_ERR (ECONNREFUSED);
      }
      return (-1);
    }
  }

  /* connectionless protocol setup
   */
  if (socket->so_type == SOCK_DGRAM || socket->so_type == SOCK_RAW)
  {
    if (!to || tolen < sizeof(*to))
    {
      SOCK_DEBUGF ((socket, ", no to-addr"));
      SOCK_ERR (EINVAL);
      return (-1);
    }
    if (setup_udp_raw(socket,to,tolen) < 0)
       return (-1);
  }

  VERIFY_RW (buf, len);


  /* Setup SIGINT handler now.
   */
  if (_sock_sig_setup() < 0)
  {
    SOCK_ERR (EINTR);
    return (-1);
  }

  switch (socket->so_type)
  {
    case SOCK_DGRAM:
         rc = udp_transmit (socket, buf, len);
         break;

    case SOCK_STREAM:
         rc = tcp_transmit (socket, buf, len, flags);
         break;

    case SOCK_RAW:
         rc = ip_transmit (socket, buf, len);
         break;

    default:
         SOCK_DEBUGF ((socket, ", EPROTONOSUPPORT"));
         SOCK_ERR (EPROTONOSUPPORT);
         rc = -1;
  }

  _sock_sig_restore();

  if (rc >= 0 && (flags & MSG_EOR))
     msg_eor_close (socket);

  return (rc);
}
示例#10
0
int accept (int s, struct sockaddr *addr, int *addrlen)
{
  Socket  *clone, *socket;
  volatile DWORD   timeout;
  volatile int     newsock = -1;
  volatile int     que_idx;
  volatile int     maxconn;

  socket = _socklist_find (s);

  SOCK_PROLOGUE (socket, "\naccept:%d", s);

  if (!socket->local_addr)
  {
    SOCK_DEBUGF ((socket, ", not bound"));
    SOCK_ERR (ENOTCONN);
    return (-1);
  }

  if (socket->so_type != SOCK_STREAM)
  {
    SOCK_DEBUGF ((socket, ", EOPNOTSUPP"));
    SOCK_ERR (EOPNOTSUPP);
    return (-1);
  }

  if (!(socket->so_options & SO_ACCEPTCONN)) /* listen() not called */
  {
    SOCK_DEBUGF ((socket, ", not SO_ACCEPTCONN"));
    SOCK_ERR (EINVAL);
    return (-1);
  }

  if (!(socket->so_state & (SS_ISLISTENING | SS_ISCONNECTING)))
  {
    SOCK_DEBUGF ((socket, ", not listening"));
    SOCK_ERR (ENOTCONN);
    return (-1);
  }

  if (addr && addrlen)
  {
    if (*addrlen < sizeof(*addr))
    {
      SOCK_DEBUGF ((socket, ", EFAULT"));
      SOCK_ERR (EFAULT);
      return (-1);
    }
    VERIFY_RW (addr, *addrlen);
  }

  /* Get max possible TCBs on listen-queue.
   * Some (or all) may be NULL until a SYN comes in.
   */
  maxconn = socket->backlog;
  if (maxconn < 1 || maxconn > SOMAXCONN)
  {
    SOCK_FATAL (("%s(%d): Illegal socket backlog %d",
                __FILE__, __LINE__, maxconn));
    SOCK_ERR (EINVAL);
    return (-1);
  }

  if (socket->timeout)
       timeout = set_timeout (1000 * socket->timeout);
  else timeout = 0UL;


  if (_sock_sig_setup() < 0)
  {
    SOCK_ERR (EINTR);
    goto accept_fail;
  }

  /* Loop over all queue-slots and accept first connected TCB
   */
  for (que_idx = 0; ; que_idx = (++que_idx % maxconn))
  {
    tcp_Socket *sk = socket->listen_queue [que_idx];

    tcp_tick (NULL);

    SOCK_YIELD();

    /* No SYNs received yet. This shouldn't happen if we called 'accept()'
     * after 'select_s()' said that socket was readable. (At least one
     * connection on the listen-queue).
     */
    if (sk)
    {
      /* This could happen if 'accept()' was called too long after connection
       * was established and then closed by peer. This could also happen if
       * someone did a portscan on us. I.e. he sent 'SYN', we replied with
       * 'SYN+ACK' and he never sent an 'ACK'. Thus we timeout in
       * 'tcp_Retransmitter()' and abort the TCB.
       *
       * Queue slot is in any case ready for another 'SYN' to come and be
       * handled by '_sock_append()'.
       */
      if (sk->state >= tcp_StateLASTACK && sk->ip_type == 0)
      {
        SOCK_DEBUGF ((socket, ", aborted TCB (idx %d)", que_idx));
        listen_free (socket, que_idx);
        continue;
      }

      /* !!to-do: Should maybe loop over all maxconn TCBs and accept the
       *          one with oldest 'syn_timestamp'.
       */
      if (tcp_established(sk))
      {
        SOCK_DEBUGF ((socket, ", connected! (idx %d)", que_idx));
        break;
      }
    }

    /* We've polled all listen-queue slots and none are connected.
     * Return fail if socket is non-blocking.
     */
    if (que_idx == maxconn-1 && (socket->so_state & SS_NBIO))
    {
      SOCK_DEBUGF ((socket, ", would block"));
      SOCK_ERR (EWOULDBLOCK);
      goto accept_fail;
    }

    if (chk_timeout(timeout))
    {
      SOCK_DEBUGF ((socket, ", ETIMEDOUT"));
      SOCK_ERR (ETIMEDOUT);
      goto accept_fail;
    }
  }

  /* We're here only when above 'tcp_established()' succeeded.
   * Now duplicate 'socket' into a new listening socket 'clone'
   * with handle 'newsock'.
   */
  _sock_enter_scope();
  newsock = dup_bind (socket, &clone, que_idx);
  if (newsock < 0)
     goto accept_fail;

  if (alloc_addr(socket, clone) < 0)
  {
    SOCK_DEL_FD (newsock);
    goto accept_fail;
  }

  /* Clone is connected, but *not* listening/accepting.
   * Note: other 'so_state' bits from parent is unchanged.
   *       e.g. clone may be non-blocking.
   */
  clone->so_state   |=  SS_ISCONNECTED;
  clone->so_state   &= ~(SS_ISLISTENING | SS_ISCONNECTING);
  clone->so_options &= ~SO_ACCEPTCONN;

  /* Prevent a PUSH on first segment sent.
   */
  sock_noflush ((sock_type*)clone->tcp_sock);

  SOCK_DEBUGF ((clone, "\nremote %s (%d)",
                inet_ntoa (clone->remote_addr->sin_addr),
                ntohs (clone->remote_addr->sin_port)));

  if (addr && addrlen)
  {
    struct sockaddr_in *sa = (struct sockaddr_in*)addr;

    sa->sin_family = AF_INET;
    sa->sin_port   = clone->remote_addr->sin_port;
    sa->sin_addr   = clone->remote_addr->sin_addr;
    memset (sa->sin_zero, 0, sizeof(sa->sin_zero));
    *addrlen = sizeof(*sa);
  }

  _sock_leave_scope();
  _sock_sig_restore();
  return (newsock);

accept_fail:
  _sock_leave_scope();
  _sock_sig_restore();
  return (-1);
}