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