summaryrefslogtreecommitdiffstats
path: root/crypto/http/http_lib.c
diff options
context:
space:
mode:
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>2021-01-28 22:10:47 +0100
committerDr. David von Oheimb <dev@ddvo.net>2021-03-01 10:30:43 +0100
commit7932982b88f5095f60397fe727d27ddf7234f4d6 (patch)
tree791fa288ead387d06147ae627169996e093c115d /crypto/http/http_lib.c
parente60e974414a7e637ff2f946dc2aa24c381a32cc2 (diff)
OSSL_HTTP_parse_url(): Handle any userinfo, query, and fragment components
Now handle [http[s]://][userinfo@]host[:port][/path][?query][#frag] by optionally providing any userinfo, query, and frag components. All usages of this function, which are client-only, silently ignore userinfo and frag components, while the query component is taken as part of the path. Update and extend the unit tests and all affected documentation. Document and deprecat OCSP_parse_url(). Fixes an issue that came up when discussing FR #14001. Reviewed-by: Richard Levitte <levitte@openssl.org> (Merged from https://github.com/openssl/openssl/pull/14009)
Diffstat (limited to 'crypto/http/http_lib.c')
-rw-r--r--crypto/http/http_lib.c191
1 files changed, 114 insertions, 77 deletions
diff --git a/crypto/http/http_lib.c b/crypto/http/http_lib.c
index 028ef12383..3c894d8629 100644
--- a/crypto/http/http_lib.c
+++ b/crypto/http/http_lib.c
@@ -15,55 +15,77 @@
#include "http_local.h"
-/*
- * Parse a URL and split it up into host, port and path components and
- * whether it indicates SSL/TLS. Return 1 on success, 0 on error.
- */
+static void init_pstring(char **pstr)
+{
+ if (pstr != NULL) {
+ *pstr = NULL;
+ }
+}
+
+static int copy_substring(char **dest, const char *start, const char *end)
+{
+ return dest == NULL
+ || (*dest = OPENSSL_strndup(start, end - start)) != NULL;
+}
-int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport,
- int *pport_num, char **ppath, int *pssl)
+static void free_pstring(char **pstr)
{
- char *p, *buf;
- char *host, *host_end;
- const char *path, *port = OSSL_HTTP_PORT;
- long portnum = 80;
-
- if (phost != NULL)
- *phost = NULL;
- if (pport != NULL)
- *pport = NULL;
- if (ppath != NULL)
- *ppath = NULL;
+ if (pstr != NULL) {
+ OPENSSL_free(*pstr);
+ *pstr = NULL;
+ }
+}
+
+int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
+ char **pport, int *pport_num,
+ char **ppath, char **pquery, char **pfrag)
+{
+ const char *p, *tmp;
+ const char *user, *user_end;
+ const char *host, *host_end;
+ const char *port = OSSL_HTTP_PORT, *port_end;
+ unsigned int portnum;
+ const char *path, *path_end;
+ const char *query, *query_end;
+ const char *frag, *frag_end;
+
if (pssl != NULL)
*pssl = 0;
+ init_pstring(puser);
+ init_pstring(phost);
+ init_pstring(pport);
+ init_pstring(ppath);
+ init_pstring(pfrag);
+ init_pstring(pquery);
if (url == NULL) {
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
- /* dup the buffer since we are going to mess with it */
- if ((buf = OPENSSL_strdup(url)) == NULL)
- goto err;
-
/* check for optional prefix "http[s]://" */
- p = strstr(buf, "://");
+ p = strstr(url, "://");
if (p == NULL) {
- p = buf;
- } else {
- *p = '\0'; /* p points to end of scheme name */
- if (strcmp(buf, OSSL_HTTPS_NAME) == 0) {
+ p = url;
+ } else { /* p points to end of scheme name */
+ if (strncmp(url, OSSL_HTTPS_NAME, strlen(OSSL_HTTPS_NAME)) == 0) {
if (pssl != NULL)
*pssl = 1;
port = OSSL_HTTPS_PORT;
- portnum = 443;
- } else if (strcmp(buf, OSSL_HTTP_NAME) != 0) {
+ } else if (strncmp(url, OSSL_HTTP_NAME, strlen(OSSL_HTTP_NAME)) != 0) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PREFIX);
goto err;
}
- p += 3;
+ p += strlen("://");
}
- host = p;
+
+ /* parse optional "userinfo@" */
+ user = user_end = host = p;
+ host = strchr(p, '@');
+ if (host != NULL)
+ user_end = host++;
+ else
+ host = p;
/* parse host name/address as far as needed here */
if (host[0] == '[') {
@@ -72,76 +94,91 @@ int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport,
host_end = strchr(host, ']');
if (host_end == NULL)
goto parse_err;
- *host_end++ = '\0';
+ p = host_end + 1;
} else {
- host_end = strchr(host, ':'); /* look for start of optional port */
+ /* look for start of optional port, path, query, or fragment */
+ host_end = strchr(host, ':');
+ if (host_end == NULL)
+ host_end = strchr(host, '/');
if (host_end == NULL)
- host_end = strchr(host, '/'); /* look for start of optional path */
+ host_end = strchr(host, '?');
if (host_end == NULL)
- /* the remaining string is just the hostname */
+ host_end = strchr(host, '#');
+ if (host_end == NULL) /* the remaining string is just the hostname */
host_end = host + strlen(host);
+ p = host_end;
}
/* parse optional port specification starting with ':' */
- p = host_end;
- if (*p == ':') {
+ if (*p == ':')
port = ++p;
- if (pport_num == NULL) {
- p = strchr(port, '/');
- if (p == NULL)
- p = host_end + 1 + strlen(port);
- } else { /* make sure a numerical port value is given */
- portnum = strtol(port, &p, 10);
- if (p == port || (*p != '\0' && *p != '/'))
- goto parse_err;
- if (portnum <= 0 || portnum >= 65536) {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER);
- goto err;
- }
- }
+ /* remaining port spec handling is also done for the default values */
+ /* make sure a decimal port number is given */
+ if (!sscanf(port, "%u", &portnum) || portnum < 1 || portnum > 65535) {
+ ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER);
+ goto err;
}
- *host_end = '\0';
- *p = '\0'; /* terminate port string */
-
- /* check for optional path at end of url starting with '/' */
- path = url + (p - buf);
- /* cannot use p + 1 because *p is '\0' and path must start with '/' */
- if (*path == '\0') {
- path = "/";
- } else if (*path != '/') {
+ for (port_end = port; '0' <= *port_end && *port_end <= '9'; port_end++)
+ ;
+ if (port == p) /* port was given explicitly */
+ p += port_end - port;
+
+ /* check for optional path starting with '/' or '?'. Else must start '#' */
+ path = p;
+ if (*path != '\0' && *path != '/' && *path != '?' && *path != '#') {
ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PATH);
goto parse_err;
}
+ path_end = query = query_end = frag = frag_end = path + strlen(path);
+
+ /* parse optional "?query" */
+ tmp = strchr(p, '?');
+ if (tmp != NULL) {
+ p = tmp;
+ if (pquery != NULL) {
+ path_end = p;
+ query = p + 1;
+ }
+ }
- if (phost != NULL && (*phost = OPENSSL_strdup(host)) == NULL)
- goto err;
- if (pport != NULL && (*pport = OPENSSL_strdup(port)) == NULL)
+ /* parse optional "#fragment" */
+ tmp = strchr(p, '#');
+ if (tmp != NULL) {
+ if (query == path_end) /* we did not record a query component */
+ path_end = tmp;
+ query_end = tmp;
+ frag = tmp + 1;
+ }
+
+ if (!copy_substring(phost, host, host_end)
+ || !copy_substring(pport, port, port_end)
+ || !copy_substring(puser, user, user_end)
+ || !copy_substring(pquery, query, query_end)
+ || !copy_substring(pfrag, frag, frag_end))
goto err;
if (pport_num != NULL)
*pport_num = (int)portnum;
- if (ppath != NULL && (*ppath = OPENSSL_strdup(path)) == NULL)
- goto err;
+ if (*path == '/') {
+ if (!copy_substring(ppath, path, path_end))
+ goto err;
+ } else if (ppath != NULL) { /* must prepend '/' */
+ size_t buflen = 1 + path_end - path + 1;
- OPENSSL_free(buf);
+ if ((*ppath = OPENSSL_malloc(buflen)) == NULL)
+ goto err;
+ snprintf(*ppath, buflen, "/%s", path);
+ }
return 1;
parse_err:
ERR_raise(ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL);
err:
- if (ppath != NULL) {
- OPENSSL_free(*ppath);
- *ppath = NULL;
- }
- if (pport != NULL) {
- OPENSSL_free(*pport);
- *pport = NULL;
- }
- if (phost != NULL) {
- OPENSSL_free(*phost);
- *phost = NULL;
- }
- OPENSSL_free(buf);
+ free_pstring(phost);
+ free_pstring(pport);
+ free_pstring(ppath);
+ free_pstring(pquery);
+ free_pstring(pfrag);
return 0;
}