aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library.js403
-rw-r--r--src/settings.js32
-rw-r--r--tests/sockets/test_getaddrinfo.c197
-rw-r--r--tests/sockets/test_gethostbyname.c (renamed from tests/sockets/test_sockets_gethostbyname.c)14
-rw-r--r--tests/sockets/test_getnameinfo.c101
-rw-r--r--tests/test_sockets.py51
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)