Пример #1
0
/* Associate a socket to a connection and and add to the manager. */
NS_INTERNAL void ns_set_sock(struct ns_connection *nc, sock_t sock) {
  ns_set_non_blocking_mode(sock);
  ns_set_close_on_exec(sock);
  nc->sock = sock;
  ns_add_conn(nc->mgr, nc);
  DBG(("%p %d", nc, sock));
}
Пример #2
0
/*
 * Create a connection, associate it with the given socket and event handler,
 * and add to the manager.
 *
 * See the `ns_add_sock_opts` structure for a description of the options.
 */
struct ns_connection *ns_add_sock_opt(struct ns_mgr *s, sock_t sock,
                                      ns_event_handler_t callback,
                                      struct ns_add_sock_opts opts) {
  struct ns_connection *conn;
  if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) {
    memset(conn, 0, sizeof(*conn));
    ns_set_non_blocking_mode(sock);
    ns_set_close_on_exec(sock);
    conn->sock = sock;
    conn->handler = callback;
    conn->mgr = s;
    conn->last_io_time = time(NULL);
    conn->flags = opts.flags;
    conn->user_data = opts.user_data;
    ns_add_conn(s, conn);
    DBG(("%p %d", conn, sock));
  }
  return conn;
}
Пример #3
0
/*
 * Schedules an async connect for a resolved address and proto.
 * Called from two places: `ns_connect_opt()` and from async resolver.
 * When called from the async resolver, it must trigger `NS_CONNECT` event
 * with a failure flag to indicate connection failure.
 */
NS_INTERNAL struct ns_connection *ns_finish_connect(struct ns_connection *nc,
                                                    int proto,
                                                    union socket_address *sa,
                                                    struct ns_add_sock_opts o) {
  sock_t sock = INVALID_SOCKET;
  int rc;

  DBG(("%p %s://%s:%hu", nc, proto == SOCK_DGRAM ? "udp" : "tcp",
       inet_ntoa(nc->sa.sin.sin_addr), ntohs(nc->sa.sin.sin_port)));

  if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) {
    int failure = errno;
    NS_SET_PTRPTR(o.error_string, "cannot create socket");
    if (nc->flags & NSF_CONNECTING) {
      ns_call(nc, NS_CONNECT, &failure);
    }
    ns_destroy_conn(nc);
    return NULL;
  }

  ns_set_non_blocking_mode(sock);
  rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa->sa, sizeof(sa->sin));

  if (rc != 0 && ns_is_error(rc)) {
    NS_SET_PTRPTR(o.error_string, "cannot connect to socket");
    if (nc->flags & NSF_CONNECTING) {
      ns_call(nc, NS_CONNECT, &rc);
    }
    ns_destroy_conn(nc);
    close(sock);
    return NULL;
  }

  /* Fire NS_CONNECT on next poll. */
  nc->flags |= NSF_CONNECTING;

  /* No ns_destroy_conn() call after this! */
  ns_set_sock(nc, sock);
  return nc;
}
Пример #4
0
/*
 * Connect to a remote host.
 *
 * If successful, `NS_CONNECT` event will be delivered
 * to the new connection. `addr` format is the same as for the `ns_bind()` call,
 * just an IP address becomes mandatory: `[PROTO://]HOST:PORT`.
 *
 * `PROTO` could be `tcp://` or `udp://`. If `HOST` is not an IP
 * address, Fossa will resolve it - beware that standard blocking resolver
 * will be used. It is a good practice to pre-resolve hosts beforehands and
 * use only IP addresses to avoid blockin an IO thread.
 *
 * See the `ns_connect_opts` structure for a description of the optional
 * parameters.
 *
 * Returns a new outbound connection, or `NULL` on error.
*/
struct ns_connection *ns_connect_opt(struct ns_mgr *mgr, const char *address,
                                     ns_event_handler_t callback,
                                     struct ns_connect_opts opts) {
  sock_t sock = INVALID_SOCKET;
  struct ns_connection *nc = NULL;
  union socket_address sa;
  int rc, proto;
  struct ns_add_sock_opts add_sock_opts;

  if (ns_parse_address(address, &sa, &proto) == 0) {
    errno = 0;
    ns_set_error_string(opts.error_string, "cannot parse address");
    return NULL;
  }
  if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) {
    ns_set_error_string(opts.error_string, "cannot create socket");
    return NULL;
  }

  ns_set_non_blocking_mode(sock);
  rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin));

  NS_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts);

  if (rc != 0 && ns_is_error(rc)) {
    ns_set_error_string(opts.error_string, "cannot connect to socket");
    closesocket(sock);
    return NULL;
  } else if ((nc = ns_add_sock_opt(mgr, sock, callback, add_sock_opts)) == NULL) {
    /* opts.error_string set by ns_add_sock_opt */
    closesocket(sock);
    return NULL;
  }

  nc->sa = sa;   /* Important, cause UDP conns will use sendto() */
  nc->flags |= (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING;

  return nc;
}
Пример #5
0
/* 'sa' must be an initialized address to bind to */
static sock_t ns_open_listening_socket(union socket_address *sa, int proto) {
  socklen_t sa_len = (sa->sa.sa_family == AF_INET) ?
    sizeof(sa->sin) : sizeof(sa->sin6);
  sock_t sock = INVALID_SOCKET;
  int on = 1;

  if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET &&

#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE)
      /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx */
      !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
                  (void *) &on, sizeof(on)) &&
