Exemple #1
0
/*
 *  Duplicate a SOCK_STREAM 'socket' to 'newconn'. Doesn't set
 *  local/remote addresses. Transfer TCB from listen-queue[idx] of
 *  'socket' to TCB of 'clone'.
 */
static int dup_bind (Socket *sock, Socket **newconn, int idx)
{
  int fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);

  if (fd >= 0)
  {
    Socket *clone = _socklist_find (fd);

    /* child gets state from parent
     */
    clone->timeout    = sock->timeout;
    clone->close_time = sock->close_time;
    clone->keepalive  = sock->keepalive;
    clone->ip_tos     = sock->ip_tos;
    clone->ip_ttl     = sock->ip_ttl;
    clone->so_state   = sock->so_state;
    clone->so_options = sock->so_options;

    /* TCB for clone is from listen-queue[idx]; free tcp_sock from
     * socket(). Reuse listen-queue slot for another SYN.
     */
    free (clone->tcp_sock);
    clone->tcp_sock = sock->listen_queue[idx];
    sock->listen_queue [idx] = NULL;
    sock->syn_timestamp[idx] = 0;
    *newconn = clone;
  }
  return (fd);
}
Exemple #2
0
int sock_fputs (const char *text, FILE *stream)
{
  int     s    = fileno (stream);
  Socket *sock = _socklist_find (s);

  SOCK_PROLOGUE (sock, "\nsock_fputs:%d", s);
  return write_s (s, text, strlen(text));
}
Exemple #3
0
int W32_CALL shutdown (int s, int how)
{
  Socket *socket = _socklist_find (s);

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

  how &= 3;
  fmt [sizeof(fmt)-3] = rrw [2*how];
  fmt [sizeof(fmt)-2] = rrw [2*how+1];
#endif

  SOCK_PROLOGUE (socket, fmt, s);

  switch (how)
  {
    case SHUT_RD:
         socket->so_error    = ESHUTDOWN;
         socket->so_state   |=  SS_CANTRCVMORE;
      /* socket->so_options &= ~SO_ACCEPTCONN; */

         /** \todo For tcp, should send RST if we get
          *        incoming data. Don't send ICMP error.
          */
         return (0);

    case SHUT_WR:
         socket->so_error    = ESHUTDOWN;
         socket->so_state   |=  SS_CANTSENDMORE;
         socket->so_state   &= ~SS_ISLISTENING;
      /* socket->so_options &= ~SO_ACCEPTCONN; */

         /** \todo For tcp, should send FIN when all Tx data
          *        has been ack'ed.
          * close_s(s) should be same as shutdown(s,SHUT_WR)
          */
         return (0);

    case SHUT_RDWR:
         socket->so_error    = ESHUTDOWN;
         socket->so_state   |= (SS_CANTRCVMORE | SS_CANTSENDMORE);
         socket->so_state   &= ~SS_ISLISTENING;
      /* socket->so_options &= ~SO_ACCEPTCONN; */
         return (0);
  }

  SOCK_ERRNO (EINVAL);
  return (-1);
}
Exemple #4
0
int W32_CALL getsockname (int s, struct sockaddr *name, int *namelen)
{
  Socket *socket = _socklist_find (s);
  int     sa_len;

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

  sa_len = (socket->so_family == AF_INET6) ? sizeof(struct sockaddr_in6) :
                                             sizeof(struct sockaddr_in);

  if (!name || !namelen || (*namelen < sa_len))
  {
    SOCK_DEBUGF ((", EINVAL"));
    SOCK_ERRNO (EINVAL);
    if (namelen)
       *namelen = 0;
    return (-1);
  }

  if (!socket->local_addr)
  {
    SOCK_DEBUGF ((", EINVAL"));
    SOCK_ERRNO (EINVAL);    /* according to HP/UX manpage */
    return (-1);
  }

  VERIFY_RW (name, *namelen);

  *namelen = sa_len;
  memcpy (name, socket->local_addr, sa_len);

#if defined(USE_IPV6)
  if (socket->so_family == AF_INET6)
  {
    const struct sockaddr_in6 *la = (const struct sockaddr_in6*)socket->local_addr;

    SOCK_DEBUGF ((", %s (%d)", _inet6_ntoa(&la->sin6_addr), ntohs(la->sin6_port)));
    ARGSUSED (la);
  }
  else
#endif
  {
    const struct sockaddr_in *la = socket->local_addr;

    SOCK_DEBUGF ((", %s (%d)", inet_ntoa(la->sin_addr), ntohs(la->sin_port)));
    ARGSUSED (la);
  }
  return (0);
}
Exemple #5
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);
}
Exemple #6
0
int W32_CALL getpeername (int s, struct sockaddr *name, int *namelen)
{
  Socket *socket = _socklist_find (s);
  int     sa_len;

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

  sa_len = (socket->so_family == AF_INET6) ? sizeof(struct sockaddr_in6) :
                                             sizeof(struct sockaddr_in);

  if (!name || !namelen || (*namelen < sa_len))
  {
    SOCK_DEBUGF ((", EINVAL"));
    SOCK_ERRNO (EINVAL);
    if (namelen)
       *namelen = 0;
    return (-1);
  }

  if (!(socket->so_state & SS_ISCONNECTED))
  {
    SOCK_DEBUGF ((", ENOTCONN"));
    SOCK_ERRNO (ENOTCONN);
    return (-1);
  }
  VERIFY_RW (name, *namelen);

  *namelen = sa_len;
  memcpy (name, socket->remote_addr, *namelen);

#if defined(USE_IPV6)
  if (socket->so_family == AF_INET6)
  {
    const struct sockaddr_in6 *ra = (const struct sockaddr_in6*)socket->remote_addr;
    SOCK_DEBUGF ((", %s (%d)", _inet6_ntoa(&ra->sin6_addr), ntohs(ra->sin6_port)));
    ARGSUSED (ra);
  }
  else
#endif
  {
    const struct sockaddr_in *ra = socket->remote_addr;
    SOCK_DEBUGF ((", %s (%d)", inet_ntoa(ra->sin_addr), ntohs(ra->sin_port)));
    ARGSUSED (ra);
  }
  return (0);
}
Exemple #7
0
int sock_fgets (char *buf, int max, FILE *stream)
{
  int        rc, s = fileno (stream);
  Socket    *sock  = _socklist_find (s);
  sock_type *sk    = NULL;

  SOCK_PROLOGUE (sock, "\nsock_fgets:%d", s);

  if (sock->tcp_sock)
     sk = (sock_type*) sock->tcp_sock;
  else if (sock->udp_sock)
     sk = (sock_type*) sock->udp_sock;

  rc = sk ? sock_gets (sk, (BYTE*)buf, max) : -1;

  SOCK_DEBUGF ((", rc %d", rc));
  return (rc);
}
Exemple #8
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);
}
Exemple #9
0
/*
 * connect()
 *  "connect" will attempt to open a connection on a foreign IP address and
 *  foreign port address.  This is achieved by specifying the foreign IP
 *  address and foreign port number in the "servaddr".
 */
