diff options
-rw-r--r-- | src/library.js | 403 | ||||
-rw-r--r-- | src/settings.js | 32 | ||||
-rw-r--r-- | tests/sockets/test_getaddrinfo.c | 197 | ||||
-rw-r--r-- | tests/sockets/test_gethostbyname.c (renamed from tests/sockets/test_sockets_gethostbyname.c) | 14 | ||||
-rw-r--r-- | tests/sockets/test_getnameinfo.c | 101 | ||||
-rw-r--r-- | tests/test_sockets.py | 51 |
6 files changed, 711 insertions, 87 deletions
diff --git a/src/library.js b/src/library.js index 3f488034..b5542609 100644 --- a/src/library.js +++ b/src/library.js @@ -7175,10 +7175,55 @@ LibraryManager.library = { // netdb.h // ========================================================================== - // All we can do is alias names to ips. you give this a name, it returns an - // "ip" that we later know to use as a name. There is no way to do actual - // name resolving clientside in a browser. - // we do the aliasing in 172.29.*.*, giving us 65536 possibilities + // Fake hostname resolution helpers. We can't actually do this + // client-side in the browser, so instead we're generating fake + // IP addresses with _lookup_name that we can resolve later on + // with _lookup_addr. + _address_map: { + id: 1, + addrs: {}, + names: {} + }, + + _lookup_name__deps: ['_address_map', '_inet_pton4_raw', '_inet_pton6_raw'], + _lookup_name: function (name) { + // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one. + var res = __inet_pton4_raw(name); + if (res) { + return name; + } + res = __inet_pton6_raw(name); + if (res) { + return name; + } + + // See if this name is already mapped. + var addr; + + if (__address_map.addrs[name]) { + addr = __address_map.addrs[name]; + } else { + var id = __address_map.id++; + assert(id < 65535, 'exceeded max address mappings of 65535'); + + addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00); + + __address_map.names[addr] = name; + __address_map.addrs[name] = addr; + } + + return addr; + }, + + _lookup_addr__deps: ['_address_map'], + _lookup_addr: function (addr) { + if (__address_map.names[addr]) { + return __address_map.names[addr]; + } + + return null; + }, + // note: lots of leaking here! __hostent_struct_layout: Runtime.generateStructInfo([ ['i8*', 'h_name'], @@ -7188,32 +7233,53 @@ LibraryManager.library = { ['i8**', 'h_addr_list'], ]), - gethostbyname__deps: ['__hostent_struct_layout'], + _addrinfo_layout: Runtime.generateStructInfo([ + ['i32', 'ai_flags'], + ['i32', 'ai_family'], + ['i32', 'ai_socktype'], + ['i32', 'ai_protocol'], + ['i32', 'ai_addrlen'], + ['*', 'ai_addr'], + ['*', 'ai_canonname'], + ['*', 'ai_next'] + ]), + + gethostbyaddr__deps: ['gethostbyname', '_inet_ntop4_raw', '_lookup_addr'], + gethostbyaddr: function (addr, addrlen, type) { + if (type !== {{{ cDefine('AF_INET') }}}) { + ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + return null; + } + addr = {{{ makeGetValue('addr', '0', 'i32') }}}; // addr is in_addr + var host = __inet_ntop4_raw(addr); + var lookup = __lookup_addr(host); + if (lookup) { + host = lookup; + } + var hostp = allocate(intArrayFromString(host), 'i8', ALLOC_STACK); + return _gethostbyname(hostp); + }, + + gethostbyname__deps: ['__hostent_struct_layout', '_inet_pton4_raw', '_lookup_name'], gethostbyname: function(name) { name = Pointer_stringify(name); - if (!_gethostbyname.id) { - _gethostbyname.id = 1; - _gethostbyname.table = {}; - } - var id = _gethostbyname.id++; - assert(id < 65535); - var fakeAddr = 172 | (29 << 8) | ((id & 0xff) << 16) | ((id & 0xff00) << 24); - _gethostbyname.table[id] = name; + // generate hostent var ret = _malloc(___hostent_struct_layout.__size__); var nameBuf = _malloc(name.length+1); writeStringToMemory(name, nameBuf); - setValue(ret+___hostent_struct_layout.h_name, nameBuf, 'i8*'); + {{{ makeSetValue('ret', '___hostent_struct_layout.h_name', 'nameBuf', 'i8*') }}} var aliasesBuf = _malloc(4); - setValue(aliasesBuf, 0, 'i8*'); - setValue(ret+___hostent_struct_layout.h_aliases, aliasesBuf, 'i8**'); - setValue(ret+___hostent_struct_layout.h_addrtype, {{{ cDefine('AF_INET') }}}, 'i32'); - setValue(ret+___hostent_struct_layout.h_length, 4, 'i32'); + {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}} + {{{ makeSetValue('ret', '___hostent_struct_layout.h_aliases', 'aliasesBuf', 'i8**') }}} + var afinet = {{{ cDefine("AF_INET") }}}; + {{{ makeSetValue('ret', '___hostent_struct_layout.h_addrtype', 'afinet', 'i32') }}} + {{{ makeSetValue('ret', '___hostent_struct_layout.h_length', '4', 'i32') }}} var addrListBuf = _malloc(12); - setValue(addrListBuf, addrListBuf+8, 'i32*'); - setValue(addrListBuf+4, 0, 'i32*'); - setValue(addrListBuf+8, fakeAddr, 'i32'); - setValue(ret+___hostent_struct_layout.h_addr_list, addrListBuf, 'i8**'); + {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}} + {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}} + {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(__lookup_name(name))', 'i32') }}} + {{{ makeSetValue('ret', '___hostent_struct_layout.h_addr_list', 'addrListBuf', 'i8**') }}} return ret; }, @@ -7222,10 +7288,218 @@ LibraryManager.library = { var data = _gethostbyname(name); _memcpy(hostData, data, ___hostent_struct_layout.__size__); _free(data); - setValue(errnum, 0, 'i32'); + {{{ makeSetValue('errnum', '0', '0', 'i32') }}} + return 0; + }, + + getaddrinfo__deps: ['$Sockets', '_addrinfo_layout', '_lookup_name', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'], + getaddrinfo: function(node, service, hint, out) { + var addrs = []; + var canon = null; + var addr = 0; + var port = 0; + var flags = 0; + var family = {{{ cDefine('AF_UNSPEC') }}}; + var type = 0; + var proto = 0; + var ai, last; + + function allocaddrinfo(family, type, proto, canon, addr, port) { + var sa, salen, ai; + var res; + + salen = family === {{{ cDefine('AF_INET6') }}} ? + Sockets.sockaddr_in6_layout.__size__ : + Sockets.sockaddr_in_layout.__size__; + addr = family === {{{ cDefine('AF_INET6') }}} ? + __inet_ntop6_raw(addr) : + __inet_ntop4_raw(addr); + sa = _malloc(salen); + res = __write_sockaddr(sa, family, addr, port); + assert(!res.errno); + + ai = _malloc(__addrinfo_layout.__size__); + {{{ makeSetValue('ai', '__addrinfo_layout.ai_family', 'family', 'i32') }}}; + {{{ makeSetValue('ai', '__addrinfo_layout.ai_socktype', 'type', 'i32') }}}; + {{{ makeSetValue('ai', '__addrinfo_layout.ai_protocol', 'proto', 'i32') }}}; + if (canon) { + {{{ makeSetValue('ai', '__addrinfo_layout.ai_canonname', 'canon', 'i32') }}}; + } + {{{ makeSetValue('ai', '__addrinfo_layout.ai_addr', 'sa', '*') }}}; + if (family === {{{ cDefine('AF_INET6') }}}) { + {{{ makeSetValue('ai', '__addrinfo_layout.ai_addrlen', 'Sockets.sockaddr_in6_layout.__size__', 'i32') }}}; + } else { + {{{ makeSetValue('ai', '__addrinfo_layout.ai_addrlen', 'Sockets.sockaddr_in_layout.__size__', 'i32') }}}; + } + + return ai; + } + + if (hint) { + flags = {{{ makeGetValue('hint', '__addrinfo_layout.ai_flags', 'i32') }}}; + family = {{{ makeGetValue('hint', '__addrinfo_layout.ai_family', 'i32') }}}; + type = {{{ makeGetValue('hint', '__addrinfo_layout.ai_socktype', 'i32') }}}; + proto = {{{ makeGetValue('hint', '__addrinfo_layout.ai_protocol', 'i32') }}}; + } + if (type && !proto) { + proto = type === {{{ cDefine('SOCK_DGRAM') }}} ? {{{ cDefine('IPPROTO_UDP') }}} : {{{ cDefine('IPPROTO_TCP') }}}; + } + if (!type && proto) { + type = proto === {{{ cDefine('IPPROTO_UDP') }}} ? {{{ cDefine('SOCK_DGRAM') }}} : {{{ cDefine('SOCK_STREAM') }}}; + } + + if (!node && !service) { + return {{{ cDefine('EAI_NONAME') }}}; + } + if (flags & ~({{{ cDefine('AI_PASSIVE') }}}|{{{ cDefine('AI_CANONNAME') }}}|{{{ cDefine('AI_NUMERICHOST') }}}| + {{{ cDefine('AI_NUMERICSERV') }}}|{{{ cDefine('AI_V4MAPPED') }}}|{{{ cDefine('AI_ALL') }}}|{{{ cDefine('AI_ADDRCONFIG') }}})) { + return {{{ cDefine('EAI_BADFLAGS') }}}; + } + if (({{{ makeGetValue('hint', '__addrinfo_layout.ai_flags', 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) { + return {{{ cDefine('EAI_BADFLAGS') }}}; + } + if (flags & {{{ cDefine('AI_ADDRCONFIG') }}}) { + // TODO + return {{{ cDefine('EAI_NONAME') }}}; + } + if (type !== {{{ cDefine('SOCK_STREAM') }}} && type !== {{{ cDefine('SOCK_DGRAM') }}}) { + return {{{ cDefine('EAI_SOCKTYPE') }}}; + } + if (family !== {{{ cDefine('AF_UNSPEC') }}} && family !== {{{ cDefine('AF_INET') }}} && family !== {{{ cDefine('AF_INET6') }}}) { + return {{{ cDefine('EAI_FAMILY') }}}; + } + + if (service) { + service = Pointer_stringify(service); + port = parseInt(service, 10); + + if (isNaN(port)) { + if (flags & {{{ cDefine('AI_NUMERICSERV') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + // TODO support resolving well-known service names from: + // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt + return {{{ cDefine('EAI_SERVICE') }}}; + } + } + + if (!node) { + if (family === {{{ cDefine('AF_UNSPEC') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } + if ((flags & {{{ cDefine('AI_PASSIVE') }}}) === 0) { + if (family === {{{ cDefine('AF_INET') }}}) { + addr = _htonl({{{ cDefine('INADDR_LOOPBACK') }}}); + } else { + addr = [0, 0, 0, 1]; + } + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + } + + // + // try as a numeric address + // + node = Pointer_stringify(node); + addr = __inet_pton4_raw(node); + if (addr !== null) { + // incoming node is a valid ipv4 address + if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } + else if (family === {{{ cDefine('AF_INET6') }}} && (flags & {{{ cDefine('AI_V4MAPPED') }}})) { + addr = [0, 0, _htonl(0xffff), addr]; + family = {{{ cDefine('AF_INET6') }}}; + } else { + return {{{ cDefine('EAI_NONAME') }}}; + } + } else { + addr = __inet_pton6_raw(node); + if (addr !== null) { + // incoming node is a valid ipv6 address + if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET6') }}}) { + family = {{{ cDefine('AF_INET6') }}}; + } else { + return {{{ cDefine('EAI_NONAME') }}}; + } + } + } + if (addr != null) { + ai = allocaddrinfo(family, type, proto, node, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + } + if (flags & {{{ cDefine('AI_NUMERICHOST') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + + // + // try as a hostname + // + // resolve the hostname to a temporary fake address + node = __lookup_name(node); + addr = __inet_pton4_raw(node); + if (family === {{{ cDefine('AF_UNSPEC') }}}) { + family = {{{ cDefine('AF_INET') }}} + } else if (family === {{{ cDefine('AF_INET6') }}}) { + addr = [0, 0, _htonl(0xffff), addr]; + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; + }, + + freeaddrinfo__deps: ['$Sockets', '_addrinfo_layout'], + freeaddrinfo: function(ai) { + var sa = {{{ makeGetValue('ai', '__addrinfo_layout.ai_addr', '*') }}}; + _free(sa); + _free(ai); + }, + + getnameinfo__deps: ['$Sockets', '__hostent_struct_layout', '_read_sockaddr', '_lookup_addr'], + getnameinfo: function (sa, salen, node, nodelen, serv, servlen, flags) { + var info = __read_sockaddr(sa, salen); + if (info.errno) { + return {{{ cDefine('EAI_FAMILY') }}}; + } + var port = info.port; + var addr = info.addr; + + if (node && nodelen) { + var lookup; + if ((flags & {{{ cDefine('NI_NUMERICHOST') }}}) || !(lookup = __lookup_addr(addr))) { + if (flags & {{{ cDefine('NI_NAMEREQD') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + } else { + addr = lookup; + } + if (addr.length >= nodelen) { + return {{{ cDefine('EAI_OVERFLOW') }}}; + } + writeStringToMemory(addr, node); + } + + if (serv && servlen) { + port = '' + port; + if (port.length > servlen) { + return {{{ cDefine('EAI_OVERFLOW') }}}; + } + writeStringToMemory(port, serv); + } + return 0; }, + gai_strerror: function(val) { + if (!_gai_strerror.error) { + _gai_strerror.error = allocate(intArrayFromString("unknown error"), 'i8', ALLOC_NORMAL); + } + return _gai_strerror.error; + }, + // ========================================================================== // sockets. Note that the implementation assumes all sockets are always // nonblocking @@ -7259,6 +7533,13 @@ LibraryManager.library = { ['i32', 'sin_zero'], ['i16', 'sin_zero_b'], ]), + sockaddr_in6_layout: Runtime.generateStructInfo([ + ['i32', 'sin6_family'], + ['i16', 'sin6_port'], + ['i32', 'sin6_flowinfo'], + ['b128', 'sin6_addr'], + ['i32', 'sin6_scope_id'] + ]), msghdr_layout: Runtime.generateStructInfo([ ['*', 'msg_name'], ['i32', 'msg_namelen'], @@ -7652,6 +7933,67 @@ LibraryManager.library = { } }, #else + // ========================================================================== + // socket.h + // ========================================================================== + _read_sockaddr__deps: ['$Sockets', '_inet_ntop4_raw', '_inet_ntop6_raw'], + _read_sockaddr: function (sa, salen) { + // family / port offsets are common to both sockaddr_in and sockaddr_in6 + var family = {{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_family', 'i32') }}}; + var port = _ntohs({{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_port', 'i16') }}}); + var addr; + + switch (family) { + case {{{ cDefine('AF_INET') }}}: + if (salen !== Sockets.sockaddr_in_layout.__size__) { + return { errno: ERRNO_CODES.EINVAL }; + } + addr = {{{ makeGetValue('sa', 'Sockets.sockaddr_in_layout.sin_addr', 'i32') }}}; + addr = __inet_ntop4_raw(addr); + break; + case {{{ cDefine('AF_INET6') }}}: + if (salen !== Sockets.sockaddr_in6_layout.__size__) { + return { errno: ERRNO_CODES.EINVAL }; + } + addr = [ + {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+0', 'i32') }}}, + {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+4', 'i32') }}}, + {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+8', 'i32') }}}, + {{{ makeGetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+12', 'i32') }}} + ]; + addr = __inet_ntop6_raw(addr); + break; + default: + return { errno: ERRNO_CODES.EAFNOSUPPORT }; + } + + return { family: family, addr: addr, port: port }; + }, + _write_sockaddr__deps: ['$Sockets', '_inet_pton4_raw', '_inet_pton6_raw'], + _write_sockaddr: function (sa, family, addr, port) { + switch (family) { + case {{{ cDefine('AF_INET') }}}: + addr = __inet_pton4_raw(addr); + {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_family', 'family', 'i32') }}}; + {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}}; + {{{ makeSetValue('sa', 'Sockets.sockaddr_in_layout.sin_port', '_htons(port)', 'i16') }}}; + break; + case {{{ cDefine('AF_INET6') }}}: + addr = __inet_pton6_raw(addr); + {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_family', 'family', 'i32') }}}; + {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+0', 'addr[0]', 'i32') }}}; + {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+4', 'addr[1]', 'i32') }}}; + {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+8', 'addr[2]', 'i32') }}}; + {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_addr+12', 'addr[3]', 'i32') }}}; + {{{ makeSetValue('sa', 'Sockets.sockaddr_in6_layout.sin6_port', '_htons(port)', 'i16') }}}; + break; + default: + return { errno: ERRNO_CODES.EAFNOSUPPORT }; + } + // kind of lame, but let's match _read_sockaddr's interface + return {}; + }, + socket__deps: ['$FS', '$Sockets'], socket: function(family, type, protocol) { var stream = type == {{{ cDefine('SOCK_STREAM') }}}; @@ -7668,21 +8010,20 @@ LibraryManager.library = { return stream.fd; }, - connect__deps: ['$FS', '$Sockets', '_inet_ntop4_raw', 'ntohs', 'gethostbyname'], + connect__deps: ['$FS', '$Sockets', '_inet_ntop4_raw', 'ntohs', '_lookup_addr'], connect: function(fd, addr, addrlen) { var info = FS.getStream(fd); if (!info) return -1; + // TODO support ipv6 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_ntop4_raw(info.addr); - // Support 'fake' ips from gethostbyname - var parts = info.host.split('.'); - if (parts[0] == '172' && parts[1] == '29') { - var low = Number(parts[2]); - var high = Number(parts[3]); - info.host = _gethostbyname.table[low + 0xff*high]; - assert(info.host, 'problem translating fake ip ' + parts); + // The incoming address could perhaps be a fake address generated by gethostbyname, + // look it up to be sure. + var lookup = __lookup_addr(info.host); + if (lookup) { + info.host = lookup; } try { console.log('opening ws://' + info.host + ':' + info.port); diff --git a/src/settings.js b/src/settings.js index 03b4ed64..bf2736b6 100644 --- a/src/settings.js +++ b/src/settings.js @@ -457,6 +457,7 @@ var C_DEFINES = { 'ABMON_8': '40', 'ABMON_9': '41', 'ACCESSPERMS': '0000400', + 'AF_UNSPEC': '0', 'AF_INET': '2', 'AF_INET6': '10', 'ALLPERMS': '0004000', @@ -1457,6 +1458,35 @@ var C_DEFINES = { 'ECANCELED': '140', 'ENOTRECOVERABLE': '141', 'EOWNERDEAD': '142', - 'ESTRPIPE': '143' + 'ESTRPIPE': '143', + 'AI_PASSIVE': '0x0001', + 'AI_CANONNAME': '0x0002', + 'AI_NUMERICHOST': '0x0004', + 'AI_V4MAPPED': '0x0008', + 'AI_ALL': '0x0010', + 'AI_ADDRCONFIG': '0x0020', + 'AI_NUMERICSERV': '0x0400', + 'EAI_ADDRFAMILY': '1', + 'EAI_AGAIN': '2', + 'EAI_BADFLAGS': '3', + 'EAI_FAIL': '4', + 'EAI_FAMILY': '5', + 'EAI_MEMORY': '6', + 'EAI_NODATA': '7', + 'EAI_NONAME': '8', + 'EAI_SERVICE': '9', + 'EAI_SOCKTYPE': '10', + 'EAI_SYSTEM': '11', + 'EAI_BADHINTS': '12', + 'EAI_PROTOCOL': '13', + 'EAI_OVERFLOW': '14', + 'EAI_MAX': '15', + 'NI_NOFQDN': '0x00000001', + 'NI_NUMERICHOST': '0x00000002', + 'NI_NAMEREQD': '0x00000004', + 'NI_NUMERICSERV': '0x00000008', + 'NI_DGRAM': '0x00000010', + 'INADDR_ANY': '0', + 'INADDR_LOOPBACK': '0x7f000001' }; diff --git a/tests/sockets/test_getaddrinfo.c b/tests/sockets/test_getaddrinfo.c new file mode 100644 index 00000000..717a9ae7 --- /dev/null +++ b/tests/sockets/test_getaddrinfo.c @@ -0,0 +1,197 @@ +#include <arpa/inet.h> +#include <sys/socket.h> +#include <assert.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if EMSCRIPTEN +#include <emscripten.h> +#endif + +int main() { + struct addrinfo hints; + struct addrinfo *servinfo; + struct sockaddr_in *sa4; + struct sockaddr_in6 *sa6; + int err; + + // no name or service + err = getaddrinfo(NULL, NULL, NULL, &servinfo); + assert(err == EAI_NONAME); + + // invalid socket type + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = 9999; + err = getaddrinfo("www.mozilla.org", "80", &hints, &servinfo); +#ifdef __APPLE__ + assert(err == EAI_BADHINTS); +#else + assert(err == EAI_SOCKTYPE); +#endif + + // invalid family + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNIX; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("www.mozilla.org", "80", &hints, &servinfo); + assert(err == EAI_FAMILY); + + // invalid service + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("www.mozilla.org", "foobar", &hints, &servinfo); +#ifdef __APPLE__ + assert(err == EAI_NONAME); +#else + assert(err == EAI_SERVICE); +#endif + + // test loopback resolution (ipv4) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo(NULL, "80", &hints, &servinfo); + assert(!err); + sa4 = ((struct sockaddr_in*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET); + assert(servinfo->ai_socktype == SOCK_STREAM); + assert(*(uint32_t*)&(sa4->sin_addr) == ntohl(INADDR_LOOPBACK)); + assert(sa4->sin_port == ntohs(80)); + freeaddrinfo(servinfo); + + // test loopback resolution (ipv6) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo(NULL, "81", &hints, &servinfo); + assert(!err); + sa6 = ((struct sockaddr_in6*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET6); + assert(servinfo->ai_socktype == SOCK_STREAM); + memcmp(&sa6->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)); + assert(sa6->sin6_port == ntohs(81)); + freeaddrinfo(servinfo); + + // test bind preparation + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo(NULL, "82", &hints, &servinfo); + assert(!err); + sa4 = ((struct sockaddr_in*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET); + assert(servinfo->ai_socktype == SOCK_STREAM); + assert(*(uint32_t*)&(sa4->sin_addr) == 0); + assert(sa4->sin_port == ntohs(82)); + freeaddrinfo(servinfo); + + // test numeric address (ipv4) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + err = getaddrinfo("1.2.3.4", "83", &hints, &servinfo); + assert(!err); + sa4 = ((struct sockaddr_in*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET); + assert(servinfo->ai_socktype == SOCK_DGRAM); + assert(*(uint32_t*)&(sa4->sin_addr) == 67305985); + assert(sa4->sin_port == ntohs(83)); + freeaddrinfo(servinfo); + + // test numeric address (ipv4 address specified as ipv6 with AI_V4MAPPED) + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_V4MAPPED; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("1.2.3.4", "84", &hints, &servinfo); + assert(!err); + sa6 = ((struct sockaddr_in6*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET6); + assert(servinfo->ai_socktype == SOCK_STREAM); + assert(*((uint32_t*)&(sa6->sin6_addr)+2) == htonl(0xffff)); + assert(*((uint32_t*)&(sa6->sin6_addr)+3) == 67305985); + assert(sa6->sin6_port == ntohs(84)); + freeaddrinfo(servinfo); + + // test numeric address (ipv4 address specified as ipv6 without AI_V4MAPPED) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("1.2.3.4", "85", &hints, &servinfo); +#ifdef __linux__ + assert(err == -9 /* EAI_ADDRFAMILY */); +#else + assert(err == EAI_NONAME); +#endif + + // test numeric address (ipv6) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + err = getaddrinfo("2001:0db8:85a3:0042:1000:8a2e:0370:7334", "86", &hints, &servinfo); + assert(!err); + sa6 = ((struct sockaddr_in6*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET6); + assert(servinfo->ai_socktype == SOCK_DGRAM); + assert(*((uint32_t*)&(sa6->sin6_addr)+0) == -1207107296); + assert(*((uint32_t*)&(sa6->sin6_addr)+1) == 1107338117); + assert(*((uint32_t*)&(sa6->sin6_addr)+2) == 780795920); + assert(*((uint32_t*)&(sa6->sin6_addr)+3) == 879980547); + assert(sa6->sin6_port == ntohs(86)); + freeaddrinfo(servinfo); + + // test numeric address (ipv6 address specified as ipv4) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("2001:0db8:85a3:0042:1000:8a2e:0370:7334", "87", &hints, &servinfo); +#ifdef __linux__ + assert(err == -9 /* EAI_ADDRFAMILY */); +#else + assert(err == EAI_NONAME); +#endif + + // test non-numeric host with AI_NUMERICHOST + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("www.mozilla.org", "88", &hints, &servinfo); + assert(err == EAI_NONAME); + + // test non-numeric host with AF_INET + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("www.mozilla.org", "89", &hints, &servinfo); + assert(!err); + sa4 = ((struct sockaddr_in*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET); + assert(servinfo->ai_socktype == SOCK_STREAM); + assert(sa4->sin_port == ntohs(89)); + + // test non-numeric host with AF_INET6 + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("www.mozilla.org", "90", &hints, &servinfo); + assert(!err); + sa6 = ((struct sockaddr_in6*)servinfo->ai_addr); + assert(servinfo->ai_family == AF_INET6); + assert(servinfo->ai_socktype == SOCK_STREAM); + assert(*((uint32_t*)&(sa6->sin6_addr)+0) != 0 || + *((uint32_t*)&(sa6->sin6_addr)+1) != 0 || + *((uint32_t*)&(sa6->sin6_addr)+2) != 0 || + *((uint32_t*)&(sa6->sin6_addr)+3) != 0); + assert(sa6->sin6_port == ntohs(90)); + + puts("success"); + + return EXIT_SUCCESS; +} + diff --git a/tests/sockets/test_sockets_gethostbyname.c b/tests/sockets/test_gethostbyname.c index 12fc6d9d..de7da706 100644 --- a/tests/sockets/test_sockets_gethostbyname.c +++ b/tests/sockets/test_gethostbyname.c @@ -11,16 +11,6 @@ #include <emscripten.h> #endif -int sockfd; - -void finish(int result) { - close(sockfd); -#if EMSCRIPTEN - REPORT_RESULT(); -#endif - exit(result); -} - int main() { char str[INET_ADDRSTRLEN]; struct in_addr addr; @@ -29,6 +19,8 @@ int main() { // resolve the hostname ot an actual address struct hostent *host = gethostbyname("slashdot.org"); + assert(host->h_addrtype == AF_INET); + assert(host->h_length == sizeof(uint32_t)); // convert the raw address to a string char **raw_addr_list = host->h_addr_list; @@ -44,6 +36,8 @@ int main() { struct hostent *host1 = gethostbyaddr(&addr, sizeof(addr), host->h_addrtype); assert(strstr(host1->h_name, "slashdot.org")); + puts("success"); + return EXIT_SUCCESS; } diff --git a/tests/sockets/test_getnameinfo.c b/tests/sockets/test_getnameinfo.c new file mode 100644 index 00000000..c3fec6b4 --- /dev/null +++ b/tests/sockets/test_getnameinfo.c @@ -0,0 +1,101 @@ +#include <arpa/inet.h> +#include <sys/socket.h> +#include <assert.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if EMSCRIPTEN +#include <emscripten.h> +#endif + +int main() { + struct addrinfo hints; + struct addrinfo *servinfo; + struct sockaddr_in sa4; + struct sockaddr_in6 sa6; + char node[256]; + char serv[256]; + int flags; + int err; + +#ifndef __APPLE__ + // incorrect sockaddr size + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4)-1, NULL, 0, NULL, 0, 0); + assert(err == EAI_FAMILY); + + memset(&sa6, 0, sizeof(sa6)); + sa6.sin6_family = AF_INET6; + err = getnameinfo((struct sockaddr*)&sa6, sizeof(sa6)-1, NULL, 0, NULL, 0, 0); + assert(err == EAI_FAMILY); + + // invalid family + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = 9999; + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), NULL, 0, NULL, 0, 0); + assert(err == EAI_FAMILY); +#endif + + // NI_NUMERICHOST and NI_NAMEREQD conflict + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + flags = NI_NUMERICHOST | NI_NAMEREQD; + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags); + assert(err == EAI_NONAME); + + // too small of buffer + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + *(uint32_t*)&sa4.sin_addr = 67305985; + sa4.sin_port = htons(54321); + flags = NI_NUMERICHOST; + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, 1, serv, sizeof(serv), flags); + assert(err == EAI_OVERFLOW); + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, 1, flags); + assert(err == EAI_OVERFLOW); + + // NI_NAMEREQD and lookup failed + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + *(uint32_t*)&sa4.sin_addr = 67305985; + flags = NI_NAMEREQD; + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags); + assert(err == EAI_NONAME); + + // lookup failed + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + *(uint32_t*)&sa4.sin_addr = 67305985; + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags); + assert(err == EAI_NONAME); + + // no lookup + memset(&sa4, 0, sizeof(sa4)); + sa4.sin_family = AF_INET; + *(uint32_t*)&sa4.sin_addr = 67305985; + sa4.sin_port = htons(54321); + flags = NI_NUMERICHOST; + err = getnameinfo((struct sockaddr*)&sa4, sizeof(sa4), node, sizeof(node), serv, sizeof(serv), flags); + assert(!err); + assert(!strcmp(node, "1.2.3.4")); + assert(!strcmp(serv, "54321")); + + // lookup + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + err = getaddrinfo("www.mozilla.org", "54321", &hints, &servinfo); + assert(!err); + flags = NI_NAMEREQD; + err = getnameinfo(servinfo->ai_addr, servinfo->ai_addrlen, node, sizeof(node), serv, sizeof(serv), flags); + assert(!err); + assert(strstr(node, "mozilla")); + assert(!strcmp(serv, "54321")); + + puts("success"); + + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/tests/test_sockets.py b/tests/test_sockets.py index 8f03d6ae..85813447 100644 --- a/tests/test_sockets.py +++ b/tests/test_sockets.py @@ -214,49 +214,14 @@ class sockets(BrowserCore): "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n" ) - def test_gethostbyname(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip("assume t2 in gethostbyname") - - src = r''' - #include <netdb.h> - #include <stdio.h> + def test_getaddrinfo(self): + self.do_run(open(path_from_root('tests', 'sockets', 'test_getaddrinfo.c')).read(), 'success') - void test(char *hostname) { - hostent *host = gethostbyname(hostname); - if (!host) { - printf("no such thing\n"); - return; - } - printf("%s : %d : %d\n", host->h_name, host->h_addrtype, host->h_length); - char **name = host->h_aliases; - while (*name) { - printf("- %s\n", *name); - name++; - } - name = host->h_addr_list; - while (name && *name) { - printf("* "); - for (int i = 0; i < host->h_length; i++) - printf("%d.", (*name)[i]); - printf("\n"); - name++; - } - } + def test_getnameinfo(self): + self.do_run(open(path_from_root('tests', 'sockets', 'test_getnameinfo.c')).read(), 'success') - int main() { - test("www.cheezburger.com"); - test("fail.on.this.never.work"); // we will "work" on this - because we are just making aliases of names to ips - test("localhost"); - return 0; - } - ''' - self.do_run(src, '''www.cheezburger.com : 2 : 4 -* -84.29.1.0. -fail.on.this.never.work : 2 : 4 -* -84.29.2.0. -localhost : 2 : 4 -* -84.29.3.0. -''') + def test_gethostbyname(self): + self.do_run(open(path_from_root('tests', 'sockets', 'test_gethostbyname.c')).read(), 'success') def test_sockets_echo(self): sockets_include = '-I'+path_from_root('tests', 'sockets') @@ -300,10 +265,6 @@ localhost : 2 : 4 with harness: self.btest(os.path.join('sockets', 'test_sockets_partial_client.c'), expected='165', args=['-DSOCKK=8995']) - # TODO add support for gethostbyaddr to re-enable this test - # def test_sockets_gethostbyname(self): - # self.btest(os.path.join('sockets', 'test_sockets_gethostbyname.c'), expected='0', args=['-O2', '-DSOCKK=8997']) - def test_sockets_select_server_down(self): for harness in [ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_select_server_down_server.c'), ['-DSOCKK=9002'], 9003, 9002) |