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