Bug 504014 - Enforce RFC 3986 syntax for IPv6 literals

This commit is contained in:
Michal Novotny 2011-06-23 17:04:23 +02:00
parent 4610476ed2
commit 60e90c98b0
5 changed files with 186 additions and 0 deletions

View File

@ -37,6 +37,8 @@
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/RangedPtr.h"
#include "nsURLHelper.h"
#include "nsReadableUtils.h"
#include "nsIServiceManager.h"
@ -53,6 +55,8 @@
#include "prprf.h"
#include "prnetdb.h"
using namespace mozilla;
//----------------------------------------------------------------------------
// Init/Shutdown
//----------------------------------------------------------------------------
@ -990,3 +994,94 @@ net_IsValidHostName(const nsCSubstring &host)
PRNetAddr addr;
return PR_StringToNetAddr(strhost.get(), &addr) == PR_SUCCESS;
}
PRBool
net_IsValidIPv4Addr(const char *addr, PRInt32 addrLen)
{
RangedPtr<const char> p(addr, addrLen);
PRInt32 octet = -1; // means no digit yet
PRInt32 dotCount = 0; // number of dots in the address
for (; addrLen; ++p, --addrLen) {
if (*p == '.') {
dotCount++;
if (octet == -1) {
// invalid octet
return PR_FALSE;
}
octet = -1;
} else if (*p >= '0' && *p <='9') {
if (octet == 0) {
// leading 0 is not allowed
return PR_FALSE;
} else if (octet == -1) {
octet = *p - '0';
} else {
octet *= 10;
octet += *p - '0';
if (octet > 255)
return PR_FALSE;
}
} else {
// invalid character
return PR_FALSE;
}
}
return (dotCount == 3 && octet != -1);
}
PRBool
net_IsValidIPv6Addr(const char *addr, PRInt32 addrLen)
{
RangedPtr<const char> p(addr, addrLen);
PRInt32 digits = 0; // number of digits in current block
PRInt32 colons = 0; // number of colons in a row during parsing
PRInt32 blocks = 0; // number of hexadecimal blocks
PRBool haveZeros = PR_FALSE; // true if double colon is present in the address
for (; addrLen; ++p, --addrLen) {
if (*p == ':') {
if (colons == 0) {
if (digits != 0) {
digits = 0;
blocks++;
}
} else if (colons == 1) {
if (haveZeros)
return PR_FALSE; // only one occurrence is allowed
haveZeros = PR_TRUE;
} else {
// too many colons in a row
return PR_FALSE;
}
colons++;
} else if ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') ||
(*p >= 'A' && *p <= 'F')) {
if (colons == 1 && blocks == 0) // starts with a single colon
return PR_FALSE;
if (digits == 4) // too many digits
return PR_FALSE;
colons = 0;
digits++;
} else if (*p == '.') {
// check valid IPv4 from the beginning of the last block
if (!net_IsValidIPv4Addr(p.get() - digits, addrLen + digits))
return PR_FALSE;
return (haveZeros && blocks < 6) || (!haveZeros && blocks == 6);
} else {
// invalid character
return PR_FALSE;
}
}
if (colons == 1) // ends with a single colon
return PR_FALSE;
if (digits) // there is a block at the end
blocks++;
return (haveZeros && blocks < 8) || (!haveZeros && blocks == 8);
}

View File

@ -248,4 +248,14 @@ inline char *net_RFindCharNotInSet(const char *str, const char *set)
*/
NS_HIDDEN_(PRBool) net_IsValidHostName(const nsCSubstring &host);
/**
* Checks whether the IPv4 address is valid according to RFC 3986 section 3.2.2.
*/
NS_HIDDEN_(PRBool) net_IsValidIPv4Addr(const char *addr, PRInt32 addrLen);
/**
* Checks whether the IPv6 address is valid according to RFC 3986 section 3.2.2.
*/
NS_HIDDEN_(PRBool) net_IsValidIPv6Addr(const char *addr, PRInt32 addrLen);
#endif // !nsURLHelper_h__

View File

@ -641,6 +641,13 @@ nsAuthURLParser::ParseServerInfo(const char *serverinfo, PRInt32 serverinfoLen,
if (port)
*port = -1;
}
// In case of IPv6 address check its validity
if (*hostnameLen > 1 && *(serverinfo + *hostnamePos) == '[' &&
*(serverinfo + *hostnamePos + *hostnameLen - 1) == ']' &&
!net_IsValidIPv6Addr(serverinfo + *hostnamePos + 1, *hostnameLen - 2))
return NS_ERROR_MALFORMED_URI;
return NS_OK;
}

View File

@ -0,0 +1,73 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
var valid_URIs = [ "http://[::]/",
"http://[::1]/",
"http://[1::]/",
"http://[::]/",
"http://[::1]/",
"http://[1::]/",
"http://[1:2:3:4:5:6:7::]/",
"http://[::1:2:3:4:5:6:7]/",
"http://[1:2:a:B:c:D:e:F]/",
"http://[1::8]/",
"http://[1:2::8]/",
"http://[0000:0123:4567:89AB:CDEF:abcd:ef00:0000]/",
"http://[::192.168.1.1]/",
"http://[1::0.0.0.0]/",
"http://[1:2::255.255.255.255]/",
"http://[1:2:3::255.255.255.255]/",
"http://[1:2:3:4::255.255.255.255]/",
"http://[1:2:3:4:5::255.255.255.255]/",
"http://[1:2:3:4:5:6:255.255.255.255]/"];
var invalid_URIs = [ "http://[1]/",
"http://[192.168.1.1]/",
"http://[:::]/",
"http://[:::1]/",
"http://[1:::]/",
"http://[::1::]/",
"http://[1:2:3:4:5:6:7:]/",
"http://[:2:3:4:5:6:7:8]/",
"http://[1:2:3:4:5:6:7:8:]/",
"http://[:1:2:3:4:5:6:7:8]/",
"http://[1:2:3:4:5:6:7:8::]/",
"http://[::1:2:3:4:5:6:7:8]/",
"http://[1:2:3:4:5:6:7]/",
"http://[1:2:3:4:5:6:7:8:9]/",
"http://[00001:2:3:4:5:6:7:8]/",
"http://[0001:2:3:4:5:6:7:89abc]/",
"http://[A:b:C:d:E:f:G:h]/",
"http://[::192.168.1]/",
"http://[::192.168.1.]/",
"http://[::.168.1.1]/",
"http://[::192..1.1]/",
"http://[::0192.168.1.1]/",
"http://[::256.255.255.255]/",
"http://[::1x.255.255.255]/",
"http://[::192.4294967464.1.1]/",
"http://[1:2:3:4:5:6::255.255.255.255]/",
"http://[1:2:3:4:5:6:7:255.255.255.255]/"];
function run_test() {
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
for (var i=0 ; i<valid_URIs.length ; i++) {
try {
var uri = ios.newURI(valid_URIs[i], null, null);
} catch (e) {
do_throw("cannot create URI:" + valid_URIs[i]);
}
}
for (var i=0 ; i<invalid_URIs.length ; i++) {
try {
var uri = ios.newURI(invalid_URIs[i], null, null);
do_throw("should throw: " + invalid_URIs[i]);
} catch (e) {
do_check_eq(e.result, Cr.NS_ERROR_MALFORMED_URI);
}
}
}

View File

@ -45,6 +45,7 @@ tail =
[test_bug482601.js]
[test_bug484684.js]
[test_bug490095.js]
[test_bug504014.js]
[test_bug510359.js]
[test_bug515583.js]
[test_bug528292.js]