diff options
author | Alon Zakai <alonzakai@gmail.com> | 2013-08-06 10:33:51 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2013-08-06 10:33:51 -0700 |
commit | b94f320c1c954e40e911033c4e11a07492ff86ed (patch) | |
tree | 2c8b7bc34636d39a8c242dde8148611556277116 /src | |
parent | 8dfcd06d78a82786a04fe74afa707dc0c0cfd1a6 (diff) | |
parent | f2c1782834a786e2f064f28e30d770ab40a3128f (diff) |
Merge pull request #1348 from mnaamani/inet
handle IPv6 addresses in inet_ntop and inet_pton
Diffstat (limited to 'src')
-rw-r--r-- | src/library.js | 251 |
1 files changed, 219 insertions, 32 deletions
diff --git a/src/library.js b/src/library.js index 0e4ac0b2..e8333b32 100644 --- a/src/library.js +++ b/src/library.js @@ -8581,42 +8581,53 @@ LibraryManager.library = { ntohl: 'htonl', ntohs: 'htons', - inet_addr: function(ptr) { - var b = Pointer_stringify(ptr).split("."); - if (b.length !== 4) return -1; // we return -1 for error, and otherwise a uint32. this helps inet_pton differentiate - return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0; + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html + inet_ntop__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_ntop4', 'inet_ntop6'], + inet_ntop: function(af, src, dst, size) { + switch (af) { + case {{{ cDefine('AF_INET') }}}: + return _inet_ntop4(src, dst, size); + case {{{ cDefine('AF_INET6') }}}: + return _inet_ntop6(src, dst, size); + default: + ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + return 0; + } }, - - inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_addr'], + inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_pton4', 'inet_pton6'], inet_pton: function(af, src, dst) { - // int af, const char *src, void *dst - if ((af ^ {{{ cDefine('AF_INET') }}}) !== 0) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); return -1; } - var ret = _inet_addr(src); - if (ret == -1 || isNaN(ret)) return 0; - setValue(dst, ret, 'i32'); - return 1; + switch (af) { + case {{{ cDefine('AF_INET') }}}: + return _inet_pton4(src, dst); + case {{{ cDefine('AF_INET6') }}}: + return _inet_pton6(src, dst); + default: + ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + return -1; + } }, - - _inet_ntop_raw: function(addr) { - return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) + inet_addr: function(ptr) { + var b = Pointer_stringify(ptr).split("."); + if (b.length !== 4) return -1; // we return -1 for error, and otherwise a uint32. this helps inet_pton differentiate + return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0; }, - - inet_ntop__deps: ['_inet_ntop_raw'], - inet_ntop: function(af, src, dst, size) { - var addr = getValue(src, 'i32'); - var str = __inet_ntop_raw(addr); - writeStringToMemory(str.substr(0, size), dst); - return dst; + _inet_aton_raw: function(str) { + var b = str.split("."); + return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0; }, - - inet_ntoa__deps: ['inet_ntop'], + _inet_ntoa_raw: function(addr) { + return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) + }, + inet_ntoa__deps: ['_inet_ntoa_raw'], inet_ntoa: function(in_addr) { if (!_inet_ntoa.buffer) { _inet_ntoa.buffer = _malloc(1024); } - return _inet_ntop(0, in_addr, _inet_ntoa.buffer, 1024); + var addr = getValue(in_addr, 'i32'); + var str = __inet_ntoa_raw(addr); + writeStringToMemory(str.substr(0, 1024), _inet_ntoa.buffer); + return _inet_ntoa.buffer; }, - inet_aton__deps: ['inet_addr'], inet_aton: function(cp, inp) { var addr = _inet_addr(cp); @@ -8625,6 +8636,182 @@ LibraryManager.library = { return 1; }, + inet_ntop4__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntoa_raw'], + inet_ntop4: function(src, dst, size) { + var str = __inet_ntoa_raw(getValue(src, 'i32')); + if (str.length+1 > size) { + ___setErrNo(ERRNO_CODES.ENOSPC); + return 0; + } + writeStringToMemory(str, dst); + return dst; + }, + + inet_ntop6__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_ntop6_raw'], + inet_ntop6: function(src, dst, size) { + var str = _inet_ntop6_raw(src); + if (str.length+1 > size) { + ___setErrNo(ERRNO_CODES.ENOSPC); + return 0; + } + writeStringToMemory(str, dst); + return dst; + }, + inet_ntop6_raw__deps: ['ntohs'], + inet_ntop6_raw: function(src) { + + // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 + // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses + // 128-bits are split into eight 16-bit words + // stored in network byte order (big-endian) + // | 80 bits | 16 | 32 bits | + // +-----------------------------------------------------------------+ + // | 10 bytes | 2 | 4 bytes | + // +--------------------------------------+--------------------------+ + // + 5 words | 1 | 2 words | + // +--------------------------------------+--------------------------+ + // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) + // +--------------------------------------+----+---------------------+ + // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) + // +--------------------------------------+----+---------------------+ + + var str = ""; + var word = 0; + var longest = 0; + var lastzero = 0; + var zstart = 0; + var len = 0; + var i = 0; + + // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses + + var hasipv4 = true; + var v4part = ""; + // check if the 10 high-order bytes are all zeros (first 5 words) + for (i = 0; i < 10; i++) { + if ({{{ makeGetValue('src', 'i', 'i8') }}} !== 0) { hasipv4 = false; break; } + } + + if (hasipv4) { + // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) + v4part = __inet_ntoa_raw({{{ makeGetValue('src', '12', 'i32') }}}); + // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) + if ({{{ makeGetValue('src', '10', 'i16') }}} === -1) { + str = "::ffff:"; + str += v4part; + return str; + } + // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) + if ({{{ makeGetValue('src', '10', 'i16') }}} === 0) { + str = "::"; + //special case IPv6 addresses + if(v4part === "0.0.0.0") v4part = ""; // any/unspecified address + if(v4part === "0.0.0.1") v4part = "1";// loopback address + str += v4part; + return str; + } + } + + // Handle all other IPv6 addresses + + // first run to find the longest contiguous zero words + for (word = 0; word < 8; word++) { + if ({{{ makeGetValue('src', 'word*2', 'i16') }}} === 0) { + if (word - lastzero > 1) { + len = 0; + } + lastzero = word; + len++; + } + if (len > longest) { + longest = len; + zstart = word - longest + 1; + } + } + + for (word = 0; word < 8; word++) { + if (longest > 1) { + // compress contiguous zeros - to produce "::" + if ({{{ makeGetValue('src', 'word*2', 'i16') }}} === 0 && word >= zstart && word < (zstart + longest) ) { + if (word === zstart) { + str += ":"; + if (zstart === 0) str += ":"; //leading zeros case + } + continue; + } + } + // converts 16-bit words from big-endian to little-endian before converting to hex string + str += Number(_ntohs({{{ makeGetValue('src', 'word*2', 'i16') }}} & 0xffff)).toString(16); + str += word < 7 ? ":" : ""; + } + return str; + }, + + inet_pton4__deps: ['inet_addr'], + inet_pton4: function(src, dst) { + var ret = _inet_addr(src); + if (ret === -1 || isNaN(ret)) return 0; + setValue(dst, ret, 'i32'); + return 1; + }, + + inet_pton6__deps: ['inet_pton6_raw'], + inet_pton6: function(src, dst) { + return _inet_pton6_raw(Pointer_stringify(src), dst); + }, + + inet_pton6_raw__deps: ['htons'], + inet_pton6_raw: function(addr, dst) { + var words; + var w, offset, z, i; + /* http://home.deds.nl/~aeron/regex/ */ + var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i + if (!valid6regx.test(addr)) { + return 0; + } + if (addr === "::") { + for (i=0; i < 4; i++) {{{ makeSetValue('dst', 'i*4', '0', 'i32') }}}; + return 1; + } + // Z placeholder to keep track of zeros when splitting the string on ":" + if (addr.indexOf("::") === 0) { + addr = addr.replace("::", "Z:"); // leading zeros case + } else { + addr = addr.replace("::", ":Z:"); + } + + if (addr.indexOf(".") > 0) { + // parse IPv4 embedded address + addr = addr.replace(new RegExp('[.]', 'g'), ":"); + words = addr.split(":"); + words[words.length-4] = parseInt(words[words.length-4]) + parseInt(words[words.length-3])*256; + words[words.length-3] = parseInt(words[words.length-2]) + parseInt(words[words.length-1])*256; + words = words.slice(0, words.length-2); + } else { + words = addr.split(":"); + } + + offset = 0; z = 0; + for (w=0; w < words.length; w++) { + if (typeof words[w] === 'string') { + if (words[w] === 'Z') { + // compressed zeros - write appropriate number of zero words + for (z = 0; z < (8 - words.length+1); z++) { + {{{ makeSetValue('dst', '(w+z)*2', '0', 'i16') }}}; + } + offset = z-1; + } else { + // parse hex to field to 16-bit value and write it in network byte-order + {{{ makeSetValue('dst', '(w+offset)*2', '_htons(parseInt(words[w],16))', 'i16') }}}; + } + } else { + // parsed IPv4 words + {{{ makeSetValue('dst', '(w+offset)*2', 'words[w]', 'i16') }}}; + } + } + return 1; + }, + // netinet/in.h _in6addr_any: @@ -8898,7 +9085,7 @@ LibraryManager.library = { // Stub: connection-oriented sockets are not supported yet. }, - bind__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'mkport'], + bind__deps: ['$Sockets', '_inet_ntoa_raw', 'ntohs', 'mkport'], bind: function(fd, addr, addrlen) { var info = FS.getStream(fd); if (!info) return -1; @@ -8910,7 +9097,7 @@ LibraryManager.library = { info.port = _mkport(); } info.addr = Sockets.localAddr; // 10.0.0.254 - info.host = __inet_ntop_raw(info.addr); + info.host = __inet_ntoa_raw(info.addr); info.close = function() { Sockets.portmap[info.port] = undefined; } @@ -8919,7 +9106,7 @@ LibraryManager.library = { info.bound = true; }, - sendmsg__deps: ['$Sockets', 'bind', '_inet_ntop_raw', 'ntohs'], + sendmsg__deps: ['$Sockets', 'bind', '_inet_ntoa_raw', 'ntohs'], sendmsg: function(fd, msg, flags) { var info = FS.getStream(fd); if (!info) return -1; @@ -8933,7 +9120,7 @@ LibraryManager.library = { var port = _ntohs(getValue(name + Sockets.sockaddr_in_layout.sin_port, 'i16')); var addr = getValue(name + Sockets.sockaddr_in_layout.sin_addr, 'i32'); var connection = Sockets.connections[addr]; - // var host = __inet_ntop_raw(addr); + // var host = __inet_ntoa_raw(addr); if (!(connection && connection.connected)) { ___setErrNo(ERRNO_CODES.EWOULDBLOCK); @@ -9135,14 +9322,14 @@ LibraryManager.library = { return stream.fd; }, - connect__deps: ['$FS', '$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'], + connect__deps: ['$FS', '$Sockets', '_inet_ntoa_raw', 'ntohs', 'gethostbyname'], connect: function(fd, addr, addrlen) { var info = FS.getStream(fd); if (!info) return -1; info.connected = true; info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); info.port = _htons(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); - info.host = __inet_ntop_raw(info.addr); + info.host = __inet_ntoa_raw(info.addr); // Support 'fake' ips from gethostbyname var parts = info.host.split('.'); if (parts[0] == '172' && parts[1] == '29') { |