Generalize schmeme parsing of OSSL_HTTP_parse_url() to OSSL_parse_url()

Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14009)
This commit is contained in:
Dr. David von Oheimb 2021-02-17 17:24:19 +01:00 committed by Dr. David von Oheimb
parent 7932982b88
commit d546e8e267
7 changed files with 105 additions and 34 deletions

View File

@ -757,7 +757,7 @@ HTTP_R_FAILED_READING_DATA:128:failed reading data
HTTP_R_INCONSISTENT_CONTENT_LENGTH:120:inconsistent content length
HTTP_R_INVALID_PORT_NUMBER:123:invalid port number
HTTP_R_INVALID_URL_PATH:125:invalid url path
HTTP_R_INVALID_URL_PREFIX:124:invalid url prefix
HTTP_R_INVALID_URL_SCHEME:124:invalid url scheme
HTTP_R_MAX_RESP_LEN_EXCEEDED:117:max resp len exceeded
HTTP_R_MISSING_ASN1_ENCODING:110:missing asn1 encoding
HTTP_R_MISSING_CONTENT_TYPE:121:missing content type

View File

@ -32,8 +32,8 @@ static const ERR_STRING_DATA HTTP_str_reasons[] = {
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_PORT_NUMBER),
"invalid port number"},
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_URL_PATH), "invalid url path"},
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_URL_PREFIX),
"invalid url prefix"},
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_URL_SCHEME),
"invalid url scheme"},
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MAX_RESP_LEN_EXCEEDED),
"max resp len exceeded"},
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MISSING_ASN1_ENCODING),

View File