int W32_CALL connect (int s, const struct sockaddr *servaddr, int addrlen)
{
  struct   sockaddr_in  *addr   = (struct sockaddr_in*) servaddr;
  struct   sockaddr_in6 *addr6  = (struct sockaddr_in6*) servaddr;
  struct   Socket       *socket = _socklist_find (s);
  volatile int rc, sa_len;
  BOOL     is_ip6;

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

  is_ip6 = (socket->so_family == AF_INET6);
  sa_len = is_ip6 ? sizeof(*addr6) : sizeof(*addr);

  if (_sock_chk_sockaddr(socket, servaddr, addrlen) < 0)
     return (-1);

  if (socket->so_type == SOCK_STREAM)
  {
    if (socket->so_state & SS_ISCONNECTED)
    {
      SOCK_DEBUGF ((", EISCONN"));
      SOCK_ERRNO (EISCONN);
      return (-1);
    }
    if (socket->so_options & SO_ACCEPTCONN)
    {
      SOCK_DEBUGF ((", EOPNOTSUPP (listen sock)"));
      SOCK_ERRNO (EOPNOTSUPP);
      return (-1);
    }
    if (!is_ip6 && IN_MULTICAST(ntohl(addr->sin_addr.s_addr)))
    {
      SOCK_DEBUGF ((", EINVAL (mcast)"));
      SOCK_ERRNO (EINVAL);
      return (-1);
    }
    else if (is_ip6 && IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr))
    {
      SOCK_DEBUGF ((", EINVAL (mcast)"));
      SOCK_ERRNO (EINVAL);
      return (-1);
    }
  }

  if (socket->remote_addr)
  {
    if ((socket->so_type == SOCK_STREAM) &&
        (socket->so_state & SS_NBIO))
       return nblk_connect (socket);

    SOCK_DEBUGF ((", connect already done!"));
    SOCK_ERRNO (EISCONN);
    return (-1);
  }

  socket->remote_addr = (struct sockaddr_in*) SOCK_CALLOC (sa_len);
  if (!socket->remote_addr)
  {
    SOCK_DEBUGF ((", ENOMEM (rem)"));
    SOCK_ERRNO (ENOMEM);
    return (-1);
  }

