text: /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to
   * deal in the Software without restriction, including without limitation the
   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   * sell copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   * IN THE SOFTWARE.
   */
  
  #include "uv.h"
  #include "internal.h"
  
  #include <stdlib.h>
  #include <unistd.h>
  #include <assert.h>
  #include <errno.h>
  
  
  static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
    struct sockaddr_storage saddr;
    socklen_t slen;
    int sockfd;
    int err;
  
    err = uv__socket(domain, SOCK_STREAM, 0);
    if (err < 0)
      return err;
    sockfd = err;
  
    err = uv__stream_open((uv_stream_t*) handle, sockfd, flags);
    if (err) {
      uv__close(sockfd);
      return err;
    }
  
    if (flags & UV_HANDLE_BOUND) {
      /* Bind this new socket to an arbitrary port */
      slen = sizeof(saddr);
      memset(&saddr, 0, sizeof(saddr));
      if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) {
        uv__close(sockfd);
        return UV__ERR(errno);
      }
  
      if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) {
        uv__close(sockfd);
        return UV__ERR(errno);
      }
    }
  
    return 0;
  }
  
  
  static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
    struct sockaddr_storage saddr;
    socklen_t slen;
  
    if (domain == AF_UNSPEC) {
      handle->flags |= flags;
      return 0;
    }
  
    if (uv__stream_fd(handle) != -1) {
  
      if (flags & UV_HANDLE_BOUND) {
  
        if (handle->flags & UV_HANDLE_BOUND) {
          /* It is already bound to a port. */
          handle->flags |= flags;
          return 0;
        }
  
        /* Query to see if tcp socket is bound. */
        slen = sizeof(saddr);
        memset(&saddr, 0, sizeof(saddr));
        if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen))
          return UV__ERR(errno);
  
        if ((saddr.ss_family == AF_INET6 &&
            ((struct sockaddr_in6*) &saddr)->sin6_port != 0) ||
            (saddr.ss_family == AF_INET &&
            ((struct sockaddr_in*) &saddr)->sin_port != 0)) {
          /* Handle is already bound to a port. */
          handle->flags |= flags;
          return 0;
        }
  
        /* Bind to arbitrary port */
        if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen))
          return UV__ERR(errno);
      }
  
      handle->flags |= flags;
      return 0;
    }
  
    return new_socket(handle, domain, flags);
  }
  
  
  int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) {
    int domain;
  
    /* Use the lower 8 bits for the domain */
    domain = flags & 0xFF;
    if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC)
      return UV_EINVAL;
  
    if (flags & ~0xFF)
      return UV_EINVAL;
  
    uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP);
  
    /* If anything fails beyond this point we need to remove the handle from
     * the handle queue, since it was added by uv__handle_init in uv_stream_init.
     */
  
    if (domain != AF_UNSPEC) {
      int err = maybe_new_socket(tcp, domain, 0);
      if (err) {
        QUEUE_REMOVE(&tcp->handle_queue);
        return err;
      }
    }
  
    return 0;
  }
  
  
  int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) {
    return uv_tcp_init_ex(loop, tcp, AF_UNSPEC);
  }
  
  
  int uv__tcp_bind(uv_tcp_t* tcp,
                   const struct sockaddr* addr,
                   unsigned int addrlen,
                   unsigned int flags) {
    int err;
    int on;
  
    /* Cannot set IPv6-only mode on non-IPv6 socket. */
    if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6)
      return UV_EINVAL;
  
    err = maybe_new_socket(tcp, addr->sa_family, 0);
    if (err)
      return err;
  
    on = 1;
    if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
      return UV__ERR(errno);
  
  #ifndef __OpenBSD__
  #ifdef IPV6_V6ONLY
    if (addr->sa_family == AF_INET6) {
      on = (flags & UV_TCP_IPV6ONLY) != 0;
      if (setsockopt(tcp->io_watcher.fd,
                     IPPROTO_IPV6,
                     IPV6_V6ONLY,
                     &on,
                     sizeof on) == -1) {
  #if defined(__MVS__)
        if (errno == EOPNOTSUPP)
          return UV_EINVAL;
  #endif
        return UV__ERR(errno);
      }
    }
  #endif
  #endif
  
    errno = 0;
    if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) {
      if (errno == EAFNOSUPPORT)
        /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
         * socket created with AF_INET to an AF_INET6 address or vice versa. */
        return UV_EINVAL;
      return UV__ERR(errno);
    }
    tcp->delayed_error = UV__ERR(errno);
  
    tcp->flags |= UV_HANDLE_BOUND;
    if (addr->sa_family == AF_INET6)
      tcp->flags |= UV_HANDLE_IPV6;
  
    return 0;
  }
  
  
  int uv__tcp_connect(uv_connect_t* req,
                      uv_tcp_t* handle,
                      const struct sockaddr* addr,
                      unsigned int addrlen,
                      uv_connect_cb cb) {
    int err;
    int r;
  
    assert(handle->type == UV_TCP);
  
    if (handle->connect_req != NULL)
      return UV_EALREADY;  /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */
  
    if (handle->delayed_error != 0)
      goto out;
  
    err = maybe_new_socket(handle,
                           addr->sa_family,
                           UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
    if (err)
      return err;
  
    do {
      errno = 0;
      r = connect(uv__stream_fd(handle), addr, addrlen);
    } while (r == -1 && errno == EINTR);
  
    /* We not only check the return value, but also check the errno != 0.
     * Because in rare cases connect() will return -1 but the errno
     * is 0 (for example, on Android 4.3, OnePlus phone A0001_12_150227)
     * and actually the tcp three-way handshake is completed.
     */
    if (r == -1 && errno != 0) {
      if (errno == EINPROGRESS)
        ; /* not an error */
      else if (errno == ECONNREFUSED
  #if defined(__OpenBSD__)
        || errno == EINVAL
  #endif
        )
      /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the
       * next tick to report the error. Solaris and OpenBSD wants to report
       * immediately -- other unixes want to wait.
       */
        handle->delayed_error = UV__ERR(ECONNREFUSED);
      else
        return UV__ERR(errno);
    }
  
  out:
  
    uv__req_init(handle->loop, req, UV_CONNECT);
    req->cb = cb;
    req->handle = (uv_stream_t*) handle;
    QUEUE_INIT(&req->queue);
    handle->connect_req = req;
  
    uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
  
    if (handle->delayed_error)
      uv__io_feed(handle->loop, &handle->io_watcher);
  
    return 0;
  }
  
  
  int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
    int err;
  
    if (uv__fd_exists(handle->loop, sock))
      return UV_EEXIST;
  
    err = uv__nonblock(sock, 1);
    if (err)
      return err;
  
    return uv__stream_open((uv_stream_t*)handle,
                           sock,
                           UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
  }
  
  
  int uv_tcp_getsockname(const uv_tcp_t* handle,
                         struct sockaddr* name,
                         int* namelen) {
  
    if (handle->delayed_error)
      return handle->delayed_error;
  
    return uv__getsockpeername((const uv_handle_t*) handle,
                               getsockname,
                               name,
                               namelen);
  }
  
  
  int uv_tcp_getpeername(const uv_tcp_t* handle,
                         struct sockaddr* name,
                         int* namelen) {
  
    if (handle->delayed_error)
      return handle->delayed_error;
  
    return uv__getsockpeername((const uv_handle_t*) handle,
                               getpeername,
                               name,
                               namelen);
  }
  
  
  int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
    int fd;
    struct linger l = { 1, 0 };
  
    /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */
    if (handle->flags & UV_HANDLE_SHUTTING)
      return UV_EINVAL;
  
    fd = uv__stream_fd(handle);
    if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)))
      return UV__ERR(errno);
  
    uv_close((uv_handle_t*) handle, close_cb);
    return 0;
  }
  
  
  int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
    static int single_accept_cached = -1;
    unsigned long flags;
    int single_accept;
    int err;
  
    if (tcp->delayed_error)
      return tcp->delayed_error;
  
    single_accept = uv__load_relaxed(&single_accept_cached);
    if (single_accept == -1) {
      const char* val = getenv("UV_TCP_SINGLE_ACCEPT");
      single_accept = (val != NULL && atoi(val) != 0);  /* Off by default. */
      uv__store_relaxed(&single_accept_cached, single_accept);
    }
  
    if (single_accept)
      tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
  
    flags = 0;
  #if defined(__MVS__)
    /* on zOS the listen call does not bind automatically
       if the socket is unbound. Hence the manual binding to
       an arbitrary port is required to be done manually
    */
    flags |= UV_HANDLE_BOUND;
  #endif
    err = maybe_new_socket(tcp, AF_INET, flags);
    if (err)
      return err;
  
    if (listen(tcp->io_watcher.fd, backlog))
      return UV__ERR(errno);
  
    tcp->connection_cb = cb;
    tcp->flags |= UV_HANDLE_BOUND;
  
    /* Start listening for connections. */
    tcp->io_watcher.cb = uv__server_io;
    uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);
  
    return 0;
  }
  
  
  int uv__tcp_nodelay(int fd, int on) {
    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)))
      return UV__ERR(errno);
    return 0;
  }
  
  
  int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
      return UV__ERR(errno);
  
  #ifdef TCP_KEEPIDLE
    if (on) {
      int intvl = 1;  /*  1 second; same as default on Win32 */
      int cnt = 10;  /* 10 retries; same as hardcoded on Win32 */
      if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
        return UV__ERR(errno);
      if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
        return UV__ERR(errno);
      if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
        return UV__ERR(errno);
    }
  #endif
  
    /* Solaris/SmartOS, if you don't support keep-alive,
     * then don't advertise it in your system headers...
     */
    /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */
  #if defined(TCP_KEEPALIVE) && !defined(__sun)
    if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
      return UV__ERR(errno);
  #endif
  
    return 0;
  }
  
  
  int uv_tcp_nodelay(uv_tcp_t* handle, int on) {
    int err;
  
    if (uv__stream_fd(handle) != -1) {
      err = uv__tcp_nodelay(uv__stream_fd(handle), on);
      if (err)
        return err;
    }
  
    if (on)
      handle->flags |= UV_HANDLE_TCP_NODELAY;
    else
      handle->flags &= ~UV_HANDLE_TCP_NODELAY;
  
    return 0;
  }
  
  
  int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) {
    int err;
  
    if (uv__stream_fd(handle) != -1) {
      err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay);
      if (err)
        return err;
    }
  
    if (on)
      handle->flags |= UV_HANDLE_TCP_KEEPALIVE;
    else
      handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE;
  
    /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge
     *      uv_tcp_t with an int that's almost never used...
     */
  
    return 0;
  }
  
  
  int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) {
    if (enable)
      handle->flags &= ~UV_HANDLE_TCP_SINGLE_ACCEPT;
    else
      handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;
    return 0;
  }
  
  
  void uv__tcp_close(uv_tcp_t* handle) {
    uv__stream_close((uv_stream_t*)handle);
  }
  