2 * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved.
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
10 #include <openssl/http.h>
11 #include <openssl/httperr.h>
12 #include <openssl/err.h>
14 #include "internal/cryptlib.h" /* for ossl_assert() */
16 #include "http_local.h"
18 static void init_pstring(char **pstr
)
25 static int copy_substring(char **dest
, const char *start
, const char *end
)
28 || (*dest
= OPENSSL_strndup(start
, end
- start
)) != NULL
;
31 static void free_pstring(char **pstr
)
39 int OSSL_parse_url(const char *url
, char **pscheme
, char **puser
, char **phost
,
40 char **pport
, int *pport_num
,
41 char **ppath
, char **pquery
, char **pfrag
)
44 const char *scheme
, *scheme_end
;
45 const char *user
, *user_end
;
46 const char *host
, *host_end
;
47 const char *port
, *port_end
;
49 const char *path
, *path_end
;
50 const char *query
, *query_end
;
51 const char *frag
, *frag_end
;
53 init_pstring(pscheme
);
62 ERR_raise(ERR_LIB_HTTP
, ERR_R_PASSED_NULL_PARAMETER
);
66 /* check for optional prefix "<scheme>://" */
67 scheme
= scheme_end
= url
;
68 p
= strstr(url
, "://");
73 if (scheme_end
== scheme
)
78 /* parse optional "userinfo@" */
79 user
= user_end
= host
= p
;
80 host
= strchr(p
, '@');
86 /* parse host name/address as far as needed here */
88 /* ipv6 literal, which may include ':' */
90 host_end
= strchr(host
, ']');
95 /* look for start of optional port, path, query, or fragment */
96 host_end
= strchr(host
, ':');
98 host_end
= strchr(host
, '/');
100 host_end
= strchr(host
, '?');
101 if (host_end
== NULL
)
102 host_end
= strchr(host
, '#');
103 if (host_end
== NULL
) /* the remaining string is just the hostname */
104 host_end
= host
+ strlen(host
);
108 /* parse optional port specification starting with ':' */
109 port
= "0"; /* default */
112 /* remaining port spec handling is also done for the default values */
113 /* make sure a decimal port number is given */
114 if (!sscanf(port
, "%u", &portnum
) || portnum
> 65535) {
115 ERR_raise(ERR_LIB_HTTP
, HTTP_R_INVALID_PORT_NUMBER
);
118 for (port_end
= port
; '0' <= *port_end
&& *port_end
<= '9'; port_end
++)
120 if (port
== p
) /* port was given explicitly */
121 p
+= port_end
- port
;
123 /* check for optional path starting with '/' or '?'. Else must start '#' */
125 if (*path
!= '\0' && *path
!= '/' && *path
!= '?' && *path
!= '#') {
126 ERR_raise(ERR_LIB_HTTP
, HTTP_R_INVALID_URL_PATH
);
129 path_end
= query
= query_end
= frag
= frag_end
= path
+ strlen(path
);
131 /* parse optional "?query" */
132 tmp
= strchr(p
, '?');
135 if (pquery
!= NULL
) {
141 /* parse optional "#fragment" */
142 tmp
= strchr(p
, '#');
144 if (query
== path_end
) /* we did not record a query component */
150 if (!copy_substring(pscheme
, scheme
, scheme_end
)
151 || !copy_substring(phost
, host
, host_end
)
152 || !copy_substring(pport
, port
, port_end
)
153 || !copy_substring(puser
, user
, user_end
)
154 || !copy_substring(pquery
, query
, query_end
)
155 || !copy_substring(pfrag
, frag
, frag_end
))
157 if (pport_num
!= NULL
)
158 *pport_num
= (int)portnum
;
160 if (!copy_substring(ppath
, path
, path_end
))
162 } else if (ppath
!= NULL
) { /* must prepend '/' */
163 size_t buflen
= 1 + path_end
- path
+ 1;
165 if ((*ppath
= OPENSSL_malloc(buflen
)) == NULL
)
167 snprintf(*ppath
, buflen
, "/%s", path
);
172 ERR_raise(ERR_LIB_HTTP
, HTTP_R_ERROR_PARSING_URL
);
175 free_pstring(pscheme
);
180 free_pstring(pquery
);
185 int OSSL_HTTP_parse_url(const char *url
, int *pssl
, char **puser
, char **phost
,
186 char **pport
, int *pport_num
,
187 char **ppath
, char **pquery
, char **pfrag
)
190 int ssl
= 0, portnum
;
195 if (!OSSL_parse_url(url
, &scheme
, puser
, phost
, &port
, pport_num
,
196 ppath
, pquery
, pfrag
))
199 /* check for optional HTTP scheme "http[s]" */
200 if (strcmp(scheme
, OSSL_HTTPS_NAME
) == 0) {
204 } else if (*scheme
!= '\0' && strcmp(scheme
, OSSL_HTTP_NAME
) != 0) {
205 ERR_raise(ERR_LIB_HTTP
, HTTP_R_INVALID_URL_SCHEME
);
206 OPENSSL_free(scheme
);
210 OPENSSL_free(scheme
);
212 if (strcmp(port
, "0") == 0) {
213 /* set default port */
215 port
= ssl
? OSSL_HTTPS_PORT
: OSSL_HTTP_PORT
;
216 if (!ossl_assert(sscanf(port
, "%d", &portnum
) == 1))
218 if (pport_num
!= NULL
)
219 *pport_num
= portnum
;
221 *pport
= OPENSSL_strdup(port
);
237 free_pstring(pquery
);
242 int http_use_proxy(const char *no_proxy
, const char *server
)
245 const char *found
= NULL
;
247 if (!ossl_assert(server
!= NULL
))
252 * using environment variable names, both lowercase and uppercase variants,
253 * compatible with other HTTP client implementations like wget, curl and git
255 if (no_proxy
== NULL
)
256 no_proxy
= getenv("no_proxy");
257 if (no_proxy
== NULL
)
258 no_proxy
= getenv(OPENSSL_NO_PROXY
);
259 if (no_proxy
!= NULL
)
260 found
= strstr(no_proxy
, server
);
262 && ((found
!= no_proxy
&& found
[-1] != ' ' && found
[-1] != ',')
263 || (found
[sl
] != '\0' && found
[sl
] != ' ' && found
[sl
] != ',')))
264 found
= strstr(found
+ 1, server
);
265 return found
== NULL
;
268 const char *http_adapt_proxy(const char *proxy
, const char *no_proxy
,
269 const char *server
, int use_ssl
)
271 const int http_len
= strlen(OSSL_HTTP_PREFIX
);
272 const int https_len
= strlen(OSSL_HTTPS_PREFIX
);
275 * using environment variable names, both lowercase and uppercase variants,
276 * compatible with other HTTP client implementations like wget, curl and git
279 proxy
= getenv(use_ssl
? "https_proxy" : "http_proxy");
281 proxy
= getenv(use_ssl
? OPENSSL_HTTP_PROXY
:
282 OPENSSL_HTTPS_PROXY
);
286 /* skip any leading "http://" or "https://" */
287 if (strncmp(proxy
, OSSL_HTTP_PREFIX
, http_len
) == 0)
289 else if (strncmp(proxy
, OSSL_HTTPS_PREFIX
, https_len
) == 0)
292 if (*proxy
== '\0' || !http_use_proxy(no_proxy
, server
))