This change started out as a simple desire to speed up the
execution time of testapi.c, which was being delayed by
nameserver requests for non-existent URL's. From there it
just sort of grew, and grew....
* nanohttp.c, nanoftp.c: changed the processing of URL's
to use the uri.c routines instead of custom code.
* include/libxml/xmlerror.h: added code XML_FTP_URL_SYNTAX
* uri.c: added accepting ipV6 addresses, in accordance with
RFC's 2732 and 2373 (TODO: allow ipV4 within ipV6)
* gentest.py, testapi.c: fixed a few problems with the
testing of the nanoftp and nanohttp routines.
* include/libxml/xmlversion.h: minor change to fix a
warning on the docs generation
* regenerated the docs
diff --git a/uri.c b/uri.c
index fd6b588..09abd3d 100644
--- a/uri.c
+++ b/uri.c
@@ -1,7 +1,7 @@
/**
* uri.c: set of generic URI related routines
*
- * Reference: RFC 2396
+ * Reference: RFCs 2396, 2732 and 2373
*
* See Copyright for the status of this software.
*
@@ -78,12 +78,14 @@
/*
- * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
+ * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
+ * "[" | "]"
*/
#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \
((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \
- ((x) == '+') || ((x) == '$') || ((x) == ','))
+ ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \
+ ((x) == ']'))
/*
* unreserved = alphanum | mark
@@ -159,7 +161,7 @@
(IS_RESERVED(*(p))))
/*
-* unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
+* unwise = "{" | "}" | "|" | "\" | "^" | "`"
*/
#define IS_UNWISE(p) \
@@ -1199,11 +1201,16 @@
* userinfo = *( unreserved | escaped |
* ";" | ":" | "&" | "=" | "+" | "$" | "," )
* hostport = host [ ":" port ]
- * host = hostname | IPv4address
+ * host = hostname | IPv4address | IPv6reference
* hostname = *( domainlabel "." ) toplabel [ "." ]
* domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
* toplabel = alpha | alpha *( alphanum | "-" ) alphanum
- * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
+ * IPv6reference = "[" IPv6address "]"
+ * IPv6address = hexpart [ ":" IPv4address ]
+ * IPv4address = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
+ * hexpart = hexseq | hexseq "::" [ hexseq ]| "::" [ hexseq ]
+ * hexseq = hex4 *( ":" hex4)
+ * hex4 = 1*4hexdig
* port = *digit
*
* Returns 0 or the error code
@@ -1212,7 +1219,8 @@
xmlParseURIServer(xmlURIPtr uri, const char **str) {
const char *cur;
const char *host, *tmp;
- const int IPmax = 4;
+ const int IPV4max = 4;
+ const int IPV6max = 8;
int oct;
if (str == NULL)
@@ -1221,7 +1229,7 @@
cur = *str;
/*
- * is there an userinfo ?
+ * is there a userinfo ?
*/
while (IS_USERINFO(cur)) NEXT(cur);
if (*cur == '@') {
@@ -1252,21 +1260,60 @@
return(0);
}
/*
- * host part of hostport can derive either an IPV4 address
- * or an unresolved name. Check the IP first, it easier to detect
- * errors if wrong one
+ * host part of hostport can denote an IPV4 address, an IPV6 address
+ * or an unresolved name. Check the IP first, its easier to detect
+ * errors if wrong one.
+ * An IPV6 address must start with a '[' and end with a ']'.
*/
- for (oct = 0; oct < IPmax; ++oct) {
- if (*cur == '.')
- return(3); /* e.g. http://.xml/ or http://18.29..30/ */
- while(IS_DIGIT(*cur)) cur++;
- if (oct == (IPmax-1))
- continue;
- if (*cur != '.')
- break;
- cur++;
+ if (*cur == '[') {
+ int compress=0;
+ cur++;
+ for (oct = 0; oct < IPV6max; ++oct) {
+ if (*cur == ':') {
+ if (compress)
+ return(3); /* multiple compression attempted */
+ if (!oct) { /* initial char is compression */
+ if (*++cur != ':')
+ return(3);
+ }
+ compress = 1; /* set compression-encountered flag */
+ cur++; /* skip over the second ':' */
+ continue;
+ }
+ while(IS_HEX(*cur)) cur++;
+ if (oct == (IPV6max-1))
+ continue;
+ if (*cur != ':')
+ break;
+ cur++;
+ }
+ if ((!compress) && (oct != IPV6max))
+ return(3);
+ if (*cur != ']')
+ return(3);
+ if (uri != NULL) {
+ if (uri->server != NULL) xmlFree(uri->server);
+ uri->server = (char *)xmlStrndup((xmlChar *)host+1,
+ (cur-host)-1);
+ }
+ cur++;
+ } else {
+ /*
+ * Not IPV6, maybe IPV4
+ */
+ for (oct = 0; oct < IPV4max; ++oct) {
+ if (*cur == '.')
+ return(3); /* e.g. http://.xml/ or http://18.29..30/ */
+ while(IS_DIGIT(*cur)) cur++;
+ if (oct == (IPV4max-1))
+ continue;
+ if (*cur != '.')
+ break;
+ cur++;
+ }
}
- if (oct < IPmax || (*cur == '.' && cur++) || IS_ALPHA(*cur)) {
+ if ((host[0] != '[') && (oct < IPV4max || (*cur == '.' && cur++) ||
+ IS_ALPHA(*cur))) {
/* maybe host_name */
if (!IS_ALPHANUM(*cur))
return(4); /* e.g. http://xml.$oft */
@@ -1300,8 +1347,10 @@
if (uri != NULL) {
if (uri->authority != NULL) xmlFree(uri->authority);
uri->authority = NULL;
- if (uri->server != NULL) xmlFree(uri->server);
- uri->server = xmlURIUnescapeString(host, cur - host, NULL);
+ if (host[0] != '[') { /* it's not an IPV6 addr */
+ if (uri->server != NULL) xmlFree(uri->server);
+ uri->server = xmlURIUnescapeString(host, cur - host, NULL);
+ }
}
/*
* finish by checking for a port presence.