1
0
mirror of https://github.com/php/php-src.git synced 2026-04-15 20:11:02 +02:00

Detect invalid port in xp_socket parse ip address

For historical reasons, fsockopen() accepts the port and hostname
separately: fsockopen('127.0.0.1', 80)

However, with the introdcution of stream transports in PHP 4.3,
it became possible to include the port in the hostname specifier:

fsockopen('127.0.0.1:80')
Or more formally: fsockopen('tcp://127.0.0.1:80')

Confusing results when these two forms are combined, however.
fsockopen('127.0.0.1:80', 443) results in fsockopen() attempting
to connect to '127.0.0.1:80:443' which any reasonable stack would
consider invalid.

Unfortunately, PHP parses the address looking for the first colon
(with special handling for IPv6, don't worry) and calls atoi()
from there.  atoi() in turn, simply stops parsing at the first
non-numeric character and returns the value so far.

The end result is that the explicitly supplied port is treated
as ignored garbage, rather than producing an error.

This diff replaces atoi() with strtol() and inspects the
stop character.  If additional "garbage" of any kind is found,
it fails and returns an error.
This commit is contained in:
Sara Golemon
2017-03-07 11:27:46 -08:00
parent 549a30d2cd
commit bab0b99f37
2 changed files with 55 additions and 11 deletions

View File

@@ -0,0 +1,37 @@
--TEST--
Use of double-port in fsockopen()
--FILE--
<?php
$try = [
'127.0.0.1:80',
'tcp://127.0.0.1:80',
'[::1]:80',
'tcp://[::1]:80',
'localhost:80',
'tcp://localhost:80',
];
foreach ($try as $addr) {
echo "== $addr ==\n";
var_dump(@fsockopen($addr, 81, $errno, $errstr), $errstr);
}
--EXPECTF--
== 127.0.0.1:80 ==
bool(false)
string(41) "Failed to parse address "127.0.0.1:80:81""
== tcp://127.0.0.1:80 ==
bool(false)
string(41) "Failed to parse address "127.0.0.1:80:81""
== [::1]:80 ==
bool(false)
string(37) "Failed to parse address "[::1]:80:81""
== tcp://[::1]:80 ==
bool(false)
string(37) "Failed to parse address "[::1]:80:81""
== localhost:80 ==
bool(false)
string(41) "Failed to parse address "localhost:80:81""
== tcp://localhost:80 ==
bool(false)
string(41) "Failed to parse address "localhost:80:81""

View File

@@ -571,37 +571,44 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po
char *host = NULL; char *host = NULL;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
char *p;
if (*(str) == '[' && str_len > 1) { if (*(str) == '[' && str_len > 1) {
/* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */
p = memchr(str + 1, ']', str_len - 2); char *p = memchr(str + 1, ']', str_len - 2), *e = NULL;
if (!p || *(p + 1) != ':') { if (!p || *(p + 1) != ':') {
if (get_err) { if (get_err) {
*err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str);
} }
return NULL; return NULL;
} }
*portno = atoi(p + 2); *portno = strtol(p + 2, &e, 10);
if (e && *e) {
if (get_err) {
*err = strpprintf(0, "Failed to parse address \"%s\"", str);
}
return NULL;
}
return estrndup(str + 1, p - str - 1); return estrndup(str + 1, p - str - 1);
} }
#endif #endif
if (str_len) { if (str_len) {
colon = memchr(str, ':', str_len - 1); colon = memchr(str, ':', str_len - 1);
} else { } else {
colon = NULL; colon = NULL;
} }
if (colon) { if (colon) {
*portno = atoi(colon + 1); char *e = NULL;
host = estrndup(str, colon - str); *portno = strtol(colon + 1, &e, 10);
} else { if (!e || !*e) {
if (get_err) { return estrndup(str, colon - str);
*err = strpprintf(0, "Failed to parse address \"%s\"", str);
} }
return NULL;
} }
return host; if (get_err) {
*err = strpprintf(0, "Failed to parse address \"%s\"", str);
}
return NULL;
} }
static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno) static inline char *parse_ip_address(php_stream_xport_param *xparam, int *portno)