@ -36,21 +36,21 @@ static void free_pstring(char **pstr)
}
}
int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
char **pport, int *pport_num,
char **ppath, char **pquery, char **pfrag)
int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
char **pport, int *pport_num,
char **ppath, char **pquery, char **pfrag)
{
const char *p, *tmp;
const char *scheme, *scheme_end;
const char *user, *user_end;
const char *host, *host_end;
const char *port = OSSL_HTTP_PORT, *port_end;
const char *port, *port_end;
unsigned int portnum;
const char *path, *path_end;
const char *query, *query_end;
const char *frag, *frag_end;
if (pssl != NULL)
*pssl = 0;
init_pstring(pscheme);
init_pstring(puser);
init_pstring(phost);
init_pstring(pport);
@ -63,19 +63,15 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
return 0;
}
/* check for optional prefix "http[s]://" */
/* check for optional prefix "<scheme>://" */
scheme = scheme_end = url;
p = strstr(url, "://");
if (p == NULL) {
p = url;
} else { /* p points to end of scheme name */
if (strncmp(url, OSSL_HTTPS_NAME, strlen(OSSL_HTTPS_NAME)) == 0) {
if (pssl != NULL)
*pssl = 1;
port = OSSL_HTTPS_PORT;
} else if (strncmp(url, OSSL_HTTP_NAME, strlen(OSSL_HTTP_NAME)) != 0) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PREFIX);
goto err;
}
} else {
scheme_end = p;
if (scheme_end == scheme)
goto parse_err;
p += strlen("://");
}
@ -110,11 +106,12 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
}
/* parse optional port specification starting with ':' */
port = "0"; /* default */
if (*p == ':')
port = ++p;
/* remaining port spec handling is also done for the default values */
/* make sure a decimal port number is given */
if (!sscanf(port, "%u", &portnum) || portnum < 1 || portnum > 65535) {
if (!sscanf(port, "%u", &portnum) || portnum > 65535) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER);
goto err;
}
@ -150,7 +147,8 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
frag = tmp + 1;
}
if (!copy_substring(phost, host, host_end)
if (!copy_substring(pscheme, scheme, scheme_end)
|| !copy_substring(phost, host, host_end)
|| !copy_substring(pport, port, port_end)
|| !copy_substring(puser, user, user_end)
|| !copy_substring(pquery, query, query_end)
@ -174,6 +172,8 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
ERR_raise(ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL);
err:
free_pstring(pscheme);
free_pstring(puser);
free_pstring(phost);
free_pstring(pport);
free_pstring(ppath);
@ -182,6 +182,63 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
return 0;
}
int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
char **pport, int *pport_num,
char **ppath, char **pquery, char **pfrag)
{
char *scheme, *port;
int ssl = 0, portnum;
init_pstring(pport);
if (pssl != NULL)
*pssl = 0;
if (!OSSL_parse_url(url, &scheme, puser, phost, &port, pport_num,
ppath, pquery, pfrag))
return 0;
/* check for optional HTTP scheme "http[s]" */
if (strcmp(scheme, OSSL_HTTPS_NAME) == 0) {
ssl = 1;
if (pssl != NULL)
*pssl = ssl;
} else if (*scheme != '\0' && strcmp(scheme, OSSL_HTTP_NAME) != 0) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_SCHEME);
OPENSSL_free(scheme);
OPENSSL_free(port);
goto err;
}
OPENSSL_free(scheme);
if (strcmp(port, "0") == 0) {
/* set default port */
OPENSSL_free(port);
port = ssl ? OSSL_HTTPS_PORT : OSSL_HTTP_PORT;
if (!ossl_assert(sscanf(port, "%d", &portnum) == 1))
goto err;
if (pport_num != NULL)
*pport_num = portnum;
if (pport != NULL) {
*pport = OPENSSL_strdup(port);
if (*pport == NULL)
goto err;
}
} else {
if (pport != NULL)
*pport = port;
else
OPENSSL_free(port);
}
return 1;
err:
free_pstring(puser);
free_pstring(phost);
free_pstring(ppath);
free_pstring(pquery);
free_pstring(pfrag);
return 0;
}
int http_use_proxy(const char *no_proxy, const char *server)
{
size_t sl;

View File

@ -2,6 +2,7 @@
=head1 NAME
OSSL_parse_url,
OSSL_HTTP_parse_url,
OCSP_parse_url
- http utility functions
@ -10,6 +11,9 @@ OCSP_parse_url
#include <openssl/http.h>
int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
char **pport, int *pport_num,
char **ppath, char **pquery, char **pfrag);
int OSSL_HTTP_parse_url(const char *url,
int *pssl, char **puser, char **phost,
char **pport, int *pport_num,
@ -24,28 +28,34 @@ L<openssl_user_macros(7)>:
=head1 DESCRIPTION
OSSL_HTTP_parse_url() parses its input string I<url> as a URL of the form
C<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]> and splits it up
into userinfo, host, port, path, query, and fragment components
and a flag indicating whether it begins with C<https>.
OSSL_parse_url() parses its input string I<url> as a URL of the form
C<[scheme://][userinfo@]host[:port][/path][?query][#fragment]> and splits it up
into scheme, userinfo, host, port, path, query, and fragment components.
The host component may be a DNS name or an IP address
where IPv6 addresses should be enclosed in square brackets C<[> and C<]>.
The port component is optional and defaults to "443" for HTTPS, else "80".
The port component is optional and defaults to C<0>.
If given, it must be in decimal form. If the I<pport_num> argument is not NULL
the integer value of the port number is assigned to I<*pport_num> on success.
The path component is also optional and defaults to C</>.
If I<pssl> is not NULL, I<*pssl> is assigned 1 in case parsing was successful
and the schema part is present and is C<https>, else 0.
Each non-NULL result pointer argument I<puser>, I<phost>, I<pport>, I<ppath>,
I<pquery>, and I<pfrag>, is assigned the respective url component.
Each non-NULL result pointer argument I<pscheme>, I<puser>, I<phost>, I<pport>,
I<ppath>, I<pquery>, and I<pfrag>, is assigned the respective url component.
On success, they are guaranteed to contain non-NULL string pointers, else NULL.
It is the reponsibility of the caller to free them using L<OPENSSL_free(3)>.
If I<pquery> is NULL, any given query component is handled as part of the path.
A string returned via I<*ppath> is guaranteed to begin with a C</> character.
For absent userinfo, query, and fragment components an empty string is given.
For absent scheme, userinfo, port, query, and fragment components
an empty string is provided.
Calling the deprecated fucntion OCSP_parse_url(url, host, port, path, ssl) is
equivalent to OSSL_HTTP_parse_url(url, ssl, NULL, host, port, NULL, path, NULL, NULL).
OSSL_HTTP_parse_url() is a special form of OSSL_parse_url()
where the scheme, if given, must be C<http> or C<https>.
If I<pssl> is not NULL, I<*pssl> is assigned 1 in case parsing was successful
and the scheme is C<https>, else 0.
The port component is optional and defaults to C<443> if the scheme is C<https>,
else C<80>.
Calling the deprecated function OCSP_parse_url(url, host, port, path, ssl)
is equivalent to
OSSL_HTTP_parse_url(url, ssl, NULL, host, port, NULL, path, NULL, NULL).
=head1 RETURN VALUES

View File

@ -96,6 +96,9 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port,
const char *proxyuser, const char *proxypass,
int timeout, BIO *bio_err, const char *prog);
int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
char **pport, int *pport_num,
char **ppath, char **pquery, char **pfrag);
int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
char **pport, int *pport_num,
char **ppath, char **pquery, char **pfrag);

View File

@ -32,7 +32,7 @@
# define HTTP_R_INCONSISTENT_CONTENT_LENGTH 120
# define HTTP_R_INVALID_PORT_NUMBER 123
# define HTTP_R_INVALID_URL_PATH 125
# define HTTP_R_INVALID_URL_PREFIX 124
# define HTTP_R_INVALID_URL_SCHEME 124
# define HTTP_R_MAX_RESP_LEN_EXCEEDED 117
# define HTTP_R_MISSING_ASN1_ENCODING 110
# define HTTP_R_MISSING_CONTENT_TYPE 121

View File

@ -4880,6 +4880,7 @@ ASN1_item_verify_ex ? 3_0_0 EXIST::FUNCTION:
BIO_socket_wait ? 3_0_0 EXIST::FUNCTION:SOCK
BIO_wait ? 3_0_0 EXIST::FUNCTION:
BIO_do_connect_retry ? 3_0_0 EXIST::FUNCTION:
OSSL_parse_url ? 3_0_0 EXIST::FUNCTION:
OSSL_HTTP_get ? 3_0_0 EXIST::FUNCTION:
OSSL_HTTP_get_asn1 ? 3_0_0 EXIST::FUNCTION:
OSSL_HTTP_post_asn1 ? 3_0_0 EXIST::FUNCTION: