1e1051a39Sopenharmony_ci/* 2e1051a39Sopenharmony_ci * Copyright 2001-2023 The OpenSSL Project Authors. All Rights Reserved. 3e1051a39Sopenharmony_ci * 4e1051a39Sopenharmony_ci * Licensed under the Apache License 2.0 (the "License"). You may not use 5e1051a39Sopenharmony_ci * this file except in compliance with the License. You can obtain a copy 6e1051a39Sopenharmony_ci * in the file LICENSE in the source distribution or at 7e1051a39Sopenharmony_ci * https://www.openssl.org/source/license.html 8e1051a39Sopenharmony_ci */ 9e1051a39Sopenharmony_ci 10e1051a39Sopenharmony_ci#include <stdio.h> /* for sscanf() */ 11e1051a39Sopenharmony_ci#include <string.h> 12e1051a39Sopenharmony_ci#include <openssl/http.h> 13e1051a39Sopenharmony_ci#include <openssl/httperr.h> 14e1051a39Sopenharmony_ci#include <openssl/bio.h> /* for BIO_snprintf() */ 15e1051a39Sopenharmony_ci#include <openssl/err.h> 16e1051a39Sopenharmony_ci#include "internal/cryptlib.h" /* for ossl_assert() */ 17e1051a39Sopenharmony_ci 18e1051a39Sopenharmony_cistatic void init_pstring(char **pstr) 19e1051a39Sopenharmony_ci{ 20e1051a39Sopenharmony_ci if (pstr != NULL) { 21e1051a39Sopenharmony_ci *pstr = NULL; 22e1051a39Sopenharmony_ci } 23e1051a39Sopenharmony_ci} 24e1051a39Sopenharmony_ci 25e1051a39Sopenharmony_cistatic int copy_substring(char **dest, const char *start, const char *end) 26e1051a39Sopenharmony_ci{ 27e1051a39Sopenharmony_ci return dest == NULL 28e1051a39Sopenharmony_ci || (*dest = OPENSSL_strndup(start, end - start)) != NULL; 29e1051a39Sopenharmony_ci} 30e1051a39Sopenharmony_ci 31e1051a39Sopenharmony_cistatic void free_pstring(char **pstr) 32e1051a39Sopenharmony_ci{ 33e1051a39Sopenharmony_ci if (pstr != NULL) { 34e1051a39Sopenharmony_ci OPENSSL_free(*pstr); 35e1051a39Sopenharmony_ci *pstr = NULL; 36e1051a39Sopenharmony_ci } 37e1051a39Sopenharmony_ci} 38e1051a39Sopenharmony_ci 39e1051a39Sopenharmony_ciint OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost, 40e1051a39Sopenharmony_ci char **pport, int *pport_num, 41e1051a39Sopenharmony_ci char **ppath, char **pquery, char **pfrag) 42e1051a39Sopenharmony_ci{ 43e1051a39Sopenharmony_ci const char *p, *tmp; 44e1051a39Sopenharmony_ci const char *scheme, *scheme_end; 45e1051a39Sopenharmony_ci const char *user, *user_end; 46e1051a39Sopenharmony_ci const char *host, *host_end; 47e1051a39Sopenharmony_ci const char *port, *port_end; 48e1051a39Sopenharmony_ci unsigned int portnum; 49e1051a39Sopenharmony_ci const char *path, *path_end; 50e1051a39Sopenharmony_ci const char *query, *query_end; 51e1051a39Sopenharmony_ci const char *frag, *frag_end; 52e1051a39Sopenharmony_ci 53e1051a39Sopenharmony_ci init_pstring(pscheme); 54e1051a39Sopenharmony_ci init_pstring(puser); 55e1051a39Sopenharmony_ci init_pstring(phost); 56e1051a39Sopenharmony_ci init_pstring(pport); 57e1051a39Sopenharmony_ci init_pstring(ppath); 58e1051a39Sopenharmony_ci init_pstring(pfrag); 59e1051a39Sopenharmony_ci init_pstring(pquery); 60e1051a39Sopenharmony_ci 61e1051a39Sopenharmony_ci if (url == NULL) { 62e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); 63e1051a39Sopenharmony_ci return 0; 64e1051a39Sopenharmony_ci } 65e1051a39Sopenharmony_ci 66e1051a39Sopenharmony_ci /* check for optional prefix "<scheme>://" */ 67e1051a39Sopenharmony_ci scheme = scheme_end = url; 68e1051a39Sopenharmony_ci p = strstr(url, "://"); 69e1051a39Sopenharmony_ci if (p == NULL) { 70e1051a39Sopenharmony_ci p = url; 71e1051a39Sopenharmony_ci } else { 72e1051a39Sopenharmony_ci scheme_end = p; 73e1051a39Sopenharmony_ci if (scheme_end == scheme) 74e1051a39Sopenharmony_ci goto parse_err; 75e1051a39Sopenharmony_ci p += strlen("://"); 76e1051a39Sopenharmony_ci } 77e1051a39Sopenharmony_ci 78e1051a39Sopenharmony_ci /* parse optional "userinfo@" */ 79e1051a39Sopenharmony_ci user = user_end = host = p; 80e1051a39Sopenharmony_ci host = strchr(p, '@'); 81e1051a39Sopenharmony_ci if (host != NULL) 82e1051a39Sopenharmony_ci user_end = host++; 83e1051a39Sopenharmony_ci else 84e1051a39Sopenharmony_ci host = p; 85e1051a39Sopenharmony_ci 86e1051a39Sopenharmony_ci /* parse host name/address as far as needed here */ 87e1051a39Sopenharmony_ci if (host[0] == '[') { 88e1051a39Sopenharmony_ci /* ipv6 literal, which may include ':' */ 89e1051a39Sopenharmony_ci host_end = strchr(host + 1, ']'); 90e1051a39Sopenharmony_ci if (host_end == NULL) 91e1051a39Sopenharmony_ci goto parse_err; 92e1051a39Sopenharmony_ci p = ++host_end; 93e1051a39Sopenharmony_ci } else { 94e1051a39Sopenharmony_ci /* look for start of optional port, path, query, or fragment */ 95e1051a39Sopenharmony_ci host_end = strchr(host, ':'); 96e1051a39Sopenharmony_ci if (host_end == NULL) 97e1051a39Sopenharmony_ci host_end = strchr(host, '/'); 98e1051a39Sopenharmony_ci if (host_end == NULL) 99e1051a39Sopenharmony_ci host_end = strchr(host, '?'); 100e1051a39Sopenharmony_ci if (host_end == NULL) 101e1051a39Sopenharmony_ci host_end = strchr(host, '#'); 102e1051a39Sopenharmony_ci if (host_end == NULL) /* the remaining string is just the hostname */ 103e1051a39Sopenharmony_ci host_end = host + strlen(host); 104e1051a39Sopenharmony_ci p = host_end; 105e1051a39Sopenharmony_ci } 106e1051a39Sopenharmony_ci 107e1051a39Sopenharmony_ci /* parse optional port specification starting with ':' */ 108e1051a39Sopenharmony_ci port = "0"; /* default */ 109e1051a39Sopenharmony_ci if (*p == ':') 110e1051a39Sopenharmony_ci port = ++p; 111e1051a39Sopenharmony_ci /* remaining port spec handling is also done for the default values */ 112e1051a39Sopenharmony_ci /* make sure a decimal port number is given */ 113e1051a39Sopenharmony_ci if (!sscanf(port, "%u", &portnum) || portnum > 65535) { 114e1051a39Sopenharmony_ci ERR_raise_data(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER, "%s", port); 115e1051a39Sopenharmony_ci goto err; 116e1051a39Sopenharmony_ci } 117e1051a39Sopenharmony_ci for (port_end = port; '0' <= *port_end && *port_end <= '9'; port_end++) 118e1051a39Sopenharmony_ci ; 119e1051a39Sopenharmony_ci if (port == p) /* port was given explicitly */ 120e1051a39Sopenharmony_ci p += port_end - port; 121e1051a39Sopenharmony_ci 122e1051a39Sopenharmony_ci /* check for optional path starting with '/' or '?'. Else must start '#' */ 123e1051a39Sopenharmony_ci path = p; 124e1051a39Sopenharmony_ci if (*path != '\0' && *path != '/' && *path != '?' && *path != '#') { 125e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PATH); 126e1051a39Sopenharmony_ci goto parse_err; 127e1051a39Sopenharmony_ci } 128e1051a39Sopenharmony_ci path_end = query = query_end = frag = frag_end = path + strlen(path); 129e1051a39Sopenharmony_ci 130e1051a39Sopenharmony_ci /* parse optional "?query" */ 131e1051a39Sopenharmony_ci tmp = strchr(p, '?'); 132e1051a39Sopenharmony_ci if (tmp != NULL) { 133e1051a39Sopenharmony_ci p = tmp; 134e1051a39Sopenharmony_ci if (pquery != NULL) { 135e1051a39Sopenharmony_ci path_end = p; 136e1051a39Sopenharmony_ci query = p + 1; 137e1051a39Sopenharmony_ci } 138e1051a39Sopenharmony_ci } 139e1051a39Sopenharmony_ci 140e1051a39Sopenharmony_ci /* parse optional "#fragment" */ 141e1051a39Sopenharmony_ci tmp = strchr(p, '#'); 142e1051a39Sopenharmony_ci if (tmp != NULL) { 143e1051a39Sopenharmony_ci if (query == path_end) /* we did not record a query component */ 144e1051a39Sopenharmony_ci path_end = tmp; 145e1051a39Sopenharmony_ci query_end = tmp; 146e1051a39Sopenharmony_ci frag = tmp + 1; 147e1051a39Sopenharmony_ci } 148e1051a39Sopenharmony_ci 149e1051a39Sopenharmony_ci if (!copy_substring(pscheme, scheme, scheme_end) 150e1051a39Sopenharmony_ci || !copy_substring(phost, host, host_end) 151e1051a39Sopenharmony_ci || !copy_substring(pport, port, port_end) 152e1051a39Sopenharmony_ci || !copy_substring(puser, user, user_end) 153e1051a39Sopenharmony_ci || !copy_substring(pquery, query, query_end) 154e1051a39Sopenharmony_ci || !copy_substring(pfrag, frag, frag_end)) 155e1051a39Sopenharmony_ci goto err; 156e1051a39Sopenharmony_ci if (pport_num != NULL) 157e1051a39Sopenharmony_ci *pport_num = (int)portnum; 158e1051a39Sopenharmony_ci if (*path == '/') { 159e1051a39Sopenharmony_ci if (!copy_substring(ppath, path, path_end)) 160e1051a39Sopenharmony_ci goto err; 161e1051a39Sopenharmony_ci } else if (ppath != NULL) { /* must prepend '/' */ 162e1051a39Sopenharmony_ci size_t buflen = 1 + path_end - path + 1; 163e1051a39Sopenharmony_ci 164e1051a39Sopenharmony_ci if ((*ppath = OPENSSL_malloc(buflen)) == NULL) 165e1051a39Sopenharmony_ci goto err; 166e1051a39Sopenharmony_ci BIO_snprintf(*ppath, buflen, "/%s", path); 167e1051a39Sopenharmony_ci } 168e1051a39Sopenharmony_ci return 1; 169e1051a39Sopenharmony_ci 170e1051a39Sopenharmony_ci parse_err: 171e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL); 172e1051a39Sopenharmony_ci 173e1051a39Sopenharmony_ci err: 174e1051a39Sopenharmony_ci free_pstring(pscheme); 175e1051a39Sopenharmony_ci free_pstring(puser); 176e1051a39Sopenharmony_ci free_pstring(phost); 177e1051a39Sopenharmony_ci free_pstring(pport); 178e1051a39Sopenharmony_ci free_pstring(ppath); 179e1051a39Sopenharmony_ci free_pstring(pquery); 180e1051a39Sopenharmony_ci free_pstring(pfrag); 181e1051a39Sopenharmony_ci return 0; 182e1051a39Sopenharmony_ci} 183e1051a39Sopenharmony_ci 184e1051a39Sopenharmony_ciint OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost, 185e1051a39Sopenharmony_ci char **pport, int *pport_num, 186e1051a39Sopenharmony_ci char **ppath, char **pquery, char **pfrag) 187e1051a39Sopenharmony_ci{ 188e1051a39Sopenharmony_ci char *scheme, *port; 189e1051a39Sopenharmony_ci int ssl = 0, portnum; 190e1051a39Sopenharmony_ci 191e1051a39Sopenharmony_ci init_pstring(pport); 192e1051a39Sopenharmony_ci if (pssl != NULL) 193e1051a39Sopenharmony_ci *pssl = 0; 194e1051a39Sopenharmony_ci if (!OSSL_parse_url(url, &scheme, puser, phost, &port, pport_num, 195e1051a39Sopenharmony_ci ppath, pquery, pfrag)) 196e1051a39Sopenharmony_ci return 0; 197e1051a39Sopenharmony_ci 198e1051a39Sopenharmony_ci /* check for optional HTTP scheme "http[s]" */ 199e1051a39Sopenharmony_ci if (strcmp(scheme, OSSL_HTTPS_NAME) == 0) { 200e1051a39Sopenharmony_ci ssl = 1; 201e1051a39Sopenharmony_ci if (pssl != NULL) 202e1051a39Sopenharmony_ci *pssl = ssl; 203e1051a39Sopenharmony_ci } else if (*scheme != '\0' && strcmp(scheme, OSSL_HTTP_NAME) != 0) { 204e1051a39Sopenharmony_ci ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_SCHEME); 205e1051a39Sopenharmony_ci OPENSSL_free(scheme); 206e1051a39Sopenharmony_ci OPENSSL_free(port); 207e1051a39Sopenharmony_ci goto err; 208e1051a39Sopenharmony_ci } 209e1051a39Sopenharmony_ci OPENSSL_free(scheme); 210e1051a39Sopenharmony_ci 211e1051a39Sopenharmony_ci if (strcmp(port, "0") == 0) { 212e1051a39Sopenharmony_ci /* set default port */ 213e1051a39Sopenharmony_ci OPENSSL_free(port); 214e1051a39Sopenharmony_ci port = ssl ? OSSL_HTTPS_PORT : OSSL_HTTP_PORT; 215e1051a39Sopenharmony_ci if (!ossl_assert(sscanf(port, "%d", &portnum) == 1)) 216e1051a39Sopenharmony_ci goto err; 217e1051a39Sopenharmony_ci if (pport_num != NULL) 218e1051a39Sopenharmony_ci *pport_num = portnum; 219e1051a39Sopenharmony_ci if (pport != NULL) { 220e1051a39Sopenharmony_ci *pport = OPENSSL_strdup(port); 221e1051a39Sopenharmony_ci if (*pport == NULL) 222e1051a39Sopenharmony_ci goto err; 223e1051a39Sopenharmony_ci } 224e1051a39Sopenharmony_ci } else { 225e1051a39Sopenharmony_ci if (pport != NULL) 226e1051a39Sopenharmony_ci *pport = port; 227e1051a39Sopenharmony_ci else 228e1051a39Sopenharmony_ci OPENSSL_free(port); 229e1051a39Sopenharmony_ci } 230e1051a39Sopenharmony_ci return 1; 231e1051a39Sopenharmony_ci 232e1051a39Sopenharmony_ci err: 233e1051a39Sopenharmony_ci free_pstring(puser); 234e1051a39Sopenharmony_ci free_pstring(phost); 235e1051a39Sopenharmony_ci free_pstring(ppath); 236e1051a39Sopenharmony_ci free_pstring(pquery); 237e1051a39Sopenharmony_ci free_pstring(pfrag); 238e1051a39Sopenharmony_ci return 0; 239e1051a39Sopenharmony_ci} 240e1051a39Sopenharmony_ci 241e1051a39Sopenharmony_ci/* Respect no_proxy, taking default value from environment variable(s) */ 242e1051a39Sopenharmony_cistatic int use_proxy(const char *no_proxy, const char *server) 243e1051a39Sopenharmony_ci{ 244e1051a39Sopenharmony_ci size_t sl; 245e1051a39Sopenharmony_ci const char *found = NULL; 246e1051a39Sopenharmony_ci 247e1051a39Sopenharmony_ci if (!ossl_assert(server != NULL)) 248e1051a39Sopenharmony_ci return 0; 249e1051a39Sopenharmony_ci sl = strlen(server); 250e1051a39Sopenharmony_ci 251e1051a39Sopenharmony_ci /* 252e1051a39Sopenharmony_ci * using environment variable names, both lowercase and uppercase variants, 253e1051a39Sopenharmony_ci * compatible with other HTTP client implementations like wget, curl and git 254e1051a39Sopenharmony_ci */ 255e1051a39Sopenharmony_ci if (no_proxy == NULL) 256e1051a39Sopenharmony_ci no_proxy = ossl_safe_getenv("no_proxy"); 257e1051a39Sopenharmony_ci if (no_proxy == NULL) 258e1051a39Sopenharmony_ci no_proxy = ossl_safe_getenv(OPENSSL_NO_PROXY); 259e1051a39Sopenharmony_ci 260e1051a39Sopenharmony_ci if (no_proxy != NULL) 261e1051a39Sopenharmony_ci found = strstr(no_proxy, server); 262e1051a39Sopenharmony_ci while (found != NULL 263e1051a39Sopenharmony_ci && ((found != no_proxy && found[-1] != ' ' && found[-1] != ',') 264e1051a39Sopenharmony_ci || (found[sl] != '\0' && found[sl] != ' ' && found[sl] != ','))) 265e1051a39Sopenharmony_ci found = strstr(found + 1, server); 266e1051a39Sopenharmony_ci return found == NULL; 267e1051a39Sopenharmony_ci} 268e1051a39Sopenharmony_ci 269e1051a39Sopenharmony_ci/* Take default value from environment variable(s), respect no_proxy */ 270e1051a39Sopenharmony_ciconst char *OSSL_HTTP_adapt_proxy(const char *proxy, const char *no_proxy, 271e1051a39Sopenharmony_ci const char *server, int use_ssl) 272e1051a39Sopenharmony_ci{ 273e1051a39Sopenharmony_ci /* 274e1051a39Sopenharmony_ci * using environment variable names, both lowercase and uppercase variants, 275e1051a39Sopenharmony_ci * compatible with other HTTP client implementations like wget, curl and git 276e1051a39Sopenharmony_ci */ 277e1051a39Sopenharmony_ci if (proxy == NULL) 278e1051a39Sopenharmony_ci proxy = ossl_safe_getenv(use_ssl ? "https_proxy" : "http_proxy"); 279e1051a39Sopenharmony_ci if (proxy == NULL) 280e1051a39Sopenharmony_ci proxy = ossl_safe_getenv(use_ssl ? OPENSSL_HTTP_PROXY : OPENSSL_HTTPS_PROXY); 281e1051a39Sopenharmony_ci 282e1051a39Sopenharmony_ci if (proxy == NULL || *proxy == '\0' || !use_proxy(no_proxy, server)) 283e1051a39Sopenharmony_ci return NULL; 284e1051a39Sopenharmony_ci return proxy; 285e1051a39Sopenharmony_ci} 286