Skip to content

Commit 9317ece

Browse files
tyllbagder
authored andcommitted
URL parser: IPv6 zone identifiers are now supported
1 parent 0bc4938 commit 9317ece

File tree

3 files changed

+61
-25
lines changed

3 files changed

+61
-25
lines changed

docs/KNOWN_BUGS

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,16 +180,7 @@ may have been fixed since this was written!
180180
--cflags suffers from the same effects with CFLAGS/CPPFLAGS.
181181

182182
30. You need to use -g to the command line tool in order to use RFC2732-style
183-
IPv6 numerical addresses in URLs.
184-
185-
29. IPv6 URLs with zone ID is not nicely supported.
186-
http://www.ietf.org/internet-drafts/draft-fenner-literal-zone-02.txt (expired)
187-
specifies the use of a plus sign instead of a percent when specifying zone
188-
IDs in URLs to get around the problem of percent signs being
189-
special. According to the reporter, Firefox deals with the URL _with_ a
190-
percent letter (which seems like a blatant URL spec violation).
191-
libcurl supports zone IDs where the percent sign is URL-escaped (i.e. %25):
192-
http://curl.haxx.se/bug/view.cgi?id=555
183+
or RFC6874-style IPv6 numerical addresses in URLs.
193184

194185
26. NTLM authentication using SSPI (on Windows) when (lib)curl is running in
195186
"system context" will make it use wrong(?) user name - at least when compared

docs/MANUAL

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -956,9 +956,9 @@ IPv6
956956
When this style is used, the -g option must be given to stop curl from
957957
interpreting the square brackets as special globbing characters. Link local
958958
and site local addresses including a scope identifier, such as fe80::1234%1,
959-
may also be used, but the scope portion must be numeric and the percent
960-
character must be URL escaped. The previous example in an SFTP URL might
961-
look like:
959+
may also be used, but the scope portion must be numeric or match an existing
960+
network interface on Linux and the percent character must be URL escaped. The
961+
previous example in an SFTP URL might look like:
962962

963963
sftp://[fe80::1234%251]/
964964

lib/url.c

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3951,23 +3951,59 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data,
39513951
if(result != CURLE_OK)
39523952
return result;
39533953

3954-
if(conn->host.name[0] == '[') {
3954+
if(conn->host.name[0] == '[' && !data->state.this_is_a_follow) {
39553955
/* This looks like an IPv6 address literal. See if there is an address
3956-
scope. */
3957-
char *percent = strstr (conn->host.name, "%25");
3956+
scope if there is no location header */
3957+
char *percent = strchr(conn->host.name, '%');
39583958
if(percent) {
3959+
unsigned int identifier_offset = 3;
39593960
char *endp;
3960-
unsigned long scope = strtoul (percent + 3, &endp, 10);
3961+
unsigned long scope;
3962+
if(strncmp("%25", percent, 3) != 0) {
3963+
infof(data,
3964+
"Please URL encode %% as %%25, see RFC 6874.\n");
3965+
identifier_offset = 1;
3966+
}
3967+
scope = strtoul(percent + identifier_offset, &endp, 10);
39613968
if(*endp == ']') {
39623969
/* The address scope was well formed. Knock it out of the
39633970
hostname. */
39643971
memmove(percent, endp, strlen(endp)+1);
3965-
if(!data->state.this_is_a_follow)
3966-
/* Don't honour a scope given in a Location: header */
3967-
conn->scope = (unsigned int)scope;
3972+
conn->scope = (unsigned int)scope;
3973+
}
3974+
else {
3975+
/* Zone identifier is not numeric */
3976+
#ifdef HAVE_NET_IF_H
3977+
char ifname[IFNAMSIZ + 2];
3978+
char *square_bracket;
3979+
unsigned int scopeidx = 0;
3980+
strncpy(ifname, percent + identifier_offset, IFNAMSIZ + 2);
3981+
/* Ensure nullbyte termination */
3982+
ifname[IFNAMSIZ + 1] = '\0';
3983+
square_bracket = strchr(ifname, ']');
3984+
if(square_bracket) {
3985+
/* Remove ']' */
3986+
*square_bracket = '\0';
3987+
scopeidx = if_nametoindex(ifname);
3988+
if(scopeidx == 0) {
3989+
infof(data, "Invalid network interface: %s; %s\n", ifname,
3990+
strerror(errno));
3991+
}
3992+
}
3993+
if(scopeidx > 0) {
3994+
/* Remove zone identifier from hostname */
3995+
memmove(percent,
3996+
percent + identifier_offset + strlen(ifname),
3997+
identifier_offset + strlen(ifname));
3998+
conn->scope = scopeidx;
3999+
}
4000+
else {
4001+
#endif /* HAVE_NET_IF_H */
4002+
infof(data, "Invalid IPv6 address format\n");
4003+
#ifdef HAVE_NET_IF_H
4004+
}
4005+
#endif /* HAVE_NET_IF_H */
39684006
}
3969-
else
3970-
infof(data, "Invalid IPv6 address format\n");
39714007
}
39724008
}
39734009

@@ -4350,12 +4386,21 @@ static CURLcode parse_proxy(struct SessionHandle *data,
43504386
/* start scanning for port number at this point */
43514387
portptr = proxyptr;
43524388

4353-
/* detect and extract RFC2732-style IPv6-addresses */
4389+
/* detect and extract RFC6874-style IPv6-addresses */
43544390
if(*proxyptr == '[') {
43554391
char *ptr = ++proxyptr; /* advance beyond the initial bracket */
4356-
while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '%') ||
4357-
(*ptr == '.')))
4392+
while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
4393+
ptr++;
4394+
if(*ptr == '%') {
4395+
/* There might be a zone identifier */
4396+
if(strncmp("%25", ptr, 3))
4397+
infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
43584398
ptr++;
4399+
/* Allow unresered characters as defined in RFC 3986 */
4400+
while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
4401+
(*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
4402+
ptr++;
4403+
}
43594404
if(*ptr == ']')
43604405
/* yeps, it ended nicely with a bracket as well */
43614406
*ptr++ = 0;

0 commit comments

Comments
 (0)