diff options
Diffstat (limited to 'crypto/http/http_lib.c')
-rw-r--r-- | crypto/http/http_lib.c | 191 |
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; } |