diff options
Diffstat (limited to 'src/library.js')
-rw-r--r-- | src/library.js | 271 |
1 files changed, 256 insertions, 15 deletions
diff --git a/src/library.js b/src/library.js index 594ba931..69642151 100644 --- a/src/library.js +++ b/src/library.js @@ -382,12 +382,14 @@ LibraryManager.library = { // You can also call this with a typed array instead of a url. It will then // do preloading for the Image/Audio part, as if the typed array were the // result of an XHR that you did manually. - createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror) { + createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) { Browser.ensureObjects(); var fullname = FS.joinPath([parent, name], true); function processData(byteArray) { function finish(byteArray) { - FS.createDataFile(parent, name, byteArray, canRead, canWrite); + if (!dontCreateFile) { + FS.createDataFile(parent, name, byteArray, canRead, canWrite); + } if (onload) onload(); removeRunDependency('cp ' + fullname); } @@ -1286,7 +1288,7 @@ LibraryManager.library = { return 0; case {{{ cDefine('F_SETOWN') }}}: case {{{ cDefine('F_GETOWN') }}}: - // These are for sockets. We don't have them implemented (yet?). + // These are for sockets. We don't have them fully implemented yet. ___setErrNo(ERRNO_CODES.EINVAL); return -1; default: @@ -2498,8 +2500,8 @@ LibraryManager.library = { while ((curr < max_ || isNaN(max_)) && next > 0) { if (!(next in __scanString.whiteSpace) && // stop on whitespace (type == 's' || - ((type === 'd' || type == 'u') && ((next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) || - (first && next == '-'.charCodeAt(0)))) || + ((type === 'd' || type == 'u' || type == 'i') && ((next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) || + (first && next == '-'.charCodeAt(0)))) || (type === 'x' && (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0) || next >= 'a'.charCodeAt(0) && next <= 'f'.charCodeAt(0) || next >= 'A'.charCodeAt(0) && next <= 'F'.charCodeAt(0)))) && @@ -2518,7 +2520,7 @@ LibraryManager.library = { var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; argIndex += Runtime.getNativeFieldSize('void*'); switch (type) { - case 'd': case 'u': + case 'd': case 'u': case 'i': if (half) { {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}}; } else { @@ -6398,14 +6400,18 @@ LibraryManager.library = { ntohl: 'htonl', ntohs: 'htons', - inet_pton__deps: ['__setErrNo', '$ERRNO_CODES'], + 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_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_addr'], inet_pton: function(af, src, dst) { // int af, const char *src, void *dst if ((af ^ {{{ cDefine("AF_INET") }}}) !== 0) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); return -1; } - var b = Pointer_stringify(src).split("."); - if (b.length !== 4) return 0; - var ret = Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24); - if (isNaN(ret)) return 0; + var ret = _inet_addr(src); + if (ret == -1 || isNaN(ret)) return 0; setValue(dst, ret, 'i32'); return 1; }, @@ -6423,7 +6429,62 @@ LibraryManager.library = { }, // ========================================================================== - // sockets + // 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 + // note: lots of leaking here! + __hostent_struct_layout: Runtime.generateStructInfo([ + ['i8*', 'h_name'], + ['i8**', 'h_aliases'], + ['i32', 'h_addrtype'], + ['i32', 'h_length'], + ['i8**', 'h_addr_list'], + ]), + gethostbyname__deps: ['__hostent_struct_layout'], + 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*'); + 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'); + 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**'); + return ret; + }, + + gethostbyname_r__deps: ['gethostbyname'], + gethostbyname_r: function(name, hostData, buffer, bufferSize, hostEntry, errnum) { + var data = _gethostbyname(name); + _memcpy(hostData, data, ___hostent_struct_layout.__size__); + _free(data); + setValue(errnum, 0, 'i32'); + return 0; + }, + + // ========================================================================== + // sockets. Note that the implementation assumes all sockets are always + // nonblocking // ========================================================================== $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], @@ -6437,6 +6498,15 @@ LibraryManager.library = { ['i32', 'sin_addr'], ['i64', 'sin_zero'], ]), + msghdr_layout: Runtime.generateStructInfo([ + ['*', 'msg_name'], + ['i32', 'msg_namelen'], + ['*', 'msg_iov'], + ['i32', 'msg_iovlen'], + ['*', 'msg_control'], + ['i32', 'msg_controllen'], + ['i32', 'msg_flags'], + ]), }, socket__deps: ['$Sockets'], @@ -6448,7 +6518,7 @@ LibraryManager.library = { return fd; }, - connect__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs'], + connect__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'], connect: function(fd, addr, addrlen) { var info = Sockets.fds[fd]; if (!info) return -1; @@ -6456,24 +6526,68 @@ LibraryManager.library = { info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); info.host = __inet_ntop_raw(info.addr); - info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['arraybuffer']); + // 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); + } + console.log('opening ws://' + info.host + ':' + info.port); + info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['base64']); info.socket.binaryType = 'arraybuffer'; info.buffer = new Uint8Array(Sockets.BUFFER_SIZE); info.bufferWrite = info.bufferRead = 0; info.socket.onmessage = function (event) { var data = event.data; +#if SOCKET_DEBUG + Module.print(['onmessage', window.location, event.data, window.atob(data)]); +#endif if (typeof data == 'string') { var binaryString = window.atob(data); var len = binaryString.length; +#if SOCKET_DEBUG + var out = []; +#endif for (var i = 0; i < len; i++) { - info.buffer[info.bufferWrite++] = binaryString.charCodeAt(i); + var curr = binaryString.charCodeAt(i); + info.buffer[info.bufferWrite++] = curr; +#if SOCKET_DEBUG + out.push(curr); +#endif if (info.bufferWrite == Sockets.BUFFER_SIZE) info.bufferWrite = 0; if (info.bufferWrite == info.bufferRead) throw 'socket buffer overflow'; } +#if SOCKET_DEBUG + Module.print(['onmessage data:', len, ':', out]); +#endif } else { console.log('binary!'); } } + info.sendQueue = []; + info.senderWaiting = false; + info.sender = function(data) { + if (data) { + info.sendQueue.push(data); + } else { + info.senderWaiting = false; // we are a setTimeout callback + if (info.sendQueue.length == 0) return; + } + if (info.socket.readyState != info.socket.OPEN) { + if (!info.senderWaiting) { + console.log('waiting for socket in order to send'); + setTimeout(info.sender, 100); + info.senderWaiting = true; + } + return; + } + for (var i = 0; i < info.sendQueue.length; i++) { + info.socket.send(window.btoa(info.sendQueue[i])); + } + info.sendQueue = []; + } return 0; }, @@ -6486,6 +6600,9 @@ LibraryManager.library = { return 0; // should this be -1 like the spec says? } var ret = 0; +#if SOCKET_DEBUG + Module.print('pre-recv: ' + [len, info.bufferWrite, info.bufferRead]); +#endif while (info.bufferWrite != info.bufferRead && len > 0) { // write out a byte {{{ makeSetValue('buf++', '0', 'info.buffer[info.bufferRead++]', 'i8') }}}; @@ -6493,9 +6610,105 @@ LibraryManager.library = { len--; ret++; } +#if SOCKET_DEBUG + Module.print('recv: ' + ret + ' : ' + Array.prototype.slice.call(HEAPU8.subarray(buf-len, buf))); +#endif + return ret; + }, + + send__deps: ['$Sockets'], + send: function(fd, buf, len, flags) { + var info = Sockets.fds[fd]; + if (!info) return -1; +#if SOCKET_DEBUG + Module.print('send: ' + Array.prototype.slice.call(HEAPU8.subarray(buf, buf+len))); +#endif + info.sender(Pointer_stringify(buf, len)); + return len; + }, + + sendmsg__deps: ['$Sockets', 'connect'], + sendmsg: function(fd, msg, flags) { + var info = Sockets.fds[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') }}}); + } + var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; + var data = ''; + for (var i = 0; i < num; i++) { + var currNum = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov+8*i' + '+4', 'i32') }}}; + if (!currNum) continue; + var currBuf = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov+8*i', 'i8*') }}}; +#if SOCKET_DEBUG + Module.print('sendmsg part ' + i + ' : ' + currNum + ' : ' + Array.prototype.slice.call(HEAPU8.subarray(currBuf, currBuf+currNum))); +#endif + data += Pointer_stringify(currBuf, currNum); + } + info.sender(data); + return data.length; + }, + + recvmsg__deps: ['$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES'], + recvmsg: function(fd, msg, flags) { +#if SOCKET_DEBUG + Module.print('recvmsg!'); +#endif + var info = Sockets.fds[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') }}}); + } + var bytes = info.bufferWrite - info.bufferRead; + if (bytes < 0) bytes += Sockets.BUFFER_SIZE; +#if SOCKET_DEBUG + Module.print('recvmsg bytes: ' + bytes); +#endif + if (bytes == 0) { + ___setErrNo(ERRNO_CODES.EWOULDBLOCK); + return -1; + } + var ret = bytes; + var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; + var data = ''; + for (var i = 0; i < num && bytes > 0; i++) { + var currNum = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_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('msg', 'Sockets.msghdr_layout.msg_iov+8*i', 'i8*') }}}; +#if SOCKET_DEBUG + Module.print('recvmsg call recv ' + currNum); +#endif + assert(_recv(fd, currBuf, currNum, 0) == currNum); + } return ret; }, + recvfrom__deps: ['$Sockets', 'connect', 'recv'], + recvfrom: function(fd, buf, len, flags, addr, addrlen) { + var info = Sockets.fds[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); + } + return _recv(fd, buf, len, flags); + }, + shutdown: function(fd, how) { var info = Sockets.fds[fd]; if (!info) return -1; @@ -6514,6 +6727,34 @@ LibraryManager.library = { return 0; }, + setsockopt: function(d, level, optname, optval, optlen) { + console.log('ignoring setsockopt command'); + return 0; + }, + + bind__deps: ['connect'], + bind: function(fd, addr, addrlen) { + return _connect(fd, addr, addrlen); + }, + + listen: function(fd, backlog) { + return 0; + }, + + 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 = Sockets.fds[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; + }, + // ========================================================================== // emscripten.h // ========================================================================== |