/* 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)); }
/* * 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; }
/* * 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; }
/* * 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; }
/* '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; }
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; }