r308996 broke IP literals by assuming that a colon could only occur as

a separator between host and port, and using strchr() to search for it.
Rewrite fetch_resolve() so it handles bracketed literals correctly, and
remove similar code elsewhere to avoid passing unbracketed literals to
fetch_resolve().  Remove #ifdef INET6 so we still parse IP literals
correctly even if we do not have the ability to connect to them.

While there, fix an off-by-one error which caused HTTP 400 errors to be
misinterpreted as redirects.

PR:		217723
MFC after:	1 week
Reported by:	bapt, bz, cem, ngie
This commit is contained in:
Dag-Erling Smørgrav 2017-03-17 14:18:52 +00:00
parent ff17a6773e
commit 08a49957b3
3 changed files with 49 additions and 42 deletions

View File

@ -248,37 +248,51 @@ fetch_resolve(const char *addr, int port, int af)
{
char hbuf[256], sbuf[8];
struct addrinfo hints, *res;
const char *sep, *host, *service;
const char *hb, *he, *sep;
const char *host, *service;
int err, len;
/* split address if necessary */
err = EAI_SYSTEM;
if ((sep = strchr(addr, ':')) != NULL) {
/* first, check for a bracketed IPv6 address */
if (*addr == '[') {
hb = addr + 1;
if ((sep = strchr(hb, ']')) == NULL) {
errno = EINVAL;
goto syserr;
}
he = sep++;
} else {
hb = addr;
sep = strchrnul(hb, ':');
he = sep;
}
/* see if we need to copy the host name */
if (*he != '\0') {
len = snprintf(hbuf, sizeof(hbuf),
"%.*s", (int)(sep - addr), addr);
"%.*s", (int)(he - hb), hb);
if (len < 0)
return (NULL);
goto syserr;
if (len >= (int)sizeof(hbuf)) {
errno = ENAMETOOLONG;
fetch_syserr();
return (NULL);
goto syserr;
}
host = hbuf;
service = sep + 1;
} else if (port != 0) {
} else {
host = hb;
}
/* was it followed by a service name? */
if (*sep == '\0' && port != 0) {
if (port < 1 || port > 65535) {
errno = EINVAL;
fetch_syserr();
return (NULL);
goto syserr;
}
if (snprintf(sbuf, sizeof(sbuf), "%d", port) < 0) {
fetch_syserr();
return (NULL);
}
host = addr;
if (snprintf(sbuf, sizeof(sbuf), "%d", port) < 0)
goto syserr;
service = sbuf;
} else if (*sep != '\0') {
service = sep;
} else {
host = addr;
service = NULL;
}
@ -292,6 +306,9 @@ fetch_resolve(const char *addr, int port, int af)
return (NULL);
}
return (res);
syserr:
fetch_syserr();
return (NULL);
}

View File

@ -386,18 +386,17 @@ fetchParseURL(const char *URL)
}
/* hostname */
#ifdef INET6
if (*p == '[' && (q = strchr(p + 1, ']')) != NULL &&
(*++q == '\0' || *q == '/' || *q == ':')) {
if ((i = q - p - 2) > MAXHOSTNAMELEN)
if ((i = q - p) > MAXHOSTNAMELEN)
i = MAXHOSTNAMELEN;
strncpy(u->host, ++p, i);
strncpy(u->host, p, i);
p = q;
} else
#endif
} else {
for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
if (i < MAXHOSTNAMELEN)
u->host[i++] = *p;
}
/* port */
if (*p == ':') {
@ -444,12 +443,12 @@ nohost:
}
DEBUG(fprintf(stderr,
"scheme: [%s]\n"
"user: [%s]\n"
"password: [%s]\n"
"host: [%s]\n"
"port: [%d]\n"
"document: [%s]\n",
"scheme: \"%s\"\n"
"user: \"%s\"\n"
"password: \"%s\"\n"
"host: \"%s\"\n"
"port: \"%d\"\n"
"document: \"%s\"\n",
u->scheme, u->user, u->pwd,
u->host, u->port, u->doc));

View File

@ -118,7 +118,7 @@ __FBSDID("$FreeBSD$");
|| (xyz) == HTTP_USE_PROXY \
|| (xyz) == HTTP_SEE_OTHER)
#define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)
#define HTTP_ERROR(xyz) ((xyz) >= 400 && (xyz) <= 599)
/*****************************************************************************
@ -1604,20 +1604,11 @@ http_request_body(struct url *URL, const char *op, struct url_stat *us,
if ((conn = http_connect(url, purl, flags)) == NULL)
goto ouch;
/* append port number only if necessary */
host = url->host;
#ifdef INET6
if (strchr(url->host, ':')) {
snprintf(hbuf, sizeof(hbuf), "[%s]", url->host);
host = hbuf;
}
#endif
if (url->port != fetch_default_port(url->scheme)) {
if (host != hbuf) {
strcpy(hbuf, host);
host = hbuf;
}
snprintf(hbuf + strlen(hbuf),
sizeof(hbuf) - strlen(hbuf), ":%d", url->port);
snprintf(hbuf, sizeof(hbuf), "%s:%d", host, url->port);
host = hbuf;
}
/* send request */