static const char *test_parse_address(void) { static const char *valid[] = { "1", "1.2.3.4:1", "tcp://123", "udp://0.0.0.0:99", "ssl://17", "ssl://900:a.pem:b.pem", "ssl://1.2.3.4:9000:aa.pem", #if defined(NS_ENABLE_IPV6) "udp://[::1]:123", "[3ffe:2a00:100:7031::1]:900", #endif NULL }; static const int protos[] = {SOCK_STREAM, SOCK_STREAM, SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM, SOCK_STREAM, SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM}; static const int use_ssls[] = {0, 0, 0, 0, 1, 1, 1, 0, 0}; static const char *invalid[] = { "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", "blah://12", NULL }; union socket_address sa; char cert[100], ca[100]; int i, proto, use_ssl; for (i = 0; valid[i] != NULL; i++) { ASSERT(ns_parse_address(valid[i], &sa, &proto, &use_ssl, cert, ca) != 0); ASSERT(proto == protos[i]); ASSERT(use_ssl == use_ssls[i]); } for (i = 0; invalid[i] != NULL; i++) { ASSERT(ns_parse_address(invalid[i], &sa, &proto, &use_ssl, cert, ca) == 0); } ASSERT(ns_parse_address("0", &sa, &proto, &use_ssl, cert, ca) != 0); return NULL; }
struct ns_connection *ns_bind_opt(struct ns_mgr *srv, const char *str, ns_event_handler_t callback, struct ns_bind_opts opts) { union socket_address sa; struct ns_connection *nc = NULL; int proto; sock_t sock; struct ns_add_sock_opts add_sock_opts; NS_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); if (ns_parse_address(str, &sa, &proto) == 0) { errno = 0; ns_set_error_string(opts.error_string, "cannot parse address"); } else if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) { DBG(("Failed to open listener: %d", errno)); ns_set_error_string(opts.error_string, "failed to open listener"); } else if ((nc = ns_add_sock_opt(srv, sock, callback, add_sock_opts)) == NULL) { /* opts.error_string set by ns_add_sock_opt */ DBG(("Failed to ns_add_sock")); closesocket(sock); } else { nc->sa = sa; nc->flags |= NSF_LISTENING; nc->handler = callback; if (proto == SOCK_DGRAM) { nc->flags |= NSF_UDP; } DBG(("%p sock %d/%d", nc, sock, proto)); } return nc; }
struct ns_connection *ns_connect_opt(struct ns_mgr *mgr, const char *address, ns_event_handler_t callback, struct ns_connect_opts opts) { struct ns_connection *nc = NULL; int proto, rc; struct ns_add_sock_opts add_sock_opts; char host[NS_MAX_HOST_LEN]; NS_COPY_COMMON_CONNECTION_OPTIONS(&add_sock_opts, &opts); if ((nc = ns_create_connection(mgr, callback, add_sock_opts)) == NULL) { return NULL; } else if ((rc = ns_parse_address(address, &nc->sa, &proto, host, sizeof(host))) < 0) { /* Address is malformed */ NS_SET_PTRPTR(opts.error_string, "cannot parse address"); ns_destroy_conn(nc); return NULL; } nc->flags |= opts.flags & _NS_ALLOWED_CONNECT_FLAGS_MASK; nc->flags |= (proto == SOCK_DGRAM) ? NSF_UDP : 0; nc->user_data = opts.user_data; if (rc == 0) { #ifndef NS_DISABLE_RESOLVER /* * DNS resolution is required for host. * ns_parse_address() fills port in nc->sa, which we pass to resolve_cb() */ if (ns_resolve_async(nc->mgr, host, NS_DNS_A_RECORD, resolve_cb, nc) != 0) { NS_SET_PTRPTR(opts.error_string, "cannot schedule DNS lookup"); ns_destroy_conn(nc); return NULL; } return nc; #else NS_SET_PTRPTR(opts.error_string, "Resolver is disabled"); ns_destroy_conn(nc); return NULL; #endif } else { /* Address is parsed and resolved to IP. proceed with connect() */ return ns_finish_connect(nc, proto, &nc->sa, add_sock_opts); } }
/* * 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; }