#if defined(USE_IPV6)
  if (is_ip6)
  {
    struct sockaddr_in6 *ra = (struct sockaddr_in6*)socket->remote_addr;

    ra->sin6_family = AF_INET6;
    ra->sin6_port   = addr6->sin6_port;
    memcpy (&ra->sin6_addr, &addr6->sin6_addr, sizeof(ra->sin6_addr));
  }
  else
#endif
  {
    socket->remote_addr->sin_family = AF_INET;
    socket->remote_addr->sin_port   = addr->sin_port;
    socket->remote_addr->sin_addr   = addr->sin_addr;
  }

  if (!socket->local_addr)
  {
    SOCK_DEBUGF ((", auto-binding"));

    socket->local_addr = (struct sockaddr_in*) SOCK_CALLOC (sa_len);
    if (!socket->local_addr)
    {
      free (socket->remote_addr);
      socket->remote_addr = NULL;
      SOCK_DEBUGF ((", ENOMEM (loc)"));
      SOCK_ERRNO (ENOMEM);
      return (-1);
    }

#if defined(USE_IPV6)
    if (is_ip6)
    {
      struct sockaddr_in6 *la = (struct sockaddr_in6*)socket->local_addr;

      la->sin6_family = AF_INET6;
      la->sin6_port   = htons (find_free_port(0,TRUE));
      memcpy (&la->sin6_addr, &in6addr_my_ip, sizeof(la->sin6_addr));
    }
    else
#endif
    {
      socket->local_addr->sin_family      = AF_INET;
      socket->local_addr->sin_port        = htons (find_free_port(0,TRUE));
      socket->local_addr->sin_addr.s_addr = htonl (my_ip_addr);
    }
  }

  SOCK_DEBUGF ((", src/dest ports: %u/%u",
                ntohs(socket->local_addr->sin_port),
                ntohs(socket->remote_addr->sin_port)));


  /* Not safe to run sock_daemon() now
   */
  _sock_crit_start();

  /* Setup SIGINT handler now.
   */
  if (_sock_sig_setup() < 0)
  {
    SOCK_ERRNO (EINTR);
    SOCK_DEBUGF ((", EINTR"));
    _sock_crit_stop();
    return (-1);
  }

  switch (socket->so_type)
  {
    case SOCK_STREAM:
         rc = tcp_connect (socket);
         break;

    case SOCK_DGRAM:
         rc = udp_connect (socket);
         break;

    case SOCK_RAW:
         rc = raw_connect (socket);
         break;

    default:
         SOCK_ERRNO (EPROTONOSUPPORT);
         rc = -1;
         break;
  }

  _sock_sig_restore();
  _sock_crit_stop();
  return (rc);
}
Exemple #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);
}