diff --git a/http-internal.h b/http-internal.h index 94059065..2d0ae8fc 100644 --- a/http-internal.h +++ b/http-internal.h @@ -215,6 +215,9 @@ void evhttp_start_write_(struct evhttp_connection *); void evhttp_response_code_(struct evhttp_request *, int, const char *); void evhttp_send_page_(struct evhttp_request *, struct evbuffer *); +/* [] has been stripped */ +#define _EVHTTP_URI_HOST_HAS_BRACKETS 0x02 + EVENT2_EXPORT_SYMBOL int evhttp_decode_uri_internal(const char *uri, size_t length, char *ret, int decode_plus); diff --git a/http.c b/http.c index 8afb3600..45441f22 100644 --- a/http.c +++ b/http.c @@ -180,7 +180,7 @@ extern int debug; static evutil_socket_t create_bind_socket_nonblock(struct evutil_addrinfo *, int reuse); static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse); static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **); -static struct evhttp_uri *evhttp_uri_parse_authority(char *source_uri); +static struct evhttp_uri *evhttp_uri_parse_authority(char *source_uri, unsigned flags); static int evhttp_associate_new_request_with_connection( struct evhttp_connection *evcon); static void evhttp_connection_start_detectclose( @@ -2055,7 +2055,7 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len) } if (type == EVHTTP_REQ_CONNECT) { - if ((req->uri_elems = evhttp_uri_parse_authority(req->uri)) == NULL) { + if ((req->uri_elems = evhttp_uri_parse_authority(req->uri, 0)) == NULL) { return -1; } } else { @@ -4910,9 +4910,11 @@ bracket_addr_ok(const char *s, const char *eos) } static int -parse_authority(struct evhttp_uri *uri, char *s, char *eos) +parse_authority(struct evhttp_uri *uri, char *s, char *eos, unsigned *flags) { + size_t len; char *cp, *port; + EVUTIL_ASSERT(eos); if (eos == s) { uri->host = mm_strdup(""); @@ -4952,22 +4954,31 @@ parse_authority(struct evhttp_uri *uri, char *s, char *eos) /* Now, cp..eos holds the "host" port, which can be an IPv4Address, * an IP-Literal, or a reg-name */ EVUTIL_ASSERT(eos >= cp); + len = eos-cp; if (*cp == '[' && eos >= cp+2 && *(eos-1) == ']') { /* IPv6address, IP-Literal, or junk. */ if (! bracket_addr_ok(cp, eos)) return -1; + if (*flags & EVHTTP_URI_HOST_STRIP_BRACKETS) + len = eos-cp-2; } else { /* Make sure the host part is ok. */ if (! regname_ok(cp,eos)) /* Match IPv4Address or reg-name */ return -1; } - uri->host = mm_malloc(eos-cp+1); + + uri->host = mm_malloc(len+1); if (uri->host == NULL) { event_warn("%s: malloc", __func__); return -1; } - memcpy(uri->host, cp, eos-cp); - uri->host[eos-cp] = '\0'; + if (*cp == '[' && *flags & EVHTTP_URI_HOST_STRIP_BRACKETS) { + memcpy(uri->host, cp+1, len); + *flags |= _EVHTTP_URI_HOST_HAS_BRACKETS; + } else { + memcpy(uri->host, cp, len); + } + uri->host[len] = '\0'; return 0; } @@ -5103,7 +5114,7 @@ evhttp_uri_parse_with_flags(const char *source_uri, unsigned flags) readp += 2; authority = readp; path = end_of_authority(readp); - if (parse_authority(uri, authority, path) < 0) + if (parse_authority(uri, authority, path, &uri->flags) < 0) goto err; readp = path; got_authority = 1; @@ -5182,7 +5193,7 @@ err: } static struct evhttp_uri * -evhttp_uri_parse_authority(char *source_uri) +evhttp_uri_parse_authority(char *source_uri, unsigned flags) { struct evhttp_uri *uri = mm_calloc(1, sizeof(struct evhttp_uri)); char *end; @@ -5192,10 +5203,10 @@ evhttp_uri_parse_authority(char *source_uri) goto err; } uri->port = -1; - uri->flags = 0; + uri->flags = flags; end = end_of_authority(source_uri); - if (parse_authority(uri, source_uri, end) < 0) + if (parse_authority(uri, source_uri, end, &uri->flags) < 0) goto err; uri->path = mm_strdup(""); @@ -5254,7 +5265,13 @@ evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit) evbuffer_add(tmp, "//", 2); if (uri->userinfo) evbuffer_add_printf(tmp,"%s@", uri->userinfo); - URI_ADD_(host); + if (uri->flags & _EVHTTP_URI_HOST_HAS_BRACKETS) { + evbuffer_add(tmp, "[", 1); + URI_ADD_(host); + evbuffer_add(tmp, "]", 1); + } else { + URI_ADD_(host); + } if (uri->port >= 0) evbuffer_add_printf(tmp,":%d", uri->port); @@ -5284,7 +5301,7 @@ evhttp_uri_join(struct evhttp_uri *uri, char *buf, size_t limit) evbuffer_free(tmp); return NULL; } - evbuffer_remove(tmp, buf, joined_size); + evbuffer_remove(tmp, buf, joined_size); output = buf; err: @@ -5363,17 +5380,39 @@ evhttp_uri_set_userinfo(struct evhttp_uri *uri, const char *userinfo) int evhttp_uri_set_host(struct evhttp_uri *uri, const char *host) { + size_t len = 0; + if (host) { + len = strlen(host); + if (host[0] == '[') { - if (! bracket_addr_ok(host, host+strlen(host))) + if (! bracket_addr_ok(host, host+len)) return -1; } else { - if (! regname_ok(host, host+strlen(host))) + if (! regname_ok(host, host+len)) return -1; } } - URI_SET_STR_(host); + if (host && host[0] == '[' && uri->flags & EVHTTP_URI_HOST_STRIP_BRACKETS) { + char *new_host; + + len -= 2; + new_host = mm_realloc(uri->host, len+1); + if (!new_host) { + free(uri->host); + uri->host = NULL; + } else { + memcpy(new_host, host+1, len); + new_host[len] = '\0'; + uri->host = new_host; + } + uri->flags |= _EVHTTP_URI_HOST_HAS_BRACKETS; + } else { + URI_SET_STR_(host); + uri->flags &= ~_EVHTTP_URI_HOST_HAS_BRACKETS; + } + return 0; } int diff --git a/include/event2/http.h b/include/event2/http.h index aef8a45b..d32bd0b3 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -1378,6 +1378,15 @@ struct evhttp_uri *evhttp_uri_parse_with_flags(const char *source_uri, * */ #define EVHTTP_URI_NONCONFORMANT 0x01 +/** + * Strip brackets from the IPv6 address and only for evhttp_uri_get_host(), + * evhttp_uri_join() returns the host with brackets. + * + * Thus you can use host part of the evhttp_uri for getaddrinfo(). + * + * @see also _EVHTTP_URI_HOST_HAS_BRACKETS + */ +#define EVHTTP_URI_HOST_STRIP_BRACKETS 0x04 /** Alias for evhttp_uri_parse_with_flags(source_uri, 0) */ EVENT2_EXPORT_SYMBOL diff --git a/sample/http-server.c b/sample/http-server.c index cf928437..b006891e 100644 --- a/sample/http-server.c +++ b/sample/http-server.c @@ -107,6 +107,7 @@ struct options { int unlink; const char *unixsock; + const char *bind; const char *docroot; }; @@ -351,6 +352,7 @@ print_usage(FILE *out, const char *prog, int exit_code) "Syntax: %s [ OPTS ] \n" " -p - port\n" " -U - bind to unix socket\n" + " -H - address to bind (default: 0.0.0.0)\n" " -u - unlink unix socket before bind\n" " -I - IOCP\n" " -m - max body size\n" @@ -365,7 +367,7 @@ parse_opts(int argc, char **argv) memset(&o, 0, sizeof(o)); - while ((opt = getopt(argc, argv, "hp:U:m:uIv")) != -1) { + while ((opt = getopt(argc, argv, "hp:U:m:uIvH:")) != -1) { switch (opt) { case 'p': o.port = atoi(optarg); break; case 'U': o.unixsock = optarg; break; @@ -373,6 +375,7 @@ parse_opts(int argc, char **argv) case 'I': o.iocp = 1; break; case 'm': o.max_body_size = atoi(optarg); break; case 'v': ++o.verbose; break; + case 'H': o.bind = optarg; break; case 'h': print_usage(stdout, argv[0], 0); break; default : fprintf(stderr, "Unknown option %c\n", opt); break; } @@ -548,9 +551,9 @@ main(int argc, char **argv) #endif /* EVENT__HAVE_STRUCT_SOCKADDR_UN */ } else { - handle = evhttp_bind_socket_with_handle(http, "0.0.0.0", o.port); + handle = evhttp_bind_socket_with_handle(http, o.bind, o.port); if (!handle) { - fprintf(stderr, "couldn't bind to port %d. Exiting.\n", o.port); + fprintf(stderr, "couldn't bind to %s:%d. Exiting.\n", o.bind, o.port); ret = 1; goto err; } diff --git a/sample/https-client.c b/sample/https-client.c index bfb405e0..733655fb 100644 --- a/sample/https-client.c +++ b/sample/https-client.c @@ -56,6 +56,8 @@ #endif static int ignore_cert = 0; +static int ipv6 = 0; +static int ipv4 = 0; static void http_request_done(struct evhttp_request *req, void *ctx) @@ -110,7 +112,7 @@ static void syntax(void) { fputs("Syntax:\n", stderr); - fputs(" https-client -url [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr); + fputs(" https-client -url [-data data-file.bin] [-ignore-cert] [-4] [-6] [-retries num] [-timeout sec] [-crt crt]\n", stderr); fputs("Example:\n", stderr); fputs(" https-client -url https://ip.appspot.com/\n", stderr); } @@ -225,7 +227,7 @@ add_cert_for_store(X509_STORE *store, const char *name) sys_store = CertOpenSystemStore(0, name); if (!sys_store) { - err("failed to open system certificate store"); + err("failed to open system certificate store\n"); return -1; } while ((ctx = CertEnumCertificatesInStore(sys_store, ctx))) { @@ -305,6 +307,10 @@ main(int argc, char **argv) } } else if (!strcmp("-ignore-cert", argv[i])) { ignore_cert = 1; + } else if (!strcmp("-4", argv[i])) { + ipv4 = 1; + } else if (!strcmp("-6", argv[i])) { + ipv6 = 1; } else if (!strcmp("-data", argv[i])) { if (i < argc - 1) { data_file = argv[i + 1]; @@ -355,20 +361,20 @@ main(int argc, char **argv) http_uri = evhttp_uri_parse(url); if (http_uri == NULL) { - err("malformed url"); + err("malformed url\n"); goto error; } scheme = evhttp_uri_get_scheme(http_uri); if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && strcasecmp(scheme, "http") != 0)) { - err("url must be http or https"); + err("url must be http or https\n"); goto error; } host = evhttp_uri_get_host(http_uri); if (host == NULL) { - err("url must have a host"); + err("url must have a host\n"); goto error; } @@ -542,6 +548,13 @@ main(int argc, char **argv) goto error; } + if (ipv4) { + evhttp_connection_set_family(evcon, AF_INET); + } + if (ipv6) { + evhttp_connection_set_family(evcon, AF_INET6); + } + if (retries > 0) { evhttp_connection_set_retries(evcon, retries); } diff --git a/test/regress_http.c b/test/regress_http.c index b952ff47..959fcc28 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -2835,6 +2835,8 @@ http_parse_uri_test(void *ptr) nonconform ? EVHTTP_URI_NONCONFORMANT : 0; struct evhttp_uri *uri = NULL; char url_tmp[4096]; +#define URI_PARSE_FLAGS(uri, flags) \ + evhttp_uri_parse_with_flags((uri), flags) #define URI_PARSE(uri) \ evhttp_uri_parse_with_flags((uri), parse_flags) @@ -3205,6 +3207,29 @@ http_parse_uri_test(void *ptr) tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0); TT_URI("#fr?ed"); evhttp_uri_free(uri); + + // EVHTTP_URI_HOST_STRIP_BRACKETS + uri = URI_PARSE_FLAGS("ftp://[ff00::127.0.0.1]/?q=test", EVHTTP_URI_HOST_STRIP_BRACKETS); + tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0); + tt_want(strcmp(evhttp_uri_get_host(uri), "ff00::127.0.0.1") == 0); + tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0); + tt_want(strcmp(evhttp_uri_get_query(uri), "q=test") == 0); + tt_want(evhttp_uri_get_userinfo(uri) == NULL); + tt_want(evhttp_uri_get_port(uri) == -1); + tt_want(evhttp_uri_get_fragment(uri) == NULL); + TT_URI("ftp://[ff00::127.0.0.1]/?q=test"); + + tt_want(0 == evhttp_uri_set_host(uri, "foo")); + tt_want(strcmp(evhttp_uri_get_host(uri), "foo") == 0); + TT_URI("ftp://foo/?q=test"); + + tt_want(0 == evhttp_uri_set_host(uri, "[ff00::127.0.0.1]")); + tt_want(strcmp(evhttp_uri_get_host(uri), "ff00::127.0.0.1") == 0); + TT_URI("ftp://[ff00::127.0.0.1]/?q=test"); + + evhttp_uri_free(uri); + +#undef URI_PARSE_FLAGS #undef URI_PARSE #undef TT_URI #undef BAD