summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-08-30 10:32:57 -0700
committerAlon Zakai <alonzakai@gmail.com>2013-08-30 10:32:57 -0700
commit3c8dc3131c6de3f7816746bec174e82b6b833275 (patch)
tree97d89c77b2d5da02de1b125418795ff86113b01c
parent15f4ad9b5ba8095ce1596f8733f03ff39030cd79 (diff)
parentdf89e4a8ea28aa3f0bcdd9116d348fb63f6cc406 (diff)
Merge pull request #1557 from inolen/sockfs
getaddrinfo, freeaddrinfo, getnameinfo support and sockfs
-rw-r--r--.gitignore2
-rw-r--r--package.json5
-rw-r--r--src/library.js1390
-rw-r--r--src/library_fs.js12
-rw-r--r--src/library_sockfs.js551
-rw-r--r--src/settings.js33
-rwxr-xr-xtests/runner.py4
-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/sockets/test_sockets_msg.h1
-rw-r--r--tests/sockets/test_sockets_select_server_down_client.c2
-rw-r--r--tests/test_core.py162
-rw-r--r--tests/test_sockets.py258
14 files changed, 2012 insertions, 720 deletions
diff --git a/.gitignore b/.gitignore
index 92043241..747394e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@
*.bc
src/relooper*.js
+node_modules/
+
# Ignore generated files
src/relooper.js
src/relooper.js.raw.js
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..157885fb
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "ws": "~0.4.28"
+ }
+}
diff --git a/src/library.js b/src/library.js
index f6b3d5ef..2c60c8ba 100644
--- a/src/library.js
+++ b/src/library.js
@@ -600,31 +600,32 @@ LibraryManager.library = {
// poll.h
// ==========================================================================
+ __DEFAULT_POLLMASK: {{{ cDefine('POLLIN') }}} | {{{ cDefine('POLLOUT') }}},
__pollfd_struct_layout: Runtime.generateStructInfo([
['i32', 'fd'],
['i16', 'events'],
['i16', 'revents']]),
- poll__deps: ['$FS', '__pollfd_struct_layout'],
+ poll__deps: ['$FS', '__DEFAULT_POLLMASK', '__pollfd_struct_layout'],
poll: function(fds, nfds, timeout) {
// int poll(struct pollfd fds[], nfds_t nfds, int timeout);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html
- // NOTE: This is pretty much a no-op mimicking glibc.
var offsets = ___pollfd_struct_layout;
var nonzero = 0;
for (var i = 0; i < nfds; i++) {
var pollfd = fds + ___pollfd_struct_layout.__size__ * i;
var fd = {{{ makeGetValue('pollfd', 'offsets.fd', 'i32') }}};
var events = {{{ makeGetValue('pollfd', 'offsets.events', 'i16') }}};
- var revents = 0;
+ var mask = {{{ cDefine('POLLNVAL') }}};
var stream = FS.getStream(fd);
if (stream) {
- if (events & {{{ cDefine('POLLIN') }}}) revents |= {{{ cDefine('POLLIN') }}};
- if (events & {{{ cDefine('POLLOUT') }}}) revents |= {{{ cDefine('POLLOUT') }}};
- } else {
- if (events & {{{ cDefine('POLLNVAL') }}}) revents |= {{{ cDefine('POLLNVAL') }}};
+ mask = ___DEFAULT_POLLMASK;
+ if (stream.stream_ops.poll) {
+ mask = stream.stream_ops.poll(stream);
+ }
}
- if (revents) nonzero++;
- {{{ makeSetValue('pollfd', 'offsets.revents', 'revents', 'i16') }}}
+ mask &= events | {{{ cDefine('POLLERR') }}} | {{{ cDefine('POLLHUP') }}};
+ if (mask) nonzero++;
+ {{{ makeSetValue('pollfd', 'offsets.revents', 'mask', 'i16') }}}
}
return nonzero;
},
@@ -723,7 +724,7 @@ LibraryManager.library = {
FS.close(stream);
return 0;
} catch (e) {
- FS.handleFSError(e);;
+ FS.handleFSError(e);
return -1;
}
},
@@ -1006,9 +1007,11 @@ LibraryManager.library = {
return -1;
}
+#if SOCKET_WEBRTC
if (stream && ('socket' in stream)) {
return _recv(fildes, buf, nbyte, 0);
}
+#endif
try {
var slab = {{{ makeGetSlabs('buf', 'i8', true) }}};
@@ -1139,9 +1142,11 @@ LibraryManager.library = {
return -1;
}
+#if SOCKET_WEBRTC
if (stream && ('socket' in stream)) {
return _send(fildes, buf, nbyte, 0);
}
+#endif
try {
var slab = {{{ makeGetSlabs('buf', 'i8', true) }}};
@@ -6910,7 +6915,6 @@ LibraryManager.library = {
// ==========================================================================
// arpa/inet.h
// ==========================================================================
-
htonl: function(value) {
return ((value & 0xff) << 24) + ((value & 0xff00) << 8) +
((value & 0xff0000) >>> 8) + ((value & 0xff000000) >>> 24);
@@ -6921,75 +6925,43 @@ LibraryManager.library = {
ntohl: 'htonl',
ntohs: 'htons',
- // 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_pton4', 'inet_pton6'],
- inet_pton: function(af, src, dst) {
- 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;
- }
- },
+ // old ipv4 only functions
+ inet_addr__deps: ['_inet_pton4_raw'],
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_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_raw: function(addr) {
- return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff)
+ var addr = __inet_pton4_raw(Pointer_stringify(ptr));
+ if (addr === null) {
+ return -1;
+ }
+ return addr;
},
- inet_ntoa__deps: ['_inet_ntoa_raw'],
+ inet_ntoa__deps: ['_inet_ntop4_raw'],
inet_ntoa: function(in_addr) {
if (!_inet_ntoa.buffer) {
_inet_ntoa.buffer = _malloc(1024);
}
- var addr = getValue(in_addr, 'i32');
- var str = __inet_ntoa_raw(addr);
+ var addr = {{{ makeGetValue('in_addr', '0', 'i32') }}};
+ var str = __inet_ntop4_raw(addr);
writeStringToMemory(str.substr(0, 1024), _inet_ntoa.buffer);
return _inet_ntoa.buffer;
},
- inet_aton__deps: ['inet_addr'],
+ inet_aton__deps: ['_inet_pton4_raw'],
inet_aton: function(cp, inp) {
- var addr = _inet_addr(cp);
- setValue(inp, addr, 'i32');
- if (addr < 0) return 0;
- 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);
+ var addr = __inet_pton4_raw(Pointer_stringify(cp));
+ if (addr === null) {
return 0;
}
- writeStringToMemory(str, dst);
- return dst;
+ {{{ makeSetValue('inp', '0', 'addr', 'i32') }}}
+ return 1;
},
- inet_ntop6__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_ntop6_raw'],
- inet_ntop6: function(src, dst, size) {
- var str = _inet_ntop6_raw(src);
+ // new ipv4 / ipv6 functions
+ _inet_ntop4_raw: function(addr) {
+ return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff)
+ },
+ _inet_ntop4__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop4_raw'],
+ _inet_ntop4: function(src, dst, size) {
+ var addr = {{{ makeGetValue('src', '0', 'i32') }}};
+ var str = __inet_ntop4_raw(addr);
if (str.length+1 > size) {
___setErrNo(ERRNO_CODES.ENOSPC);
return 0;
@@ -6997,9 +6969,8 @@ LibraryManager.library = {
writeStringToMemory(str, dst);
return dst;
},
- inet_ntop6_raw__deps: ['ntohs'],
- inet_ntop6_raw: function(src) {
-
+ _inet_ntop6_raw__deps: ['ntohs', '_inet_ntop4_raw'],
+ _inet_ntop6_raw: function(ints) {
// 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
@@ -7014,7 +6985,6 @@ LibraryManager.library = {
// +--------------------------------------+----+---------------------+
// |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped)
// +--------------------------------------+----+---------------------+
-
var str = "";
var word = 0;
var longest = 0;
@@ -7022,27 +6992,37 @@ LibraryManager.library = {
var zstart = 0;
var len = 0;
var i = 0;
+ var parts = [
+ ints[0] & 0xffff,
+ (ints[0] >> 16),
+ ints[1] & 0xffff,
+ (ints[1] >> 16),
+ ints[2] & 0xffff,
+ (ints[2] >> 16),
+ ints[3] & 0xffff,
+ (ints[3] >> 16)
+ ];
// 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; }
+ for (i = 0; i < 5; i++) {
+ if (parts[i] !== 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') }}});
+ v4part = __inet_ntop4_raw(parts[6] | (parts[7] << 16));
// IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word)
- if ({{{ makeGetValue('src', '10', 'i16') }}} === -1) {
+ if (parts[5] === -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) {
+ if (parts[5] === 0) {
str = "::";
//special case IPv6 addresses
if(v4part === "0.0.0.0") v4part = ""; // any/unspecified address
@@ -7056,7 +7036,7 @@ LibraryManager.library = {
// first run to find the longest contiguous zero words
for (word = 0; word < 8; word++) {
- if ({{{ makeGetValue('src', 'word*2', 'i16') }}} === 0) {
+ if (parts[word] === 0) {
if (word - lastzero > 1) {
len = 0;
}
@@ -7072,7 +7052,7 @@ LibraryManager.library = {
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 (parts[word] === 0 && word >= zstart && word < (zstart + longest) ) {
if (word === zstart) {
str += ":";
if (zstart === 0) str += ":"; //leading zeros case
@@ -7081,54 +7061,86 @@ LibraryManager.library = {
}
}
// 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 += Number(_ntohs(parts[word] & 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_ntop6__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop6_raw'],
+ _inet_ntop6: function(src, dst, size) {
+ var addr = [
+ {{{ makeGetValue('src', '0', 'i32') }}}, {{{ makeGetValue('src', '4', 'i32') }}},
+ {{{ makeGetValue('src', '8', 'i32') }}}, {{{ makeGetValue('src', '12', 'i32') }}}
+ ];
+ var str = __inet_ntop6_raw(addr);
+ if (str.length+1 > size) {
+ ___setErrNo(ERRNO_CODES.ENOSPC);
+ return 0;
+ }
+ writeStringToMemory(str, dst);
+ return dst;
},
-
- inet_pton6__deps: ['inet_pton6_raw'],
- inet_pton6: function(src, dst) {
- return _inet_pton6_raw(Pointer_stringify(src), dst);
+ inet_ntop__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop4', '_inet_ntop6'],
+ inet_ntop: function(af, src, dst, size) {
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html
+ 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_pton6_raw__deps: ['htons'],
- inet_pton6_raw: function(addr, dst) {
+ _inet_pton4_raw: function(str) {
+ var b = str.split('.');
+ for (var i = 0; i < 4; i++) {
+ var tmp = Number(b[i]);
+ if (isNaN(tmp)) return null;
+ b[i] = tmp;
+ }
+ return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0;
+ },
+ _inet_pton4__deps: ['_inet_pton4_raw'],
+ _inet_pton4: function(src, dst) {
+ var ret = __inet_pton4_raw(Pointer_stringify(src));
+ if (ret === null) {
+ return 0;
+ }
+ {{{ makeSetValue('dst', '0', 'ret', 'i32') }}}
+ return 1;
+ },
+ _inet_pton6_raw__deps: ['htons'],
+ _inet_pton6_raw: function(str) {
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;
+ var parts = [];
+ if (!valid6regx.test(str)) {
+ return null;
}
- if (addr === "::") {
- for (i=0; i < 4; i++) {{{ makeSetValue('dst', 'i*4', '0', 'i32') }}};
- return 1;
+ if (str === "::") {
+ return [0, 0, 0, 0, 0, 0, 0, 0];
}
// Z placeholder to keep track of zeros when splitting the string on ":"
- if (addr.indexOf("::") === 0) {
- addr = addr.replace("::", "Z:"); // leading zeros case
+ if (str.indexOf("::") === 0) {
+ str = str.replace("::", "Z:"); // leading zeros case
} else {
- addr = addr.replace("::", ":Z:");
+ str = str.replace("::", ":Z:");
}
- if (addr.indexOf(".") > 0) {
- // parse IPv4 embedded address
- addr = addr.replace(new RegExp('[.]', 'g'), ":");
- words = addr.split(":");
+ if (str.indexOf(".") > 0) {
+ // parse IPv4 embedded stress
+ str = str.replace(new RegExp('[.]', 'g'), ":");
+ words = str.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(":");
+ words = str.split(":");
}
offset = 0; z = 0;
@@ -7137,20 +7149,49 @@ LibraryManager.library = {
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') }}};
+ parts[w+z] = 0;
}
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') }}};
+ parts[w+offset] = _htons(parseInt(words[w],16));
}
} else {
// parsed IPv4 words
- {{{ makeSetValue('dst', '(w+offset)*2', 'words[w]', 'i16') }}};
+ parts[w+offset] = words[w];
}
}
+ return [
+ (parts[1] << 16) | parts[0],
+ (parts[3] << 16) | parts[2],
+ (parts[5] << 16) | parts[4],
+ (parts[7] << 16) | parts[6]
+ ];
+ },
+ _inet_pton6__deps: ['_inet_pton6_raw'],
+ _inet_pton6: function(src, dst) {
+ var ints = __inet_pton6_raw(Pointer_stringify(src));
+ if (ints === null) {
+ return 0;
+ }
+ for (var i = 0; i < 4; i++) {
+ {{{ makeSetValue('dst', 'i*4', 'ints[i]', 'i32') }}};
+ }
return 1;
},
+ inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_pton4', '_inet_pton6'],
+ inet_pton: function(af, src, dst) {
+ // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_pton.html
+ 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;
+ }
+ },
// netinet/in.h
@@ -7173,10 +7214,56 @@ 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
+ // We can't actually resolve hostnames in the browser, so instead
+ // we're generating fake IP addresses with lookup_name that we can
+ // resolve later on with lookup_addr.
+ // We do the aliasing in 172.29.*.*, giving us 65536 possibilities.
+ $DNS: {
+ address_map: {
+ id: 1,
+ addrs: {},
+ names: {}
+ },
+
+ lookup_name__deps: ['_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 (DNS.address_map.addrs[name]) {
+ addr = DNS.address_map.addrs[name];
+ } else {
+ var id = DNS.address_map.id++;
+ assert(id < 65535, 'exceeded max address mappings of 65535');
+
+ addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00);
+
+ DNS.address_map.names[addr] = name;
+ DNS.address_map.addrs[name] = addr;
+ }
+
+ return addr;
+ },
+
+ lookup_addr: function (addr) {
+ if (DNS.address_map.names[addr]) {
+ return DNS.address_map.names[addr];
+ }
+
+ return null;
+ }
+ },
+
// note: lots of leaking here!
__hostent_struct_layout: Runtime.generateStructInfo([
['i8*', 'h_name'],
@@ -7186,32 +7273,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: ['$DNS', 'gethostbyname', '_inet_ntop4_raw'],
+ 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 = DNS.lookup_addr(host);
+ if (lookup) {
+ host = lookup;
+ }
+ var hostp = allocate(intArrayFromString(host), 'i8', ALLOC_STACK);
+ return _gethostbyname(hostp);
+ },
+
+ gethostbyname__deps: ['$DNS', '__hostent_struct_layout', '_inet_pton4_raw'],
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(DNS.lookup_name(name))', 'i32') }}}
+ {{{ makeSetValue('ret', '___hostent_struct_layout.h_addr_list', 'addrListBuf', 'i8**') }}}
return ret;
},
@@ -7220,10 +7328,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', '$DNS', '_addrinfo_layout', '_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 = DNS.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', '$DNS', '__hostent_struct_layout', '_read_sockaddr'],
+ 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 = DNS.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
@@ -7257,6 +7573,13 @@ LibraryManager.library = {
['i32', 'sin_zero'],
['i16', 'sin_zero_b'],
]),
+ sockaddr_in6_layout: Runtime.generateStructInfo([
+ ['i32', 'sin6_family'],
+ ['i16', 'sin6_port'],
+ ['i32', 'sin6_flowinfo'],
+ ['b16', 'sin6_addr'],
+ ['i32', 'sin6_scope_id']
+ ]),
msghdr_layout: Runtime.generateStructInfo([
['*', 'msg_name'],
['i32', 'msg_namelen'],
@@ -7266,6 +7589,10 @@ LibraryManager.library = {
['i32', 'msg_controllen'],
['i32', 'msg_flags'],
]),
+ iovec_layout: Runtime.generateStructInfo([
+ ['i8*', 'iov_base'],
+ ['i32', 'iov_len']
+ ])
},
#if SOCKET_WEBRTC
@@ -7424,7 +7751,7 @@ LibraryManager.library = {
// Stub: connection-oriented sockets are not supported yet.
},
- bind__deps: ['$FS', '$Sockets', '_inet_ntoa_raw', 'ntohs', 'mkport'],
+ bind__deps: ['$FS', '$Sockets', '_inet_ntop4_raw', 'ntohs', 'mkport'],
bind: function(fd, addr, addrlen) {
var info = FS.getStream(fd);
if (!info) return -1;
@@ -7436,7 +7763,7 @@ LibraryManager.library = {
info.port = _mkport();
}
info.addr = Sockets.localAddr; // 10.0.0.254
- info.host = __inet_ntoa_raw(info.addr);
+ info.host = __inet_ntop4_raw(info.addr);
info.close = function() {
Sockets.portmap[info.port] = undefined;
}
@@ -7445,7 +7772,7 @@ LibraryManager.library = {
info.bound = true;
},
- sendmsg__deps: ['$FS', '$Sockets', 'bind', '_inet_ntoa_raw', 'ntohs'],
+ sendmsg__deps: ['$FS', '$Sockets', 'bind', '_inet_ntop4_raw', 'ntohs'],
sendmsg: function(fd, msg, flags) {
var info = FS.getStream(fd);
if (!info) return -1;
@@ -7459,7 +7786,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_ntoa_raw(addr);
+ // var host = __inet_ntop4_raw(addr);
if (!(connection && connection.connected)) {
___setErrNo(ERRNO_CODES.EWOULDBLOCK);
@@ -7650,425 +7977,532 @@ LibraryManager.library = {
}
},
#else
- socket__deps: ['$FS', '$Sockets'],
+ // ==========================================================================
+ // 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', '$SOCKFS'],
socket: function(family, type, protocol) {
- var stream = type == {{{ cDefine('SOCK_STREAM') }}};
- if (protocol) {
- assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
+ var sock = SOCKFS.createSocket(family, type, protocol);
+ assert(sock.stream.fd < 64); // select() assumes socket fd values are in 0..63
+ return sock.stream.fd;
+ },
+
+ socketpair__deps: ['$ERRNO_CODES', '__setErrNo'],
+ socketpair: function(domain, type, protocol, sv) {
+ // int socketpair(int domain, int type, int protocol, int sv[2]);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html
+ ___setErrNo(ERRNO_CODES.EOPNOTSUPP);
+ return -1;
+ },
+
+ shutdown__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo'],
+ shutdown: function(fd, how) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- var stream = FS.createStream({
- connected: false,
- stream: stream,
- socket: true,
- stream_ops: {}
- });
- assert(stream.fd < 64); // select() assumes socket fd values are in 0..63
- return stream.fd;
+ _close(fd);
},
- 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_ntoa_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);
+ bind__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ bind: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- try {
- console.log('opening ws://' + info.host + ':' + info.port);
- info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
- info.socket.binaryType = 'arraybuffer';
- var i32Temp = new Uint32Array(1);
- var i8Temp = new Uint8Array(i32Temp.buffer);
+ var info = __read_sockaddr(addrp, addrlen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ var port = info.port;
+ var addr = DNS.lookup_addr(info.addr) || info.addr;
- info.inQueue = [];
- info.hasData = function() { return info.inQueue.length > 0 }
- if (!info.stream) {
- var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message
- }
+ try {
+ sock.sock_ops.bind(sock, addr, port);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
+ },
- info.socket.onmessage = function(event) {
- assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
- var data = new Uint8Array(event.data); // make a typed array view on the array buffer
-#if SOCKET_DEBUG
- Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]);
-#endif
- if (info.stream) {
- info.inQueue.push(data);
- } else {
- // we added headers with message sizes, read those to find discrete messages
- if (partialBuffer) {
- // append to the partial buffer
- var newBuffer = new Uint8Array(partialBuffer.length + data.length);
- newBuffer.set(partialBuffer);
- newBuffer.set(data, partialBuffer.length);
- // forget the partial buffer and work on data
- data = newBuffer;
- partialBuffer = null;
- }
- var currPos = 0;
- while (currPos+4 < data.length) {
- i8Temp.set(data.subarray(currPos, currPos+4));
- var currLen = i32Temp[0];
- assert(currLen > 0);
- if (currPos + 4 + currLen > data.length) {
- break; // not enough data has arrived
- }
- currPos += 4;
-#if SOCKET_DEBUG
- Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]);
-#endif
- info.inQueue.push(data.subarray(currPos, currPos+currLen));
- currPos += currLen;
- }
- // If data remains, buffer it
- if (currPos < data.length) {
- partialBuffer = data.subarray(currPos);
- }
- }
- }
- function send(data) {
- // TODO: if browser accepts views, can optimize this
-#if SOCKET_DEBUG
- Module.print('sender actually sending ' + Array.prototype.slice.call(data));
-#endif
- // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning
- info.socket.send(data.buffer);
- }
- var outQueue = [];
- var intervalling = false, interval;
- function trySend() {
- if (info.socket.readyState != info.socket.OPEN) {
- if (!intervalling) {
- intervalling = true;
- console.log('waiting for socket in order to send');
- interval = setInterval(trySend, 100);
- }
- return;
- }
- for (var i = 0; i < outQueue.length; i++) {
- send(outQueue[i]);
- }
- outQueue.length = 0;
- if (intervalling) {
- intervalling = false;
- clearInterval(interval);
- }
- }
- info.sender = function(data) {
- if (!info.stream) {
- // add a header with the message size
- var header = new Uint8Array(4);
- i32Temp[0] = data.length;
- header.set(i8Temp);
- outQueue.push(header);
- }
- outQueue.push(new Uint8Array(data));
- trySend();
- };
- } catch(e) {
- Module.printErr('Error in connect(): ' + e);
- ___setErrNo(ERRNO_CODES.EACCES);
+ connect__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ connect: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
- // always "fail" in non-blocking mode
- ___setErrNo(ERRNO_CODES.EINPROGRESS);
- return -1;
+ var info = __read_sockaddr(addrp, addrlen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ var port = info.port;
+ var addr = DNS.lookup_addr(info.addr) || info.addr;
+
+ try {
+ sock.sock_ops.connect(sock, addr, port);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
- recv__deps: ['$FS'],
- recv: function(fd, buf, len, flags) {
- var info = FS.getStream(fd);
- if (!info) {
+ listen__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo'],
+ listen: function(fd, backlog) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
-#if SOCKET_WEBRTC == 0
- if (!info.hasData()) {
- if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) {
- // socket has closed
- return 0;
- } else {
- // else, our socket is in a valid state but truly has nothing available
- ___setErrNo(ERRNO_CODES.EAGAIN);
- return -1;
- }
+ try {
+ sock.sock_ops.listen(sock, backlog);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
-#endif
- var buffer = info.inQueue.shift();
-#if SOCKET_DEBUG
- Module.print('recv: ' + [Array.prototype.slice.call(buffer)]);
-#endif
- if (len < buffer.length) {
- if (info.stream) {
- // This is tcp (reliable), so if not all was read, keep it
- info.inQueue.unshift(buffer.subarray(len));
-#if SOCKET_DEBUG
- Module.print('recv: put back: ' + (len - buffer.length));
-#endif
+ },
+
+ accept__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', 'socket'],
+ accept: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ var newfd = _socket(sock.family, sock.type, sock.protocol);
+ var newsock = SOCKFS.getSocket(newfd);
+ assert(newsock);
+ try {
+ sock.sock_ops.accept(sock, newsock, sock.stream.flags);
+ if (addrp) {
+ var res = __write_sockaddr(addr, newsock.family, DNS.lookup_name(newsock.daddr), newsock.dport);
+ assert(!res.errno);
}
- buffer = buffer.subarray(0, len);
+ return newfd;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- HEAPU8.set(buffer, buf);
- return buffer.length;
},
- send__deps: ['$FS'],
- send: function(fd, buf, len, flags) {
- var info = FS.getStream(fd);
- if (!info) {
+ getsockname__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_inet_pton_raw'],
+ getsockname: function (fd, addr, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
-#if SOCKET_WEBRTC == 0
- if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) {
- ___setErrNo(ERRNO_CODES.ENOTCONN);
- return -1;
- } else if (info.socket.readyState === WebSocket.CONNECTING) {
- ___setErrNo(ERRNO_CODES.EAGAIN);
+ try {
+ var info = sock.sock_ops.getname(sock);
+ var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(info.addr), info.port);
+ assert(!res.errno);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
return -1;
}
-#endif
- info.sender(HEAPU8.subarray(buf, buf+len));
- return len;
},
- sendmsg__deps: ['$FS', '$Sockets', 'connect'],
- sendmsg: function(fd, msg, flags) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
- _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ getpeername__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_inet_pton_raw'],
+ getpeername: function (fd, addr, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
- var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('sendmsg vecs: ' + num);
-#endif
- var totalSize = 0;
- for (var i = 0; i < num; i++) {
- totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
+ try {
+ var info = sock.sock_ops.getname(sock, true);
+ var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(info.addr), info.port);
+ assert(!res.errno);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- var buffer = new Uint8Array(totalSize);
- var ret = 0;
- for (var i = 0; i < num; i++) {
- var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('sendmsg curr size: ' + currNum);
-#endif
- if (!currNum) continue;
- var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
- buffer.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret);
- ret += currNum;
+ },
+
+ send__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'write'],
+ send: function(fd, buf, len, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- info.sender(buffer); // send all the iovs as a single message
- return ret;
+ // TODO honor flags
+ return _write(fd, buf, len);
},
- recvmsg__deps: ['$FS', '$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons'],
- recvmsg: function(fd, msg, flags) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
-#if SOCKET_DEBUG
- Module.print('recvmsg connecting');
-#endif
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
- _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ recv__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'read'],
+ recv: function(fd, buf, len, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- if (!info.hasData()) {
- ___setErrNo(ERRNO_CODES.EWOULDBLOCK);
+ // TODO honor flags
+ return _read(fd, buf, len);
+ },
+
+ sendto__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ sendto: function(fd, message, length, flags, dest_addr, dest_len) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
- var buffer = info.inQueue.shift();
- var bytes = buffer.length;
-#if SOCKET_DEBUG
- Module.print('recvmsg bytes: ' + bytes);
-#endif
- // write source
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'info.addr', 'i32') }}};
- {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(info.port)', 'i16') }}};
- // write data
- var ret = bytes;
- var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
- var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
- var bufferPos = 0;
- for (var i = 0; i < num && bytes > 0; i++) {
- var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('recvmsg loop ' + [i, num, bytes, currNum]);
-#endif
- if (!currNum) continue;
- currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..?
- bytes -= currNum;
- var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
-#if SOCKET_DEBUG
- Module.print('recvmsg call recv ' + currNum);
-#endif
- HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf);
- bufferPos += currNum;
+
+ // read the address and port to send to
+ var info = __read_sockaddr(dest_addr, dest_len);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
}
- if (info.stream) {
- // This is tcp (reliable), so if not all was read, keep it
- if (bufferPos < bytes) {
- info.inQueue.unshift(buffer.subarray(bufferPos));
-#if SOCKET_DEBUG
- Module.print('recvmsg: put back: ' + (bytes - bufferPos));
-#endif
- }
+ var port = info.port;
+ var addr = DNS.lookup_addr(info.addr) || info.addr;
+
+ // send the message
+ try {
+ var slab = {{{ makeGetSlabs('message', 'i8', true) }}};
+ return sock.sock_ops.sendmsg(sock, slab, message, length, addr, port);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- return ret;
},
- recvfrom__deps: ['$FS', 'connect', 'recv'],
+ recvfrom__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'],
recvfrom: function(fd, buf, len, flags, addr, addrlen) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
- //var name = {{{ makeGetValue('addr', '0', '*') }}};
- _connect(fd, addr, addrlen);
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- return _recv(fd, buf, len, flags);
- },
- shutdown__deps: ['$FS'],
- shutdown: function(fd, how) {
- var stream = FS.getStream(fd);
- if (!stream) return -1;
- stream.socket.close();
- FS.closeStream(stream);
- },
+ // read from the socket
+ var msg;
+ try {
+ msg = sock.sock_ops.recvmsg(sock, len);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
- ioctl__deps: ['$FS'],
- ioctl: function(fd, request, varargs) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- var bytes = 0;
- if (info.hasData()) {
- bytes = info.inQueue[0].length;
+ if (!msg) {
+ // socket is closed
+ return 0;
}
- var dest = {{{ makeGetValue('varargs', '0', 'i32') }}};
- {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}};
- return 0;
+
+ // write the source address out
+ if (addr) {
+ var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(msg.addr), msg.port);
+ assert(!res.errno);
+ }
+ // write the buffer out
+ HEAPU8.set(msg.buffer, buf);
+
+ return msg.buffer.byteLength;
},
- setsockopt: function(d, level, optname, optval, optlen) {
- console.log('ignoring setsockopt command');
- return 0;
+ sendmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'],
+ sendmsg: function(fd, message, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+
+ var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', '*') }}};
+ var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+
+ // read the address and port to send to
+ var addr;
+ var port;
+ var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ var namelen = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}};
+ if (name) {
+ var info = __read_sockaddr(name, namelen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ port = info.port;
+ addr = DNS.lookup_addr(info.addr) || info.addr;
+ }
+
+ // concatenate scatter-gather arrays into one message buffer
+ var total = 0;
+ for (var i = 0; i < num; i++) {
+ total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ }
+ var view = new Uint8Array(total);
+ var offset = 0;
+ for (var i = 0; i < num; i++) {
+ var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}};
+ var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ for (var j = 0; j < iovlen; j++) {
+ view[offset++] = {{{ makeGetValue('iovbase', 'j', 'i8') }}};
+ }
+ }
+
+ // write the buffer
+ try {
+ return sock.sock_ops.sendmsg(sock, view, 0, total, addr, port);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
- bind__deps: ['connect'],
- bind: function(fd, addr, addrlen) {
- _connect(fd, addr, addrlen);
- return 0;
+ recvmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_inet_pton_raw', '_write_sockaddr'],
+ recvmsg: function(fd, message, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+
+ var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
+ var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+
+ // get the total amount of data we can read across all arrays
+ var total = 0;
+ for (var i = 0; i < num; i++) {
+ total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ }
+
+ // try to read total data
+ var msg;
+ try {
+ msg = sock.sock_ops.recvmsg(sock, total);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
+
+ if (!msg) {
+ // socket is closed
+ return 0;
+ }
+
+ // TODO honor flags:
+ // MSG_OOB
+ // Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific.
+ // MSG_PEEK
+ // Peeks at the incoming message.
+ // MSG_WAITALL
+ // Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket.
+
+ // write the source address out
+ var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ if (name) {
+ var res = __write_sockaddr(name, sock.family, DNS.lookup_name(msg.addr), msg.port);
+ assert(!res.errno);
+ }
+ // write the buffer out to the scatter-gather arrays
+ var bytesRead = 0;
+ var bytesRemaining = msg.buffer.byteLength;
+
+ for (var i = 0; bytesRemaining > 0 && i < num; i++) {
+ var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}};
+ var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ if (!iovlen) {
+ continue;
+ }
+ var length = Math.min(iovlen, bytesRemaining);
+ var buf = msg.buffer.subarray(bytesRead, bytesRead + length);
+ HEAPU8.set(buf, iovbase + bytesRead);
+ bytesRead += length;
+ bytesRemaining -= length;
+ }
+
+ // TODO set msghdr.msg_flags
+ // MSG_EOR
+ // End of record was received (if supported by the protocol).
+ // MSG_OOB
+ // Out-of-band data was received.
+ // MSG_TRUNC
+ // Normal data was truncated.
+ // MSG_CTRUNC
+
+ return bytesRead;
},
- listen: function(fd, backlog) {
+ setsockopt: function(fd, level, optname, optval, optlen) {
+ console.log('ignoring setsockopt command');
return 0;
},
- accept__deps: ['$FS', '$Sockets'],
- accept: function(fd, addr, addrlen) {
- // TODO: webrtc queued incoming connections, etc.
- // For now, the model is that bind does a connect, and we "accept" that one connection,
- // which has host:port the same as ours. We also return the same socket fd.
- var info = FS.getStream(fd);
- if (!info) return -1;
- if (addr) {
- setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32');
- setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32');
- setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32');
- }
- return fd;
- },
+ // ==========================================================================
+ // select.h
+ // ==========================================================================
- select__deps: ['$FS'],
+ select__deps: ['$FS', '__DEFAULT_POLLMASK'],
select: function(nfds, readfds, writefds, exceptfds, timeout) {
// readfds are supported,
// writefds checks socket open status
// exceptfds not supported
// timeout is always 0 - fully async
- assert(!exceptfds);
-
- var errorCondition = 0;
+ assert(nfds <= 64, 'nfds must be less than or equal to 64'); // fd sets have 64 bits
+ assert(!exceptfds, 'exceptfds not supported');
- function canRead(info) {
- return (info.hasData && info.hasData()) ||
- info.socket.readyState == WebSocket.CLOSING || // let recv return 0 once closed
- info.socket.readyState == WebSocket.CLOSED;
- }
+ var total = 0;
+
+ var srcReadLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0),
+ srcReadHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0);
+ var srcWriteLow = (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0),
+ srcWriteHigh = (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0);
+ var srcExceptLow = (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0),
+ srcExceptHigh = (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
+
+ var dstReadLow = 0,
+ dstReadHigh = 0;
+ var dstWriteLow = 0,
+ dstWriteHigh = 0;
+ var dstExceptLow = 0,
+ dstExceptHigh = 0;
+
+ var allLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0) |
+ (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0) |
+ (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0);
+ var allHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0) |
+ (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0) |
+ (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
+
+ function get(fd, low, high, val) {
+ return (fd < 32 ? (low & val) : (high & val));
+ }
+
+ for (var fd = 0; fd < nfds; fd++) {
+ var mask = 1 << (fd % 32);
+ if (!(get(fd, allLow, allHigh, mask))) {
+ continue; // index isn't in the set
+ }
- function canWrite(info) {
- return info.socket && (info.socket.readyState == info.socket.OPEN);
- }
+ var stream = FS.getStream(fd);
+ if (!stream) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
- function checkfds(nfds, fds, can) {
- if (!fds) return 0;
+ var flags = ___DEFAULT_POLLMASK;
- var bitsSet = 0;
- var dstLow = 0;
- var dstHigh = 0;
- var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}};
- var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}};
- nfds = Math.min(64, nfds); // fd sets have 64 bits
+ if (stream.stream_ops.poll) {
+ flags = stream.stream_ops.poll(stream);
+ }
- for (var fd = 0; fd < nfds; fd++) {
- var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh;
- if (int_ & mask) {
- // index is in the set, check if it is ready for read
- var info = FS.getStream(fd);
- if (!info) {
- ___setErrNo(ERRNO_CODES.EBADF);
- return -1;
- }
- if (can(info)) {
- // set bit
- fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask);
- bitsSet++;
- }
- }
+ if ((flags & {{{ cDefine('POLLIN') }}}) && get(fd, srcReadLow, srcReadHigh, mask)) {
+ fd < 32 ? (dstReadLow = dstReadLow | mask) : (dstReadHigh = dstReadHigh | mask);
+ total++;
}
+ if ((flags & {{{ cDefine('POLLOUT') }}}) && get(fd, srcWriteLow, srcWriteHigh, mask)) {
+ fd < 32 ? (dstWriteLow = dstWriteLow | mask) : (dstWriteHigh = dstWriteHigh | mask);
+ total++;
+ }
+ if ((flags & {{{ cDefine('POLLPRI') }}}) && get(fd, srcExceptLow, srcExceptHigh, mask)) {
+ fd < 32 ? (dstExceptLow = dstExceptLow | mask) : (dstExceptHigh = dstExceptHigh | mask);
+ total++;
+ }
+ }
- {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}};
- {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}};
- return bitsSet;
+ if (readfds) {
+ {{{ makeSetValue('readfds', '0', 'dstReadLow', 'i32') }}};
+ {{{ makeSetValue('readfds', '4', 'dstReadHigh', 'i32') }}};
+ }
+ if (writefds) {
+ {{{ makeSetValue('writefds', '0', 'dstWriteLow', 'i32') }}};
+ {{{ makeSetValue('writefds', '4', 'dstWriteHigh', 'i32') }}};
+ }
+ if (exceptfds) {
+ {{{ makeSetValue('exceptfds', '0', 'dstExceptLow', 'i32') }}};
+ {{{ makeSetValue('exceptfds', '4', 'dstExceptHigh', 'i32') }}};
}
+
+ return total;
+ },
- var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite);
- if (errorCondition) {
+ // ==========================================================================
+ // sys/ioctl.h
+ // ==========================================================================
+
+ ioctl__deps: ['$FS'],
+ ioctl: function(fd, request, varargs) {
+ var stream = FS.getStream(fd);
+ if (!stream) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
- } else {
- return totalHandles;
}
+ var arg = {{{ makeGetValue('varargs', '0', 'i32') }}};
+ return FS.ioctl(stream, request, arg);
},
#endif
- socketpair__deps: ['__setErrNo', '$ERRNO_CODES'],
- socketpair: function(domain, type, protocol, sv) {
- // int socketpair(int domain, int type, int protocol, int sv[2]);
- // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html
- ___setErrNo(ERRNO_CODES.EOPNOTSUPP);
- return -1;
- },
-
// pty.h
openpty: function() { throw 'openpty: TODO' },
diff --git a/src/library_fs.js b/src/library_fs.js
index 1d9748d3..5573dc27 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -159,6 +159,9 @@ mergeInto(LibraryManager.library, {
isFIFO: function(mode) {
return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}};
},
+ isSocket: function(mode) {
+ return (mode & {{{ cDefine('S_IFSOCK') }}}) === {{{ cDefine('S_IFSOCK') }}};
+ },
//
// paths
@@ -400,6 +403,9 @@ mergeInto(LibraryManager.library, {
getStream: function(fd) {
return FS.streams[fd];
},
+ // TODO parameterize this function such that a stream
+ // object isn't directly passed in. not possible until
+ // SOCKFS is completed.
createStream: function(stream, fd_start, fd_end) {
var fd = FS.nextfd(fd_start, fd_end);
stream.fd = fd;
@@ -1459,6 +1465,12 @@ mergeInto(LibraryManager.library, {
throw new FS.errnoError(ERRNO_CODES.ENODEV);
}
return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags);
+ },
+ ioctl: function(stream, cmd, arg) {
+ if (!stream.stream_ops.ioctl) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTTY);
+ }
+ return stream.stream_ops.ioctl(stream, cmd, arg);
}
}
});
diff --git a/src/library_sockfs.js b/src/library_sockfs.js
index 13118b71..8e7a9579 100644
--- a/src/library_sockfs.js
+++ b/src/library_sockfs.js
@@ -3,16 +3,557 @@ mergeInto(LibraryManager.library, {
$SOCKFS__deps: ['$FS'],
$SOCKFS: {
mount: function(mount) {
- var node = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
- node.node_ops = SOCKFS.node_ops;
- node.stream_ops = SOCKFS.stream_ops;
- return node;
+ return FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0);
},
- node_ops: {
+ nextname: function() {
+ if (!SOCKFS.nextname.current) {
+ SOCKFS.nextname.current = 0;
+ }
+ return 'socket[' + (SOCKFS.nextname.current++) + ']';
},
+ createSocket: function(family, type, protocol) {
+ var streaming = type == {{{ cDefine('SOCK_STREAM') }}};
+ if (protocol) {
+ assert(streaming == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
+ }
+
+ // create our internal socket structure
+ var sock = {
+ family: family,
+ type: type,
+ protocol: protocol,
+ server: null,
+ peers: {},
+ pending: [],
+ recv_queue: [],
+#if SOCKET_WEBRTC
+#else
+ sock_ops: SOCKFS.websocket_sock_ops
+#endif
+ };
+
+ // create the filesystem node to store the socket structure
+ var name = SOCKFS.nextname();
+ var node = FS.createNode(SOCKFS.root, name, {{{ cDefine('S_IFSOCK') }}}, 0);
+ node.sock = sock;
+
+ // and the wrapping stream that enables library functions such
+ // as read and write to indirectly interact with the socket
+ var stream = FS.createStream({
+ path: name,
+ node: node,
+ flags: FS.modeStringToFlags('r+'),
+ seekable: false,
+ stream_ops: SOCKFS.stream_ops
+ });
+
+ // map the new stream to the socket structure (sockets have a 1:1
+ // relationship with a stream)
+ sock.stream = stream;
+
+ return sock;
+ },
+ getSocket: function(fd) {
+ var stream = FS.getStream(fd);
+ if (!stream || !FS.isSocket(stream.node.mode)) {
+ return null;
+ }
+ return stream.node.sock;
+ },
+ // node and stream ops are backend agnostic
stream_ops: {
+ poll: function(stream) {
+ var sock = stream.node.sock;
+ return sock.sock_ops.poll(sock);
+ },
+ ioctl: function(stream, request, varargs) {
+ var sock = stream.node.sock;
+ return sock.sock_ops.ioctl(sock, request, varargs);
+ },
+ read: function(stream, buffer, offset, length, position /* ignored */) {
+ var sock = stream.node.sock;
+ var msg = sock.sock_ops.recvmsg(sock, length);
+ if (!msg) {
+ // socket is closed
+ return 0;
+ }
+#if USE_TYPED_ARRAYS == 2
+ buffer.set(msg.buffer, offset);
+#else
+ for (var i = 0; i < size; i++) {
+ buffer[offset + i] = msg.buffer[i];
+ }
+#endif
+ return msg.buffer.length;
+ },
+ write: function(stream, buffer, offset, length, position /* ignored */) {
+ var sock = stream.node.sock;
+ return sock.sock_ops.sendmsg(sock, buffer, offset, length);
+ },
+ close: function(stream) {
+ var sock = stream.node.sock;
+ sock.sock_ops.close(sock);
+ }
},
+ // backend-specific stream ops
websocket_sock_ops: {
+ //
+ // peers are a small wrapper around a WebSocket to help in
+ // emulating dgram sockets
+ //
+ // these functions aren't actually sock_ops members, but we're
+ // abusing the namespace to organize them
+ //
+ createPeer: function(sock, addr, port) {
+ var ws;
+
+ if (typeof addr === 'object') {
+ ws = addr;
+ addr = null;
+ port = null;
+ }
+
+ if (ws) {
+ // for sockets that've already connected (e.g. we're the server)
+ // we can inspect the _socket property for the address
+ if (ws._socket) {
+ addr = ws._socket.remoteAddress;
+ port = ws._socket.remotePort;
+ }
+ // if we're just now initializing a connection to the remote,
+ // inspect the url property
+ else {
+ var result = /ws[s]?:\/\/([^:]+):(\d+)/.exec(ws.url);
+ if (!result) {
+ throw new Error('WebSocket URL must be in the format ws(s)://address:port');
+ }
+ addr = result[1];
+ port = parseInt(result[2], 10);
+ }
+ } else {
+ // create the actual websocket object and connect
+ try {
+ var url = 'ws://' + addr + ':' + port;
+#if SOCKET_DEBUG
+ console.log('connect: ' + url);
+#endif
+ // the node ws library API is slightly different than the browser's
+ var opts = ENVIRONMENT_IS_NODE ? {} : ['binary'];
+ ws = new WebSocket(url, opts);
+ ws.binaryType = 'arraybuffer';
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EHOSTUNREACH);
+ }
+ }
+
+ var peer = {
+ addr: addr,
+ port: port,
+ socket: ws,
+ send_queue: []
+ };
+
+ SOCKFS.websocket_sock_ops.addPeer(sock, peer);
+ SOCKFS.websocket_sock_ops.handlePeerEvents(sock, peer);
+
+ // if this is a bound dgram socket, send the port number first to allow
+ // us to override the ephemeral port reported to us by remotePort on the
+ // remote end.
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}} && typeof sock.sport !== 'undefined') {
+ peer.send_queue.push(new Uint8Array([
+ 255, 255, 255, 255,
+ 'p'.charCodeAt(0), 'o'.charCodeAt(0), 'r'.charCodeAt(0), 't'.charCodeAt(0),
+ ((sock.sport & 0xff00) >> 8) , (sock.sport & 0xff)
+ ]));
+ }
+
+ return peer;
+ },
+ getPeer: function(sock, addr, port) {
+ return sock.peers[addr + ':' + port];
+ },
+ addPeer: function(sock, peer) {
+ sock.peers[peer.addr + ':' + peer.port] = peer;
+ },
+ removePeer: function(sock, peer) {
+ delete sock.peers[peer.addr + ':' + peer.port];
+ },
+ handlePeerEvents: function(sock, peer) {
+ var first = true;
+
+ var handleOpen = function () {
+ try {
+ var queued = peer.send_queue.shift();
+ while (queued) {
+#if SOCKET_DEBUG
+ Module.print('websocket sending queued data (' + queued.byteLength + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(queued))]);
+#endif
+ peer.socket.send(queued);
+ queued = peer.send_queue.shift();
+ }
+ } catch (e) {
+ peer.socket.close();
+ }
+ };
+
+ var handleMessage = function(data) {
+ assert(typeof data !== 'string' && data.byteLength !== undefined); // must receive an ArrayBuffer
+ data = new Uint8Array(data); // make a typed array view on the array buffer
+
+ // if this is the port message, override the peer's port with it
+ var wasfirst = first;
+ first = false;
+ if (wasfirst &&
+ data.length === 10 &&
+ data[0] === 255 && data[1] === 255 && data[2] === 255 && data[3] === 255 &&
+ data[4] === 'p'.charCodeAt(0) && data[5] === 'o'.charCodeAt(0) && data[6] === 'r'.charCodeAt(0) && data[7] === 't'.charCodeAt(0)) {
+ // update the peer's port and it's key in the peer map
+ var newport = ((data[8] << 8) | data[9]);
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
+ peer.port = newport;
+ SOCKFS.websocket_sock_ops.addPeer(sock, peer);
+ return;
+ }
+
+ sock.recv_queue.push({ addr: peer.addr, port: peer.port, data: data });
+ };
+
+ if (ENVIRONMENT_IS_NODE) {
+ peer.socket.on('open', handleOpen);
+ peer.socket.on('message', function(data, flags) {
+ if (!flags.binary) {
+ return;
+ }
+ handleMessage((new Uint8Array(data)).buffer); // copy from node Buffer -> ArrayBuffer
+ });
+ peer.socket.on('error', function() {
+ // don't throw
+ });
+ } else {
+ peer.socket.onopen = handleOpen;
+ peer.socket.onmessage = function(event) {
+ handleMessage(event.data);
+ };
+ }
+ },
+
+ //
+ // actual sock ops
+ //
+ poll: function(sock) {
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && sock.server) {
+ // listen sockets should only say they're available for reading
+ // if there are pending clients.
+ return sock.pending.length ? ({{{ cDefine('POLLRDNORM') }}} | {{{ cDefine('POLLIN') }}}) : 0;
+ }
+
+ var mask = 0;
+ var dest = sock.type === {{{ cDefine('SOCK_STREAM') }}} ? // we only care about the socket state for connection-based sockets
+ SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport) :
+ null;
+
+ if (sock.recv_queue.length ||
+ !dest || // connection-less sockets are always ready to read
+ (dest && dest.socket.readyState === dest.socket.CLOSING) ||
+ (dest && dest.socket.readyState === dest.socket.CLOSED)) { // let recv return 0 once closed
+ mask |= ({{{ cDefine('POLLRDNORM') }}} | {{{ cDefine('POLLIN') }}});
+ }
+
+ if (!dest || // connection-less sockets are always ready to write
+ (dest && dest.socket.readyState === dest.socket.OPEN)) {
+ mask |= {{{ cDefine('POLLOUT') }}};
+ }
+
+ if ((dest && dest.socket.readyState === dest.socket.CLOSING) ||
+ (dest && dest.socket.readyState === dest.socket.CLOSED)) {
+ mask |= {{{ cDefine('POLLHUP') }}};
+ }
+
+ return mask;
+ },
+ ioctl: function(sock, request, arg) {
+ switch (request) {
+ case {{{ cDefine('FIONREAD') }}}:
+ var bytes = 0;
+ if (sock.recv_queue.length) {
+ bytes = sock.recv_queue[0].data.length;
+ }
+ {{{ makeSetValue('arg', '0', 'bytes', 'i32') }}};
+ return 0;
+ default:
+ return ERRNO_CODES.EINVAL;
+ }
+ },
+ close: function(sock) {
+ // if we've spawned a listen server, close it
+ if (sock.server) {
+ try {
+ sock.server.close();
+ } catch (e) {
+ }
+ sock.server = null;
+ }
+ // close any peer connections
+ var peers = Object.keys(sock.peers);
+ for (var i = 0; i < peers.length; i++) {
+ var peer = sock.peers[peers[i]];
+ try {
+ peer.socket.close();
+ } catch (e) {
+ }
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
+ }
+ return 0;
+ },
+ bind: function(sock, addr, port) {
+ if (typeof sock.saddr !== 'undefined' || typeof sock.sport !== 'undefined') {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL); // already bound
+ }
+ sock.saddr = addr;
+ sock.sport = port || _mkport();
+ // in order to emulate dgram sockets, we need to launch a listen server when
+ // binding on a connection-less socket
+ // note: this is only required on the server side
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
+ // close the existing server if it exists
+ if (sock.server) {
+ sock.server.close();
+ sock.server = null;
+ }
+ // swallow error operation not supported error that occurs when binding in the
+ // browser where this isn't supported
+ try {
+ sock.sock_ops.listen(sock, 0);
+ } catch (e) {
+ if (!(e instanceof FS.ErrnoError)) throw e;
+ if (e.errno !== ERRNO_CODES.EOPNOTSUPP) throw e;
+ }
+ }
+ },
+ connect: function(sock, addr, port) {
+ if (sock.server) {
+ throw new FS.ErrnoError(ERRNO_CODS.EOPNOTSUPP);
+ }
+
+ // TODO autobind
+ // if (!sock.addr && sock.type == {{{ cDefine('SOCK_DGRAM') }}}) {
+ // }
+
+ // early out if we're already connected / in the middle of connecting
+ if (typeof sock.daddr !== 'undefined' && typeof sock.dport !== 'undefined') {
+ var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport);
+ if (dest) {
+ if (dest.socket.readyState === dest.socket.CONNECTING) {
+ throw new FS.ErrnoError(ERRNO_CODES.EALREADY);
+ } else {
+ throw new FS.ErrnoError(ERRNO_CODES.EISCONN);
+ }
+ }
+ }
+
+ // add the socket to our peer list and set our
+ // defination address / port to match
+ var peer = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
+ sock.daddr = peer.addr;
+ sock.dport = peer.port;
+
+ // always "fail" in non-blocking mode
+ throw new FS.ErrnoError(ERRNO_CODES.EINPROGRESS);
+ },
+ listen: function(sock, backlog) {
+ if (!ENVIRONMENT_IS_NODE) {
+ throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP);
+ }
+ if (sock.server) {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL); // already listening
+ }
+ var WebSocketServer = require('ws').Server;
+ var host = sock.saddr;
+#if SOCKET_DEBUG
+ console.log('listen: ' + host + ':' + sock.sport);
+#endif
+ sock.server = new WebSocketServer({
+ host: host,
+ port: sock.sport
+ // TODO support backlog
+ });
+
+ sock.server.on('connection', function(ws) {
+#if SOCKET_DEBUG
+ console.log('received connection from: ' + ws._socket.remoteAddress + ':' + ws._socket.remotePort);
+#endif
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
+ // push to queue for accept to pick up
+ sock.pending.push(ws);
+ } else {
+ // auto-accept, adding the peer to the listen socket so
+ // calling sendto with the listen socket and an address
+ // will resolve to the correct client
+ SOCKFS.websocket_sock_ops.createPeer(sock, ws);
+ }
+ });
+ sock.server.on('closed', function() {
+ sock.server = null;
+ });
+ sock.server.on('error', function() {
+ // don't throw
+ });
+ },
+ accept: function(listensock, newsock, flags) {
+ if (!listensock.server) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return -1;
+ }
+
+ // create a peer on the server's client socket
+ var peer = SOCKFS.websocket_sock_ops.createPeer(newsock, listensock.pending.shift());
+ newsock.daddr = peer.addr;
+ newsock.dport = peer.port;
+ newsock.stream.flags = flags;
+
+ return 0;
+ },
+ getname: function(sock, peer) {
+ var addr, port;
+ if (peer) {
+ if (sock.daddr === undefined || sock.dport === undefined) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ }
+ addr = sock.daddr;
+ port = sock.dport;
+ } else {
+ // TODO saddr and sport will be set for bind()'d UDP sockets, but what
+ // should we be returning for TCP sockets that've been connect()'d?
+ addr = sock.saddr || 0;
+ port = sock.sport || 0;
+ }
+ return { addr: addr, port: port };
+ },
+ sendmsg: function(sock, buffer, offset, length, addr, port) {
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
+ // connection-less sockets will honor the message address,
+ // and otherwise fall back to the bound destination address
+ if (addr === undefined || port === undefined) {
+ addr = sock.daddr;
+ port = sock.dport;
+ }
+ // if there was no address to fall back to, error out
+ if (addr === undefined || port === undefined) {
+ throw new FS.ErrnoError(ERRNO_CODES.EDESTADDRREQ);
+ }
+ } else {
+ // connection-based sockets will only use the bound
+ addr = sock.daddr;
+ port = sock.dport;
+ }
+
+ // find the peer for the destination address
+ var dest = SOCKFS.websocket_sock_ops.getPeer(sock, addr, port);
+
+ // early out if not connected with a connection-based socket
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
+ if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ } else if (dest.socket.readyState === dest.socket.CONNECTING) {
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ }
+
+ // create a copy of the incoming data to send, as the WebSocket API
+ // doesn't work entirely with an ArrayBufferView, it'll just send
+ // the entire underlying buffer
+ var data;
+ if (buffer instanceof Array || buffer instanceof ArrayBuffer) {
+ data = buffer.slice(offset, offset + length);
+ } else { // ArrayBufferView
+ data = buffer.buffer.slice(buffer.byteOffset + offset, buffer.byteOffset + offset + length);
+ }
+
+ // if we're emulating a connection-less dgram socket and don't have
+ // a cached connection, queue the buffer to send upon connect and
+ // lie, saying the data was sent now.
+ if (sock.type === {{{ cDefine('SOCK_DGRAM') }}}) {
+ if (!dest || dest.socket.readyState !== dest.socket.OPEN) {
+ // if we're not connected, open a new connection
+ if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
+ dest = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
+ }
+#if SOCKET_DEBUG
+ Module.print('websocket queuing (' + length + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(data))]);
+#endif
+ dest.send_queue.push(data);
+ return length;
+ }
+ }
+
+ try {
+#if SOCKET_DEBUG
+ Module.print('websocket send (' + length + ' bytes): ' + [Array.prototype.slice.call(new Uint8Array(data))]);
+#endif
+ // send the actual data
+ dest.socket.send(data);
+ return length;
+ } catch (e) {
+ throw new FS.ErrnoError(ERRNO_CODES.EINVAL);
+ }
+ },
+ recvmsg: function(sock, length) {
+ // http://pubs.opengroup.org/onlinepubs/7908799/xns/recvmsg.html
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && sock.server) {
+ // tcp servers should not be recv()'ing on the listen socket
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ }
+
+ var queued = sock.recv_queue.shift();
+ if (!queued) {
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}}) {
+ var dest = SOCKFS.websocket_sock_ops.getPeer(sock, sock.daddr, sock.dport);
+
+ if (!dest) {
+ // if we have a destination address but are not connected, error out
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTCONN);
+ }
+ else if (dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
+ // return null if the socket has closed
+ return null;
+ }
+ else {
+ // else, our socket is in a valid state but truly has nothing available
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ } else {
+ throw new FS.ErrnoError(ERRNO_CODES.EAGAIN);
+ }
+ }
+
+ // queued.data will be an ArrayBuffer if it's unadulterated, but if it's
+ // requeued TCP data it'll be an ArrayBufferView
+ var queuedLength = queued.data.byteLength || queued.data.length;
+ var queuedOffset = queued.data.byteOffset || 0;
+ var queuedBuffer = queued.data.buffer || queued.data;
+ var bytesRead = Math.min(length, queuedLength);
+ var res = {
+ buffer: new Uint8Array(queuedBuffer, queuedOffset, bytesRead),
+ addr: queued.addr,
+ port: queued.port
+ };
+
+#if SOCKET_DEBUG
+ Module.print('websocket read (' + bytesRead + ' bytes): ' + [Array.prototype.slice.call(res.buffer)]);
+#endif
+
+ // push back any unread data for TCP connections
+ if (sock.type === {{{ cDefine('SOCK_STREAM') }}} && bytesRead < queuedLength) {
+ var bytesRemaining = queuedLength - bytesRead;
+#if SOCKET_DEBUG
+ Module.print('websocket read: put back ' + bytesRemaining + ' bytes');
+#endif
+ queued.data = new Uint8Array(queuedBuffer, queuedOffset + bytesRead, bytesRemaining);
+ sock.recv_queue.unshift(queued);
+ }
+
+ return res;
+ }
}
}
}); \ No newline at end of file
diff --git a/src/settings.js b/src/settings.js
index 02423ba5..b40330b8 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',
@@ -718,6 +719,7 @@ var C_DEFINES = {
'PM_STR': '6',
'POLLERR': '8',
'POLLHUP': '16',
+ 'POLLPRI': '32',
'POLLIN': '1',
'POLLNVAL': '4',
'POLLOUT': '2',
@@ -1457,6 +1459,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/runner.py b/tests/runner.py
index bbbc23e5..f0e61c4e 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -63,6 +63,10 @@ class RunnerCore(unittest.TestCase):
self.working_dir = dirname
os.chdir(dirname)
+ # Use emscripten root for node module lookup
+ scriptdir = os.path.dirname(os.path.abspath(__file__))
+ os.environ['NODE_PATH'] = os.path.join(scriptdir, '..', 'node_modules')
+
if not self.save_dir:
self.has_prev_ll = False
for temp_file in os.listdir(TEMP_DIR):
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/sockets/test_sockets_msg.h b/tests/sockets/test_sockets_msg.h
index 30094d65..b60b056a 100644
--- a/tests/sockets/test_sockets_msg.h
+++ b/tests/sockets/test_sockets_msg.h
@@ -52,6 +52,7 @@ int do_msg_write(int sockfd, msg_t *msg, int offset, int length, struct sockaddr
assert(errno == EAGAIN);
return res;
}
+ printf("do_msg_write: sending message header for %d bytes\n", msg->length);
assert(res == sizeof(int));
}
diff --git a/tests/sockets/test_sockets_select_server_down_client.c b/tests/sockets/test_sockets_select_server_down_client.c
index e05bd4c8..27e200e0 100644
--- a/tests/sockets/test_sockets_select_server_down_client.c
+++ b/tests/sockets/test_sockets_select_server_down_client.c
@@ -48,7 +48,7 @@ void iter(void *arg) {
char buffer[1024];
int n = recv(sockfd, buffer, sizeof(buffer), 0);
if (n == -1 && retries++ > 10) {
- perror("revv failed");
+ perror("recv failed");
finish(EXIT_FAILURE);
} else if (!n) {
perror("Connection to websocket server failed as expected.");
diff --git a/tests/test_core.py b/tests/test_core.py
index 491db66c..bd6d9a03 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -7437,168 +7437,6 @@ def process(filename):
'''
self.do_run(src, re.sub('(^|\n)\s+', '\\1', expected))
- def test_inet(self):
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
-
- int main() {
- printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf));
- in_addr_t i = inet_addr("190.180.10.78");
- printf("%x\n", i);
- return 0;
- }
- '''
- self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n')
-
- def test_inet2(self):
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
-
- int main() {
- struct in_addr x, x2;
- int *y = (int*)&x;
- *y = 0x12345678;
- printf("%s\n", inet_ntoa(x));
- int r = inet_aton(inet_ntoa(x), &x2);
- printf("%s\n", inet_ntoa(x2));
- return 0;
- }
- '''
- self.do_run(src, '120.86.52.18\n120.86.52.18\n')
-
- def test_inet3(self):
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- int main() {
- char dst[64];
- struct in_addr x, x2;
- int *y = (int*)&x;
- *y = 0x12345678;
- printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst));
- int r = inet_aton(inet_ntoa(x), &x2);
- printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst));
- return 0;
- }
- '''
- self.do_run(src, '120.86.52.18\n120.86.52.18\n')
-
- def test_inet4(self):
- if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
-
- src = r'''
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
-
- void test(char *test_addr){
- char str[40];
- struct in6_addr addr;
- unsigned char *p = (unsigned char*)&addr;
- int ret;
- ret = inet_pton(AF_INET6,test_addr,&addr);
- if(ret == -1) return;
- if(ret == 0) return;
- if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return;
- printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n",
- p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str);
- }
- int main(){
- test("::");
- test("::1");
- test("::1.2.3.4");
- test("::17.18.19.20");
- test("::ffff:1.2.3.4");
- test("1::ffff");
- test("::255.255.255.255");
- test("0:ff00:1::");
- test("0:ff::");
- test("abcd::");
- test("ffff::a");
- test("ffff::a:b");
- test("ffff::a:b:c");
- test("ffff::a:b:c:d");
- test("ffff::a:b:c:d:e");
- test("::1:2:0:0:0");
- test("0:0:1:2:3::");
- test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
- test("1::255.255.255.255");
-
- //below should fail and not produce results..
- test("1.2.3.4");
- test("");
- test("-");
- }
- '''
- self.do_run(src,
- "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n"
- "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n"
- "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n"
- "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n"
- "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n"
- "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n"
- "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n"
- "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n"
- "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n"
- "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n"
- "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n"
- "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n"
- "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n"
- "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n"
- "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n"
- "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n"
- "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n"
- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n"
- "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>
-
- 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++;
- }
- }
-
- 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_799(self):
src = open(path_from_root('tests', '799.cpp'), 'r').read()
self.do_run(src, '''Set PORT family: 0, port: 3979
diff --git a/tests/test_sockets.py b/tests/test_sockets.py
index 82ddc6fe..b2116e21 100644
--- a/tests/test_sockets.py
+++ b/tests/test_sockets.py
@@ -34,11 +34,11 @@ def make_relay_server(port1, port2):
return proc
class WebsockifyServerHarness:
- def __init__(self, filename, args, listen_port, target_port):
+ def __init__(self, filename, args, listen_port):
self.pids = []
self.filename = filename
- self.target_port = target_port
self.listen_port = listen_port
+ self.target_port = listen_port-1
self.args = args or []
def __enter__(self):
@@ -48,7 +48,7 @@ class WebsockifyServerHarness:
# NOTE empty filename support is a hack to support
# the current test_enet
if self.filename:
- Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server'] + self.args).communicate()
+ Popen([CLANG_CC, path_from_root('tests', self.filename), '-o', 'server', '-DSOCKK=%d' % self.target_port] + self.args).communicate()
process = Popen([os.path.abspath('server')])
self.pids.append(process.pid)
@@ -71,16 +71,21 @@ class WebsockifyServerHarness:
class CompiledServerHarness:
- def __init__(self, filename, args):
+ def __init__(self, filename, args, listen_port):
self.pids = []
self.filename = filename
+ self.listen_port = listen_port
self.args = args or []
def __enter__(self):
- import socket, websockify
+ # assuming this is only used for WebSocket tests at the moment, validate that
+ # the ws module is installed
+ child = Popen([NODE_JS, '-e', 'require("ws");'])
+ child.communicate()
+ assert child.returncode == 0, 'ws module for Node.js not installed. Please run \'npm install\' from %s' % EMSCRIPTEN_ROOT
# compile the server
- Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js'] + self.args).communicate()
+ Popen([PYTHON, EMCC, path_from_root('tests', self.filename), '-o', 'server.js', '-DSOCKK=%d' % self.listen_port] + self.args).communicate()
process = Popen([NODE_JS, 'server.js'])
self.pids.append(process.pid)
@@ -96,72 +101,196 @@ class CompiledServerHarness:
# proper listen server support.
class sockets(BrowserCore):
+ def test_inet(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+
+ int main() {
+ printf("*%x,%x,%x,%x,%x,%x*\n", htonl(0xa1b2c3d4), htonl(0xfe3572e0), htonl(0x07abcdf0), htons(0xabcd), ntohl(0x43211234), ntohs(0xbeaf));
+ in_addr_t i = inet_addr("190.180.10.78");
+ printf("%x\n", i);
+ return 0;
+ }
+ '''
+ self.do_run(src, '*d4c3b2a1,e07235fe,f0cdab07,cdab,34122143,afbe*\n4e0ab4be\n')
+
+ def test_inet2(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+
+ int main() {
+ struct in_addr x, x2;
+ int *y = (int*)&x;
+ *y = 0x12345678;
+ printf("%s\n", inet_ntoa(x));
+ int r = inet_aton(inet_ntoa(x), &x2);
+ printf("%s\n", inet_ntoa(x2));
+ return 0;
+ }
+ '''
+ self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+
+ def test_inet3(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+ int main() {
+ char dst[64];
+ struct in_addr x, x2;
+ int *y = (int*)&x;
+ *y = 0x12345678;
+ printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst));
+ int r = inet_aton(inet_ntoa(x), &x2);
+ printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst));
+ return 0;
+ }
+ '''
+ self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+
+ def test_inet4(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
+
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+
+ void test(char *test_addr){
+ char str[40];
+ struct in6_addr addr;
+ unsigned char *p = (unsigned char*)&addr;
+ int ret;
+ ret = inet_pton(AF_INET6,test_addr,&addr);
+ if(ret == -1) return;
+ if(ret == 0) return;
+ if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return;
+ printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n",
+ p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str);
+ }
+ int main(){
+ test("::");
+ test("::1");
+ test("::1.2.3.4");
+ test("::17.18.19.20");
+ test("::ffff:1.2.3.4");
+ test("1::ffff");
+ test("::255.255.255.255");
+ test("0:ff00:1::");
+ test("0:ff::");
+ test("abcd::");
+ test("ffff::a");
+ test("ffff::a:b");
+ test("ffff::a:b:c");
+ test("ffff::a:b:c:d");
+ test("ffff::a:b:c:d:e");
+ test("::1:2:0:0:0");
+ test("0:0:1:2:3::");
+ test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ test("1::255.255.255.255");
+
+ //below should fail and not produce results..
+ test("1.2.3.4");
+ test("");
+ test("-");
+ }
+ '''
+ self.do_run(src,
+ "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n"
+ "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n"
+ "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n"
+ "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n"
+ "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n"
+ "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n"
+ "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n"
+ "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n"
+ "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n"
+ "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n"
+ "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n"
+ "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n"
+ "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n"
+ "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n"
+ "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n"
+ "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n"
+ "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n"
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n"
+ "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n"
+ )
+
+ def test_getaddrinfo(self):
+ self.do_run(open(path_from_root('tests', 'sockets', 'test_getaddrinfo.c')).read(), 'success')
+
+ def test_getnameinfo(self):
+ self.do_run(open(path_from_root('tests', 'sockets', 'test_getnameinfo.c')).read(), 'success')
+
+ 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')
- for datagram in [0]:
- dgram_define = '-DTEST_DGRAM=%d' % datagram
+ # Websockify-proxied servers can't run dgram tests
+ harnesses = [
+ (WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49160), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49161), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49162), 1)
+ ]
- for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include], 8991, 8990)
- # CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8990', dgram_define, sockets_include])
- ]:
- with harness:
- self.btest(os.path.join('sockets', 'test_sockets_echo_client.c'), expected='0', args=['-DSOCKK=8991', dgram_define, sockets_include])
+ for harness, datagram in harnesses:
+ with harness:
+ self.btest(os.path.join('sockets', 'test_sockets_echo_client.c'), expected='0', args=['-DSOCKK=%d' % harness.listen_port, '-DTEST_DGRAM=%d' % datagram, sockets_include])
def test_sockets_echo_bigdata(self):
sockets_include = '-I'+path_from_root('tests', 'sockets')
- for datagram in [0]:
- dgram_define = '-DTEST_DGRAM=%d' % datagram
+ # generate a large string literal to use as our message
+ message = ''
+ for i in range(256*256*2):
+ message += str(unichr(ord('a') + (i % 26)))
- # generate a large string literal to use as our message
- message = ''
- for i in range(256*256*2):
- message += str(unichr(ord('a') + (i % 26)))
+ # re-write the client test with this literal (it's too big to pass via command line)
+ input_filename = path_from_root('tests', 'sockets', 'test_sockets_echo_client.c')
+ input = open(input_filename).read()
+ output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message)
- # re-write the client test with this literal (it's too big to pass via command line)
- input_filename = path_from_root('tests', 'sockets', 'test_sockets_echo_client.c')
- input = open(input_filename).read()
- output = input.replace('#define MESSAGE "pingtothepong"', '#define MESSAGE "%s"' % message)
+ harnesses = [
+ (WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49170), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=0'], 49171), 0),
+ (CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include, '-DTEST_DGRAM=1'], 49172), 1)
+ ]
- for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=8992', dgram_define, sockets_include], 8993, 8992)
- ]:
- with harness:
- self.btest(output, expected='0', args=['-DSOCKK=8993', dgram_define, sockets_include], force_c=True)
+ for harness, datagram in harnesses:
+ with harness:
+ self.btest(output, expected='0', args=[sockets_include, '-DSOCKK=%d' % harness.listen_port, '-DTEST_DGRAM=%d' % datagram], force_c=True)
def test_sockets_partial(self):
for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), ['-DSOCKK=8994'], 8995, 8994)
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), [], 49180),
+ CompiledServerHarness(os.path.join('sockets', 'test_sockets_partial_server.c'), [], 49181)
]:
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'])
+ self.btest(os.path.join('sockets', 'test_sockets_partial_client.c'), expected='165', args=['-DSOCKK=%d' % harness.listen_port])
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)
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_select_server_down_server.c'), [], 49190),
+ CompiledServerHarness(os.path.join('sockets', 'test_sockets_select_server_down_server.c'), [], 49191)
]:
with harness:
- self.btest(os.path.join('sockets', 'test_sockets_select_server_down_client.c'), expected='266', args=['-DSOCKK=9003'])
+ self.btest(os.path.join('sockets', 'test_sockets_select_server_down_client.c'), expected='266', args=['-DSOCKK=%d' % harness.listen_port])
def test_sockets_select_server_closes_connection_rw(self):
sockets_include = '-I'+path_from_root('tests', 'sockets')
for harness in [
- WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), ['-DSOCKK=9004', sockets_include], 9005, 9004)
+ WebsockifyServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49200),
+ CompiledServerHarness(os.path.join('sockets', 'test_sockets_echo_server.c'), [sockets_include], 49201)
]:
with harness:
- self.btest(os.path.join('sockets', 'test_sockets_select_server_closes_connection_client_rw.c'), expected='266', args=['-DSOCKK=9005', sockets_include])
+ self.btest(os.path.join('sockets', 'test_sockets_select_server_closes_connection_client_rw.c'), expected='266', args=[sockets_include, '-DSOCKK=%d' % harness.listen_port])
- # TODO remove this once we have proper listen server support built into emscripten.
- # being that enet uses datagram sockets, we can't proxy to a native server with
- # websockify, so we're emulating the listen server in the browser and relaying
- # between two TCP servers.
def test_enet(self):
try_delete(self.in_dir('enet'))
shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet'))
@@ -171,20 +300,18 @@ class sockets(BrowserCore):
Popen([PYTHON, path_from_root('emmake'), 'make']).communicate()
enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')]
os.chdir(pwd)
- Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate()
-
- with WebsockifyServerHarness('', [], 2235, 2234):
- with WebsockifyServerHarness('', [], 2237, 2236):
- pids = []
- try:
- proc = make_relay_server(2234, 2236)
- pids.append(proc.pid)
- self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=2237', '-DUSE_IFRAME'] + enet)
- finally:
- clean_pids(pids);
-
- # TODO use this once we have listen server support
- # def test_enet(self):
+
+ for harness in [
+ CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), enet, 49210)
+ ]:
+ with harness:
+ self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=enet + ['-DSOCKK=%d' % harness.listen_port])
+
+ # This test is no longer in use for WebSockets as we can't truly emulate
+ # a server in the browser (in the past, there were some hacks to make it
+ # somewhat work, but those have been removed). However, with WebRTC it
+ # should be able to resurect this test.
+ # def test_enet_in_browser(self):
# try_delete(self.in_dir('enet'))
# shutil.copytree(path_from_root('tests', 'enet'), self.in_dir('enet'))
# pwd = os.getcwd()
@@ -193,12 +320,17 @@ class sockets(BrowserCore):
# Popen([PYTHON, path_from_root('emmake'), 'make']).communicate()
# enet = [self.in_dir('enet', '.libs', 'libenet.a'), '-I'+path_from_root('tests', 'enet', 'include')]
# os.chdir(pwd)
-
- # for harness in [
- # self.CompiledServerHarness(os.path.join('sockets', 'test_enet_server.c'), ['-DSOCKK=9010'] + enet, 9011, 9010)
- # ]:
- # with harness:
- # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=9011'] + enet)
+ # Popen([PYTHON, EMCC, path_from_root('tests', 'sockets', 'test_enet_server.c'), '-o', 'server.html', '-DSOCKK=2235'] + enet).communicate()
+
+ # with WebsockifyServerHarness('', [], 2235, 2234):
+ # with WebsockifyServerHarness('', [], 2237, 2236):
+ # pids = []
+ # try:
+ # proc = make_relay_server(2234, 2236)
+ # pids.append(proc.pid)
+ # self.btest(os.path.join('sockets', 'test_enet_client.c'), expected='0', args=['-DSOCKK=2237', '-DUSE_IFRAME=1'] + enet)
+ # finally:
+ # clean_pids(pids);
def test_webrtc(self):
host_src = 'webrtc_host.c'