From 1b2f22fd68a2e50cabdfe2e036096cc9e7a05a1f Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Fri, 5 Jan 2018 14:44:10 -0800 Subject: Convert existing websocket and http code to use new URL framework. This also fixes a use-after-free bug in the HTTP framework, where the handler could be deleted why callbacks were still using it. (We now reference count the handlers.) --- src/core/transport.c | 70 +------------ src/core/transport.h | 8 +- src/core/url.c | 13 ++- src/core/url.h | 3 +- src/supplemental/http/server.c | 43 +++++--- src/supplemental/websocket/websocket.c | 185 ++++++++++----------------------- src/transport/tcp/tcp.c | 178 +++++++++---------------------- 7 files changed, 147 insertions(+), 353 deletions(-) (limited to 'src') diff --git a/src/core/transport.c b/src/core/transport.c index 9c129a72..0891ec8c 100644 --- a/src/core/transport.c +++ b/src/core/transport.c @@ -1,6 +1,6 @@ // -// Copyright 2017 Garrett D'Amore -// Copyright 2017 Capitar IT Group BV +// Copyright 2018 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -106,72 +106,6 @@ nni_tran_find(const char *addr) return (NULL); } -// nni_tran_parse_host_port is a convenience routine to parse the host portion -// of a URL (which includes a DNS name or IP address and an optional service -// name or port, separated by a colon) into its host and port name parts. It -// understands IPv6 address literals when surrounded by brackets ([]). -// If either component is empty, then NULL is passed back for the value, -// otherwise a string suitable for freeing with nni_strfree is supplied. -int -nni_tran_parse_host_port(const char *pair, char **hostp, char **portp) -{ - const char *hstart; - const char *pstart; - char * host; - char * port; - size_t hlen, plen; - - if (pair[0] == '[') { - hstart = pair + 1; - hlen = 0; - while (hstart[hlen] != ']') { - if (hstart[hlen] == '\0') { - return (NNG_EADDRINVAL); - } - hlen++; - } - pstart = hstart + hlen + 1; // skip over the trailing ']' - } else { - // Normal thing. - hstart = pair; - hlen = 0; - while ((hstart[hlen] != ':') && (hstart[hlen] != '\0')) { - hlen++; - } - pstart = hstart + hlen; - } - if (pstart[0] == ':') { - pstart++; - } - plen = strlen(pstart); - - host = NULL; - if (hostp) { - if ((hlen > 1) || ((hlen == 1) && (*hstart != '*'))) { - if ((host = nni_alloc(hlen + 1)) == NULL) { - return (NNG_ENOMEM); - } - memcpy(host, hstart, hlen); - host[hlen] = '\0'; - } - } - - port = NULL; - if ((plen != 0) && (portp)) { - if ((port = nni_strdup(pstart)) == NULL) { - nni_strfree(host); - return (NNG_ENOMEM); - } - } - if (hostp) { - *hostp = host; - } - if (portp) { - *portp = port; - } - return (0); -} - int nni_tran_chkopt(const char *name, const void *v, size_t sz) { diff --git a/src/core/transport.h b/src/core/transport.h index 3098bd1a..9a6dc31d 100644 --- a/src/core/transport.h +++ b/src/core/transport.h @@ -1,6 +1,6 @@ // -// Copyright 2017 Garrett D'Amore -// Copyright 2017 Capitar IT Group BV +// Copyright 2018 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -162,10 +162,6 @@ struct nni_tran_pipe { nni_tran_pipe_option *p_options; }; -// Utility for transports. - -extern int nni_tran_parse_host_port(const char *, char **, char **); - // These APIs are used by the framework internally, and not for use by // transport implementations. extern nni_tran *nni_tran_find(const char *); diff --git a/src/core/url.c b/src/core/url.c index 8dee5219..8cb5a2e7 100644 --- a/src/core/url.c +++ b/src/core/url.c @@ -1,6 +1,6 @@ // -// Copyright 2017 Garrett D'Amore -// Copyright 2017 Capitar IT Group BV +// Copyright 2018 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -250,13 +250,17 @@ nni_url_parse(nni_url **urlp, const char *raw) } if ((url->u_host = nni_alloc(len + 1)) == NULL) { - nni_url_free(url); - return (NNG_ENOMEM); + rv = NNG_ENOMEM; + goto error; } memcpy(url->u_host, s, len); url->u_host[len] = '\0'; s += len; + if ((url->u_rawpath = nni_strdup(s)) == NULL) { + rv = NNG_ENOMEM; + goto error; + } for (len = 0; (c = s[len]) != '\0'; len++) { if ((c == '?') || (c == '#')) { break; @@ -363,5 +367,6 @@ nni_url_free(nni_url *url) nni_strfree(url->u_path); nni_strfree(url->u_query); nni_strfree(url->u_fragment); + nni_strfree(url->u_rawpath); NNI_FREE_STRUCT(url); } \ No newline at end of file diff --git a/src/core/url.h b/src/core/url.h index ee336b1d..91054dcb 100644 --- a/src/core/url.h +++ b/src/core/url.h @@ -1,5 +1,5 @@ // -// Copyright 2018 Garrett D'Amore +// Copyright 2018 Staysail Systems, Inc. // Copyright 2018 Capitar IT Group BV // // This software is supplied under the terms of the MIT License, a @@ -23,6 +23,7 @@ struct nni_url { char *u_path; // path, will be "" if not specified char *u_query; // without '?', will be NULL if not specified char *u_fragment; // without '#', will be NULL if not specified + char *u_rawpath; // includes query and fragment, "" if not specified }; extern int nni_url_parse(nni_url **, const char *path); diff --git a/src/supplemental/http/server.c b/src/supplemental/http/server.c index cce73765..01f1230d 100644 --- a/src/supplemental/http/server.c +++ b/src/supplemental/http/server.c @@ -35,6 +35,7 @@ typedef struct http_handler { char * h_host; bool h_is_upgrader; bool h_is_dir; + int h_refcnt; void (*h_cb)(nni_aio *); void (*h_free)(void *); } http_handler; @@ -72,6 +73,7 @@ struct nni_http_server { static nni_list http_servers; static nni_mtx http_servers_lk; +static void http_handler_fini(http_handler *); static void http_sconn_reap(void *arg) @@ -372,6 +374,7 @@ http_sconn_rxdone(void *arg) strncpy(uri, val, urisz); path = http_uri_canonify(uri); + nni_mtx_lock(&s->mtx); NNI_LIST_FOREACH (&s->handlers, h) { size_t len; if (h->h_host != NULL) { @@ -438,6 +441,7 @@ http_sconn_rxdone(void *arg) nni_free(uri, urisz); if (h == NULL) { + nni_mtx_unlock(&s->mtx); if (badmeth) { http_sconn_error( sc, NNI_HTTP_STATUS_METHOD_NOT_ALLOWED); @@ -450,23 +454,27 @@ http_sconn_rxdone(void *arg) nni_aio_set_input(sc->cbaio, 0, sc->http); nni_aio_set_input(sc->cbaio, 1, sc->req); nni_aio_set_input(sc->cbaio, 2, h->h_arg); - nni_aio_set_data(sc->cbaio, 1, h); // Technically, probably callback should initialize this with // start, but we do it instead. if (nni_aio_start(sc->cbaio, NULL, NULL) == 0) { + nni_aio_set_data(sc->cbaio, 1, h); + h->h_refcnt++; h->h_cb(sc->cbaio); } + nni_mtx_unlock(&s->mtx); } static void http_sconn_cbdone(void *arg) { - http_sconn * sc = arg; - nni_aio * aio = sc->cbaio; - nni_http_res *res; - http_handler *h; + http_sconn * sc = arg; + nni_aio * aio = sc->cbaio; + nni_http_res * res; + http_handler * h; + nni_http_server *s = sc->server; + bool upgrader; if (nni_aio_result(aio) != 0) { // Hard close, no further feedback. @@ -477,10 +485,18 @@ http_sconn_cbdone(void *arg) h = nni_aio_get_data(aio, 1); res = nni_aio_get_output(aio, 0); + nni_mtx_lock(&s->mtx); + upgrader = h->h_is_upgrader; + h->h_refcnt--; + if (h->h_refcnt == 0) { + http_handler_fini(h); + } + nni_mtx_unlock(&s->mtx); + // If its an upgrader, and they didn't give us back a response, it // means that they took over, and we should just discard this session, // without closing the underlying channel. - if ((h->h_is_upgrader) && (res == NULL)) { + if (upgrader && (res == NULL)) { sc->http = NULL; // the underlying HTTP is not closed sc->req = NULL; sc->res = NULL; @@ -795,13 +811,11 @@ http_server_add_handler(void **hp, nni_http_server *s, nni_http_handler *hh, h->h_is_upgrader = hh->h_is_upgrader; h->h_free = freeit; - // Ignore the port part of the host. + // When registering the handler, only register *host*, not port. if (hh->h_host != NULL) { - int rv; - rv = nni_tran_parse_host_port(hh->h_host, &h->h_host, NULL); - if (rv != 0) { + if ((h->h_host = nni_strdup(hh->h_host)) == NULL) { http_handler_fini(h); - return (rv); + return (NNG_ENOMEM); } } @@ -847,6 +861,7 @@ http_server_add_handler(void **hp, nni_http_server *s, nni_http_handler *hh, return (NNG_EADDRINUSE); } } + h->h_refcnt = 1; nni_list_append(&s->handlers, h); nni_mtx_unlock(&s->mtx); if (hp != NULL) { @@ -869,9 +884,11 @@ nni_http_server_del_handler(nni_http_server *s, void *harg) nni_mtx_lock(&s->mtx); nni_list_node_remove(&h->node); + h->h_refcnt--; + if (h->h_refcnt == 0) { + http_handler_fini(h); + } nni_mtx_unlock(&s->mtx); - - http_handler_fini(h); } // Very limited MIME type map. Used only if the handler does not diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index effba8d7..c350a3c7 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -57,15 +57,12 @@ struct nni_ws { struct nni_ws_listener { nni_http_server * server; char * proto; - char * url; - char * host; - char * serv; - char * path; nni_mtx mtx; nni_cv cv; nni_list pend; nni_list reply; nni_list aios; + nni_url * url; bool started; bool closed; void * hp; // handler pointer @@ -88,12 +85,7 @@ struct nni_ws_dialer { nni_mtx mtx; nni_aio * conaio; char * proto; - char * host; - char * serv; - char * path; - char * qinfo; - char * addr; // full address (a URL really) - char * uri; // path + query + nni_url * url; nni_list conaios; // user aios waiting for connect. nni_list httpaios; // user aios waiting for HTTP nego. bool started; @@ -1280,7 +1272,7 @@ ws_init(nni_ws **wsp, nni_http *http, nni_http_req *req, nni_http_res *res) } nni_aio_set_timeout(ws->closeaio, 100); - nni_aio_set_timeout(ws->httpaio, 1000); + nni_aio_set_timeout(ws->httpaio, 2000); ws->fragsize = 1 << 20; // we won't send a frame larger than this ws->maxframe = (1 << 20) * 10; // default limit on incoming frame size @@ -1311,17 +1303,16 @@ nni_ws_listener_fini(nni_ws_listener *l) nni_cv_fini(&l->cv); nni_mtx_fini(&l->mtx); - nni_strfree(l->url); nni_strfree(l->proto); - nni_strfree(l->host); - nni_strfree(l->serv); - nni_strfree(l->path); while ((hdr = nni_list_first(&l->headers)) != NULL) { nni_list_remove(&l->headers, hdr); nni_strfree(hdr->name); nni_strfree(hdr->value); NNI_FREE_STRUCT(hdr); } + if (l->url) { + nni_url_free(l->url); + } NNI_FREE_STRUCT(l); } @@ -1479,103 +1470,15 @@ err: } } -static int -ws_parse_url(const char *url, char **schemep, char **hostp, char **servp, - char **pathp, char **queryp) -{ - size_t scrlen; - char * scr; - char * pair; - char * scheme = NULL; - char * path = NULL; - char * query = NULL; - char * host = NULL; - char * serv = NULL; - int rv; - - // We need a scratch copy of the url to parse. - scrlen = strlen(url) + 1; - if ((scr = nni_alloc(scrlen)) == NULL) { - return (NNG_ENOMEM); - } - nni_strlcpy(scr, url, scrlen); - scheme = scr; - pair = strchr(scr, ':'); - if ((pair == NULL) || (pair[1] != '/') || (pair[2] != '/')) { - nni_free(scr, scrlen); - return (NNG_EADDRINVAL); - } - - *pair = '\0'; - pair += 3; - - path = strchr(pair, '/'); - if (path != NULL) { - *path = '\0'; // We will restore it shortly. - } - if ((rv = nni_tran_parse_host_port(pair, hostp, servp)) != 0) { - nni_free(scr, scrlen); - return (rv); - } - - // If service was missing, assume normal defaults. - if (*servp == NULL) { - if (strcmp(scheme, "wss")) { - *servp = nni_strdup("443"); - } else { - *servp = nni_strdup("80"); - } - } - - if (path) { - // Restore the path, and trim off the query parameter. - *path = '/'; - if ((query = strchr(path, '?')) != NULL) { - *query = '\0'; - query++; - } else { - query = ""; - } - } else { - path = "/"; - query = ""; - } - - if (schemep) { - *schemep = nni_strdup(scheme); - } - if (pathp) { - *pathp = nni_strdup(path); - } - if (queryp) { - *queryp = nni_strdup(query); - } - nni_free(scr, scrlen); - - if ((schemep && (*schemep == NULL)) || (*pathp == NULL) || - (*servp == NULL) || (queryp && (*queryp == NULL))) { - nni_strfree(*hostp); - nni_strfree(*servp); - nni_strfree(*pathp); - if (schemep) { - nni_strfree(*schemep); - } - if (queryp) { - nni_strfree(*queryp); - } - return (NNG_ENOMEM); - } - - return (0); -} - int -nni_ws_listener_init(nni_ws_listener **wslp, const char *url) +nni_ws_listener_init(nni_ws_listener **wslp, const char *addr) { nni_ws_listener *l; int rv; nni_aio * aio; nni_sockaddr sa; + char * host; + char * serv; if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { return (NNG_ENOMEM); @@ -1587,16 +1490,25 @@ nni_ws_listener_init(nni_ws_listener **wslp, const char *url) NNI_LIST_INIT(&l->pend, nni_ws, node); NNI_LIST_INIT(&l->reply, nni_ws, node); - rv = ws_parse_url(url, NULL, &l->host, &l->serv, &l->path, NULL); - if (rv != 0) { + if ((rv = nni_url_parse(&l->url, addr)) != 0) { nni_ws_listener_fini(l); return (rv); } + + host = l->url->u_hostname; + if ((strlen(host) == 0) || (strcmp(host, "*") == 0)) { + host = NULL; + } + serv = l->url->u_port; + if (strlen(serv) == 0) { + serv = (strcmp(l->url->u_scheme, "wss") == 0) ? "443" : "80"; + } + l->handler.h_is_dir = false; l->handler.h_is_upgrader = true; l->handler.h_method = "GET"; - l->handler.h_path = l->path; - l->handler.h_host = l->host; + l->handler.h_path = l->url->u_path; + l->handler.h_host = host; // ignore the port l->handler.h_cb = ws_handler; if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { @@ -1604,7 +1516,7 @@ nni_ws_listener_init(nni_ws_listener **wslp, const char *url) return (rv); } aio->a_addr = &sa; - nni_plat_tcp_resolv(l->host, l->serv, NNG_AF_UNSPEC, true, aio); + nni_plat_tcp_resolv(host, serv, NNG_AF_UNSPEC, true, aio); nni_aio_wait(aio); rv = nni_aio_result(aio); nni_aio_fini(aio); @@ -1813,18 +1725,12 @@ ws_conn_cb(void *arg) nni_base64_encode(raw, 16, wskey, 24); wskey[24] = '\0'; - if (d->qinfo && d->qinfo[0] != '\0') { - rv = nni_asprintf(&d->uri, "%s?%s", d->path, d->qinfo); - } else if ((d->uri = nni_strdup(d->path)) == NULL) { - rv = NNG_ENOMEM; - } - #define SETH(h, v) nni_http_req_set_header(req, h, v) if ((rv != 0) || ((rv = nni_http_req_init(&req)) != 0) || - ((rv = nni_http_req_set_uri(req, d->uri)) != 0) || + ((rv = nni_http_req_set_uri(req, d->url->u_rawpath)) != 0) || ((rv = nni_http_req_set_version(req, "HTTP/1.1")) != 0) || ((rv = nni_http_req_set_method(req, "GET")) != 0) || - ((rv = SETH("Host", d->host)) != 0) || + ((rv = SETH("Host", d->url->u_host)) != 0) || ((rv = SETH("Upgrade", "websocket")) != 0) || ((rv = SETH("Connection", "Upgrade")) != 0) || ((rv = SETH("Sec-WebSocket-Key", wskey)) != 0) || @@ -1879,12 +1785,6 @@ nni_ws_dialer_fini(nni_ws_dialer *d) nni_aio_fini(d->conaio); nni_strfree(d->proto); - nni_strfree(d->addr); - nni_strfree(d->uri); - nni_strfree(d->host); - nni_strfree(d->serv); - nni_strfree(d->path); - nni_strfree(d->qinfo); while ((hdr = nni_list_first(&d->headers)) != NULL) { nni_list_remove(&d->headers, hdr); nni_strfree(hdr->name); @@ -1894,16 +1794,22 @@ nni_ws_dialer_fini(nni_ws_dialer *d) if (d->client) { nni_http_client_fini(d->client); } + if (d->url) { + nni_url_free(d->url); + } nni_mtx_fini(&d->mtx); NNI_FREE_STRUCT(d); } int -nni_ws_dialer_init(nni_ws_dialer **dp, const char *url) +nni_ws_dialer_init(nni_ws_dialer **dp, const char *addr) { nni_ws_dialer *d; int rv; nni_aio * aio; + nni_url * url; + char * host; + char * serv; if ((d = NNI_ALLOC_STRUCT(d)) == NULL) { return (NNG_ENOMEM); @@ -1913,15 +1819,29 @@ nni_ws_dialer_init(nni_ws_dialer **dp, const char *url) nni_aio_list_init(&d->conaios); nni_aio_list_init(&d->httpaios); - if ((d->addr = nni_strdup(url)) == NULL) { + if ((rv = nni_url_parse(&d->url, addr)) != 0) { nni_ws_dialer_fini(d); - return (NNG_ENOMEM); + return (rv); } - if ((rv = ws_parse_url( - url, NULL, &d->host, &d->serv, &d->path, &d->qinfo)) != 0) { + + // Dialer requires a valid host. + if ((strlen(d->url->u_hostname) == 0) || + (strcmp(d->url->u_hostname, "*") == 0)) { nni_ws_dialer_fini(d); - return (rv); + return (NNG_EADDRINVAL); } + + // Default port is 80 for ws, and 443 for wss. + if ((d->url->u_port == NULL) || (strlen(d->url->u_port) == 0)) { + if (strcmp(d->url->u_scheme, "wss") == 0) { + serv = "443"; + } else { + serv = "80"; + } + } else { + serv = d->url->u_port; + } + if ((rv = nni_aio_init(&d->conaio, ws_conn_cb, d)) != 0) { nni_ws_dialer_fini(d); return (rv); @@ -1933,7 +1853,8 @@ nni_ws_dialer_init(nni_ws_dialer **dp, const char *url) } // XXX: this is synchronous. We should fix this in the HTTP layer. aio->a_addr = &d->sa; - nni_plat_tcp_resolv(d->host, d->serv, NNG_AF_UNSPEC, false, aio); + nni_plat_tcp_resolv( + d->url->u_hostname, serv, NNG_AF_UNSPEC, false, aio); nni_aio_wait(aio); rv = nni_aio_result(aio); nni_aio_fini(aio); diff --git a/src/transport/tcp/tcp.c b/src/transport/tcp/tcp.c index 538833c2..a361da53 100644 --- a/src/transport/tcp/tcp.c +++ b/src/transport/tcp/tcp.c @@ -22,7 +22,6 @@ typedef struct nni_tcp_ep nni_tcp_ep; // nni_tcp_pipe is one end of a TCP connection. struct nni_tcp_pipe { - const char * addr; nni_plat_tcp_pipe *tpp; uint16_t peer; uint16_t proto; @@ -46,7 +45,6 @@ struct nni_tcp_pipe { }; struct nni_tcp_ep { - char addr[NNG_MAXADDRLEN + 1]; nni_plat_tcp_ep *tep; uint16_t proto; size_t rcvmax; @@ -54,6 +52,7 @@ struct nni_tcp_ep { int ipv4only; nni_aio * aio; nni_aio * user_aio; + nni_url * url; nni_mtx mtx; }; @@ -123,7 +122,6 @@ nni_tcp_pipe_init(nni_tcp_pipe **pipep, nni_tcp_ep *ep, void *tpp) p->proto = ep->proto; p->rcvmax = ep->rcvmax; p->tpp = tpp; - p->addr = ep->addr; *pipep = p; return (0); @@ -488,96 +486,7 @@ nni_tcp_pipe_getopt_remaddr(void *arg, void *v, size_t *szp) return (rv); } -static int -nni_tcp_parse_pair(char *pair, char **hostp, char **servp) -{ - char *host, *serv, *end; - - if (pair[0] == '[') { - host = pair + 1; - // IP address enclosed ... for IPv6 usually. - if ((end = strchr(host, ']')) == NULL) { - return (NNG_EADDRINVAL); - } - *end = '\0'; - serv = end + 1; - if (*serv == ':') { - serv++; - } else if (*serv != '\0') { - return (NNG_EADDRINVAL); - } - } else { - host = pair; - serv = strchr(host, ':'); - if (serv != NULL) { - *serv = '\0'; - serv++; - } - } - if ((strlen(host) == 0) || (strcmp(host, "*") == 0)) { - *hostp = NULL; - } else { - *hostp = host; - } - if ((serv == NULL) || (strlen(serv) == 0)) { - *servp = NULL; - } else { - *servp = serv; - } - // Stash the port in big endian (network) byte order. - return (0); -} - // Note that the url *must* be in a modifiable buffer. -int -nni_tcp_parse_url(char *url, char **lhost, char **lserv, char **rhost, - char **rserv, int mode) -{ - char *h1; - int rv; - - if (strncmp(url, "tcp://", strlen("tcp://")) != 0) { - return (NNG_EADDRINVAL); - } - url += strlen("tcp://"); - if ((mode == NNI_EP_MODE_DIAL) && ((h1 = strchr(url, ';')) != 0)) { - // The local address is the first part, the remote address - // is the second part. - *h1 = '\0'; - h1++; - if (((rv = nni_tcp_parse_pair(h1, rhost, rserv)) != 0) || - ((rv = nni_tcp_parse_pair(url, lhost, lserv)) != 0)) { - return (rv); - } - if ((*rserv == NULL) || (*rhost == NULL)) { - // We have to know where to connect to! - return (NNG_EADDRINVAL); - } - } else if (mode == NNI_EP_MODE_DIAL) { - *lhost = NULL; - *lserv = NULL; - if ((rv = nni_tcp_parse_pair(url, rhost, rserv)) != 0) { - return (rv); - } - if ((*rserv == NULL) || (*rhost == NULL)) { - // We have to know where to connect to! - return (NNG_EADDRINVAL); - } - } else { - NNI_ASSERT(mode == NNI_EP_MODE_LISTEN); - *rhost = NULL; - *rserv = NULL; - if ((rv = nni_tcp_parse_pair(url, lhost, lserv)) != 0) { - return (rv); - } - // We have to have a port to listen on! - if (*lserv == NULL) { - return (NNG_EADDRINVAL); - } - } - return (0); -} - static void nni_tcp_pipe_start(void *arg, nni_aio *aio) { @@ -618,83 +527,94 @@ nni_tcp_ep_fini(void *arg) if (ep->tep != NULL) { nni_plat_tcp_ep_fini(ep->tep); } + nni_url_free(ep->url); nni_aio_fini(ep->aio); nni_mtx_fini(&ep->mtx); NNI_FREE_STRUCT(ep); } static int -nni_tcp_ep_init(void **epp, const char *url, nni_sock *sock, int mode) +nni_tcp_ep_init(void **epp, const char *addr, nni_sock *sock, int mode) { nni_tcp_ep * ep; int rv; - char buf[NNG_MAXADDRLEN + 1]; - char * rhost; - char * rserv; - char * lhost; - char * lserv; + char * host; + char * serv; nni_sockaddr rsa, lsa; nni_aio * aio; int passive; + nni_url * url; - // Make a copy of the url (to allow for destructive operations) - if (nni_strlcpy(buf, url, sizeof(buf)) >= sizeof(buf)) { + if ((rv = nni_url_parse(&url, addr)) != 0) { + return (rv); + } + // Check for invalid URL components. + if ((strlen(url->u_path) != 0) && (strcmp(url->u_path, "/") != 0)) { + nni_url_free(url); return (NNG_EADDRINVAL); } - - // Parse the URLs first. - rv = nni_tcp_parse_url(buf, &lhost, &lserv, &rhost, &rserv, mode); - if (rv != 0) { - return (rv); + if ((url->u_fragment != NULL) || (url->u_userinfo != NULL) || + (url->u_query != NULL)) { + nni_url_free(url); + return (NNG_EADDRINVAL); } - passive = (mode == NNI_EP_MODE_DIAL ? 0 : 1); if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { + nni_url_free(url); return (rv); } + if ((strlen(url->u_hostname) == 0) || + (strcmp(url->u_hostname, "*") == 0)) { + host = NULL; + } else { + host = url->u_hostname; + } + + if (strlen(url->u_port) == 0) { + serv = NULL; + } else { + serv = url->u_port; + } // XXX: arguably we could defer this part to the point we do a bind // or connect! - - if ((rhost != NULL) || (rserv != NULL)) { - aio->a_addr = &rsa; - nni_plat_tcp_resolv(rhost, rserv, NNG_AF_UNSPEC, passive, aio); - nni_aio_wait(aio); - if ((rv = nni_aio_result(aio)) != 0) { + if (mode == NNI_EP_MODE_DIAL) { + passive = 0; + lsa.s_un.s_family = NNG_AF_UNSPEC; + aio->a_addr = &rsa; + if ((host == NULL) || (serv == NULL)) { + nni_url_free(url); nni_aio_fini(aio); - return (rv); + return (NNG_EADDRINVAL); } } else { + passive = 1; rsa.s_un.s_family = NNG_AF_UNSPEC; + aio->a_addr = &lsa; } - if ((lhost != NULL) || (lserv != NULL)) { - aio->a_addr = &lsa; - nni_plat_tcp_resolv(lhost, lserv, NNG_AF_UNSPEC, passive, aio); - nni_aio_wait(aio); - if ((rv = nni_aio_result(aio)) != 0) { - nni_aio_fini(aio); - return (rv); - } - } else { - lsa.s_un.s_family = NNG_AF_UNSPEC; + nni_plat_tcp_resolv(host, serv, NNG_AF_UNSPEC, passive, aio); + nni_aio_wait(aio); + if ((rv = nni_aio_result(aio)) != 0) { + nni_url_free(url); + nni_aio_fini(aio); + return (rv); } + nni_aio_fini(aio); if ((ep = NNI_ALLOC_STRUCT(ep)) == NULL) { + nni_url_free(url); return (NNG_ENOMEM); } - if (nni_strlcpy(ep->addr, url, sizeof(ep->addr)) >= sizeof(ep->addr)) { - NNI_FREE_STRUCT(ep); - return (NNG_EADDRINVAL); - } + nni_mtx_init(&ep->mtx); + ep->url = url; if ((rv = nni_plat_tcp_ep_init(&ep->tep, &lsa, &rsa, mode)) != 0) { - NNI_FREE_STRUCT(ep); + nni_tcp_ep_fini(ep); return (rv); } - nni_mtx_init(&ep->mtx); if ((rv = nni_aio_init(&ep->aio, nni_tcp_ep_cb, ep)) != 0) { nni_tcp_ep_fini(ep); return (rv); -- cgit v1.2.3-70-g09d2