/* * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include #include "internal/cryptlib.h" /* for ossl_assert() */ #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. */ int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport, int *pport_num, char **ppath, int *pssl) { 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 (pssl != NULL) *pssl = 0; if (url == NULL) { HTTPerr(0, 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, "://"); if (p == NULL) { p = buf; } else { *p = '\0'; /* p points to end of scheme name */ if (strcmp(buf, OSSL_HTTPS_NAME) == 0) { if (pssl != NULL) *pssl = 1; port = OSSL_HTTPS_PORT; portnum = 443; } else if (strcmp(buf, OSSL_HTTP_NAME) != 0) { HTTPerr(0, HTTP_R_INVALID_URL_PREFIX); goto err; } p += 3; } host = p; /* parse host name/address as far as needed here */ if (host[0] == '[') { /* ipv6 literal, which may include ':' */ host++; host_end = strchr(host, ']'); if (host_end == NULL) goto parse_err; *host_end++ = '\0'; } else { host_end = strchr(host, ':'); /* look for start of optional port */ if (host_end == NULL) host_end = strchr(host, '/'); /* look for start of optional path */ if (host_end == NULL) /* the remaining string is just the hostname */ host_end = host + strlen(host); } /* parse optional port specification starting with ':' */ p = host_end; 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) { HTTPerr(0, 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 != '/') { HTTPerr(0, HTTP_R_INVALID_URL_PATH); goto parse_err; } if (phost != NULL && (*phost = OPENSSL_strdup(host)) == NULL) goto err; if (pport != NULL && (*pport = OPENSSL_strdup(port)) == NULL) goto err; if (pport_num != NULL) *pport_num = (int)portnum; if (ppath != NULL && (*ppath = OPENSSL_strdup(path)) == NULL) goto err; OPENSSL_free(buf); return 1; parse_err: HTTPerr(0, 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); return 0; } int http_use_proxy(const char *no_proxy, const char *server) { size_t sl; const char *found = NULL; if (!ossl_assert(server != NULL)) return 0; sl = strlen(server); /* * using environment variable names, both lowercase and uppercase variants, * compatible with other HTTP client implementations like wget, curl and git */ if (no_proxy == NULL) no_proxy = getenv("no_proxy"); if (no_proxy == NULL) no_proxy = getenv(OPENSSL_NO_PROXY); if (no_proxy != NULL) found = strstr(no_proxy, server); while (found != NULL && ((found != no_proxy && found[-1] != ' ' && found[-1] != ',') || (found[sl] != '\0' && found[sl] != ' ' && found[sl] != ','))) found = strstr(found + 1, server); return found == NULL; } const char *http_adapt_proxy(const char *proxy, const char *no_proxy, const char *server, int use_ssl) { const int http_len = strlen(OSSL_HTTP_PREFIX); const int https_len = strlen(OSSL_HTTPS_PREFIX); /* * using environment variable names, both lowercase and uppercase variants, * compatible with other HTTP client implementations like wget, curl and git */ if (proxy == NULL) proxy = getenv(use_ssl ? "https_proxy" : "http_proxy"); if (proxy == NULL) proxy = getenv(use_ssl ? OPENSSL_HTTP_PROXY : OPENSSL_HTTPS_PROXY); if (proxy == NULL) return NULL; /* skip any leading "http://" or "https://" */ if (strncmp(proxy, OSSL_HTTP_PREFIX, http_len) == 0) proxy += http_len; else if (strncmp(proxy, OSSL_HTTPS_PREFIX, https_len) == 0) proxy += https_len; if (*proxy == '\0' || !http_use_proxy(no_proxy, server)) return NULL; return proxy; }