#endif

#if 1 || !defined(_WIN32) || defined(SO_EXCLUSIVEADDRUSE)
      /*
       * SO_RESUSEADDR is not enabled on Windows because the semantics of
       * SO_REUSEADDR on UNIX and Windows is different. On Windows,
       * SO_REUSEADDR allows to bind a socket to a port without error even if
       * the port is already open by another program. This is not the behavior
       * SO_REUSEADDR was designed for, and leads to hard-to-track failure
       * scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless
       * SO_EXCLUSIVEADDRUSE is supported and set on a socket.
       */
      !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
#endif

      !bind(sock, &sa->sa, sa_len) &&
      (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) {
    ns_set_non_blocking_mode(sock);
    /* In case port was set to 0, get the real port number */
    (void) getsockname(sock, &sa->sa, &sa_len);
  } else if (sock != INVALID_SOCKET) {
    closesocket(sock);
    sock = INVALID_SOCKET;
  }

  return sock;
}
Пример #6
0
static int do_test(const char *cert_file, const char *key_file,
                   const char *ca_file, const char *cipher) {
  struct sockaddr_in sa;
  socklen_t slen;
  SSL_CTX *ctx;
  SSL *ssl;
  int ret = 0;
  int fd, cfd;

  ctx = setup_ctx(cert_file, key_file, ca_file, cipher);
  if (NULL == ctx) goto out;

  ssl = SSL_new(ctx);
  if (NULL == ssl) goto out_ctx;

  fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (fd < 0) {
    fprintf(stderr, "socket: %s\n", strerror(errno));
    goto out_ssl;
  }

  do {
    int val = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
  } while (0);

  sa.sin_family = AF_INET;
  sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  sa.sin_port = htons(TEST_PORT);
  if (bind(fd, (struct sockaddr *) &sa, sizeof(sa))) {
    fprintf(stderr, "bind: %s\n", strerror(errno));
    goto out_close;
  }

  if (listen(fd, 128)) {
    fprintf(stderr, "bind: %s\n", strerror(errno));
    goto out_close;
  }

  slen = sizeof(sa);
  printf("Waiting for a connection...\n");
  cfd = accept(fd, (struct sockaddr *) &sa, &slen);
  if (cfd < 0) {
    fprintf(stderr, "accept: %s\n", strerror(errno));
    goto out_close;
  }
  ns_set_non_blocking_mode(cfd);

  if (!SSL_set_fd(ssl, cfd)) goto out_close_cl;

  printf("Got connection\n");
  if (do_accept(ssl) <= 0) {
    goto shutdown;
  }

  if (!test_content(ssl)) {
    goto shutdown;
  }

  ret = 1;

shutdown:
  if (do_shutdown(ssl) > 0 && ret) {
    printf("SUCCESS\n");
  } else {
    printf("shutdown failed\n");
    ret = 0;
  }
out_close_cl:
  close(cfd);
out_close:
  close(fd);
out_ssl:
  SSL_free(ssl);
out_ctx:
  SSL_CTX_free(ctx);
out:
  return ret;
}