]>
Commit | Line | Data |
---|---|---|
29f178bd DDO |
1 | /* |
2 | * Copyright 2001-2020 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 <openssl/http.h> | |
11 | #include <openssl/httperr.h> | |
12 | #include <openssl/err.h> | |
13 | #include <string.h> | |
4b1fe471 | 14 | #include "internal/cryptlib.h" /* for ossl_assert() */ |
29f178bd | 15 | |
afe554c2 DDO |
16 | #include "http_local.h" |
17 | ||
29f178bd DDO |
18 | /* |
19 | * Parse a URL and split it up into host, port and path components and | |
20 | * whether it indicates SSL/TLS. Return 1 on success, 0 on error. | |
21 | */ | |
22 | ||
23 | int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport, | |
d7fcee3b | 24 | int *pport_num, char **ppath, int *pssl) |
29f178bd DDO |
25 | { |
26 | char *p, *buf; | |
d7fcee3b DDO |
27 | char *host, *host_end; |
28 | const char *path, *port = OSSL_HTTP_PORT; | |
29 | long portnum = 80; | |
29f178bd DDO |
30 | |
31 | if (phost != NULL) | |
32 | *phost = NULL; | |
33 | if (pport != NULL) | |
34 | *pport = NULL; | |
35 | if (ppath != NULL) | |
36 | *ppath = NULL; | |
37 | if (pssl != NULL) | |
38 | *pssl = 0; | |
39 | ||
d7fcee3b | 40 | if (url == NULL) { |
9311d0c4 | 41 | ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); |
d7fcee3b DDO |
42 | return 0; |
43 | } | |
44 | ||
29f178bd DDO |
45 | /* dup the buffer since we are going to mess with it */ |
46 | if ((buf = OPENSSL_strdup(url)) == NULL) | |
47 | goto err; | |
48 | ||
d7fcee3b DDO |
49 | /* check for optional prefix "http[s]://" */ |
50 | p = strstr(buf, "://"); | |
51 | if (p == NULL) { | |
29f178bd DDO |
52 | p = buf; |
53 | } else { | |
d7fcee3b | 54 | *p = '\0'; /* p points to end of scheme name */ |
4b1fe471 | 55 | if (strcmp(buf, OSSL_HTTPS_NAME) == 0) { |
29f178bd DDO |
56 | if (pssl != NULL) |
57 | *pssl = 1; | |
4b1fe471 | 58 | port = OSSL_HTTPS_PORT; |
d7fcee3b | 59 | portnum = 443; |
4b1fe471 | 60 | } else if (strcmp(buf, OSSL_HTTP_NAME) != 0) { |
9311d0c4 | 61 | ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PREFIX); |
d7fcee3b | 62 | goto err; |
29f178bd | 63 | } |
d7fcee3b | 64 | p += 3; |
29f178bd DDO |
65 | } |
66 | host = p; | |
67 | ||
d7fcee3b | 68 | /* parse host name/address as far as needed here */ |
29f178bd | 69 | if (host[0] == '[') { |
d7fcee3b | 70 | /* ipv6 literal, which may include ':' */ |
29f178bd | 71 | host++; |
d7fcee3b DDO |
72 | host_end = strchr(host, ']'); |
73 | if (host_end == NULL) | |
29f178bd | 74 | goto parse_err; |
d7fcee3b DDO |
75 | *host_end++ = '\0'; |
76 | } else { | |
77 | host_end = strchr(host, ':'); /* look for start of optional port */ | |
78 | if (host_end == NULL) | |
79 | host_end = strchr(host, '/'); /* look for start of optional path */ | |
80 | if (host_end == NULL) | |
81 | /* the remaining string is just the hostname */ | |
82 | host_end = host + strlen(host); | |
29f178bd DDO |
83 | } |
84 | ||
d7fcee3b DDO |
85 | /* parse optional port specification starting with ':' */ |
86 | p = host_end; | |
87 | if (*p == ':') { | |
88 | port = ++p; | |
89 | if (pport_num == NULL) { | |
90 | p = strchr(port, '/'); | |
91 | if (p == NULL) | |
bde4aa8d | 92 | p = host_end + 1 + strlen(port); |
d7fcee3b DDO |
93 | } else { /* make sure a numerical port value is given */ |
94 | portnum = strtol(port, &p, 10); | |
95 | if (p == port || (*p != '\0' && *p != '/')) | |
96 | goto parse_err; | |
97 | if (portnum <= 0 || portnum >= 65536) { | |
9311d0c4 | 98 | ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER); |
d7fcee3b DDO |
99 | goto err; |
100 | } | |
101 | } | |
102 | } | |
103 | *host_end = '\0'; | |
104 | *p = '\0'; /* terminate port string */ | |
105 | ||
106 | /* check for optional path at end of url starting with '/' */ | |
107 | path = url + (p - buf); | |
108 | /* cannot use p + 1 because *p is '\0' and path must start with '/' */ | |
109 | if (*path == '\0') { | |
110 | path = "/"; | |
111 | } else if (*path != '/') { | |
9311d0c4 | 112 | ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PATH); |
d7fcee3b | 113 | goto parse_err; |
29f178bd | 114 | } |
d7fcee3b | 115 | |
29f178bd DDO |
116 | if (phost != NULL && (*phost = OPENSSL_strdup(host)) == NULL) |
117 | goto err; | |
118 | if (pport != NULL && (*pport = OPENSSL_strdup(port)) == NULL) | |
119 | goto err; | |
d7fcee3b DDO |
120 | if (pport_num != NULL) |
121 | *pport_num = (int)portnum; | |
122 | if (ppath != NULL && (*ppath = OPENSSL_strdup(path)) == NULL) | |
123 | goto err; | |
29f178bd DDO |
124 | |
125 | OPENSSL_free(buf); | |
126 | return 1; | |
127 | ||
128 | parse_err: | |
9311d0c4 | 129 | ERR_raise(ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL); |
29f178bd DDO |
130 | |
131 | err: | |
132 | if (ppath != NULL) { | |
133 | OPENSSL_free(*ppath); | |
134 | *ppath = NULL; | |
135 | } | |
136 | if (pport != NULL) { | |
137 | OPENSSL_free(*pport); | |
138 | *pport = NULL; | |
139 | } | |
140 | if (phost != NULL) { | |
141 | OPENSSL_free(*phost); | |
142 | *phost = NULL; | |
143 | } | |
144 | OPENSSL_free(buf); | |
145 | return 0; | |
146 | } | |
afe554c2 DDO |
147 | |
148 | int http_use_proxy(const char *no_proxy, const char *server) | |
149 | { | |
4b1fe471 | 150 | size_t sl; |
afe554c2 DDO |
151 | const char *found = NULL; |
152 | ||
4b1fe471 DDO |
153 | if (!ossl_assert(server != NULL)) |
154 | return 0; | |
155 | sl = strlen(server); | |
156 | ||
157 | /* | |
158 | * using environment variable names, both lowercase and uppercase variants, | |
159 | * compatible with other HTTP client implementations like wget, curl and git | |
160 | */ | |
afe554c2 DDO |
161 | if (no_proxy == NULL) |
162 | no_proxy = getenv("no_proxy"); | |
163 | if (no_proxy == NULL) | |
4b1fe471 | 164 | no_proxy = getenv(OPENSSL_NO_PROXY); |
afe554c2 DDO |
165 | if (no_proxy != NULL) |
166 | found = strstr(no_proxy, server); | |
167 | while (found != NULL | |
168 | && ((found != no_proxy && found[-1] != ' ' && found[-1] != ',') | |
169 | || (found[sl] != '\0' && found[sl] != ' ' && found[sl] != ','))) | |
170 | found = strstr(found + 1, server); | |
171 | return found == NULL; | |
172 | } | |
173 | ||
174 | const char *http_adapt_proxy(const char *proxy, const char *no_proxy, | |
175 | const char *server, int use_ssl) | |
176 | { | |
4b1fe471 DDO |
177 | const int http_len = strlen(OSSL_HTTP_PREFIX); |
178 | const int https_len = strlen(OSSL_HTTPS_PREFIX); | |
afe554c2 | 179 | |
4b1fe471 DDO |
180 | /* |
181 | * using environment variable names, both lowercase and uppercase variants, | |
182 | * compatible with other HTTP client implementations like wget, curl and git | |
183 | */ | |
afe554c2 DDO |
184 | if (proxy == NULL) |
185 | proxy = getenv(use_ssl ? "https_proxy" : "http_proxy"); | |
186 | if (proxy == NULL) | |
4b1fe471 DDO |
187 | proxy = getenv(use_ssl ? OPENSSL_HTTP_PROXY : |
188 | OPENSSL_HTTPS_PROXY); | |
189 | if (proxy == NULL) | |
190 | return NULL; | |
191 | ||
192 | /* skip any leading "http://" or "https://" */ | |
193 | if (strncmp(proxy, OSSL_HTTP_PREFIX, http_len) == 0) | |
194 | proxy += http_len; | |
195 | else if (strncmp(proxy, OSSL_HTTPS_PREFIX, https_len) == 0) | |
196 | proxy += https_len; | |
197 | ||
198 | if (*proxy == '\0' || !http_use_proxy(no_proxy, server)) | |
199 | return NULL; | |
afe554c2 DDO |
200 | return proxy; |
201 | } |