diff --git a/lib/net.js b/lib/net.js index dd415f7d23..d4926c6471 100644 --- a/lib/net.js +++ b/lib/net.js @@ -39,7 +39,7 @@ var setNoDelay = binding.setNoDelay; var socketError = binding.socketError; var getsockname = binding.getsockname; var getaddrinfo = binding.getaddrinfo; -var needsLookup = binding.needsLookup; +var isIP = binding.isIP; var errnoException = binding.errnoException; var EINPROGRESS = binding.EINPROGRESS; var ENOENT = binding.ENOENT; @@ -422,6 +422,7 @@ Stream.prototype.write = function (data, encoding) { Stream.prototype._writeOut = function (data, encoding) { if (!this.writable) throw new Error('Stream is not writable'); + if (data.length == 0) return true; var buffer, off, len; var bytesWritten, charsWritten; @@ -566,6 +567,8 @@ function doConnect (socket, port, host) { return; } + debug('connecting to ' + host + ' : ' + port); + // Don't start the read watcher until connection is established socket._readWatcher.set(socket.fd, true, false); @@ -607,13 +610,13 @@ Stream.prototype.connect = function () { var port = parseInt(arguments[0]); if (port >= 0) { - self.fd = socket('tcp'); //debug('new fd = ' + self.fd); - self.type = 'tcp'; // TODO dns resolution on arguments[1] var port = arguments[0]; self._resolving = true; - lookupDomainName(arguments[1], function (ip) { + lookupDomainName(arguments[1], function (ip, isV4) { + self.type = isV4 ? 'tcp4' : 'tcp6'; + self.fd = socket(self.type); self._resolving = false; doConnect(self, port, ip); }); @@ -729,8 +732,8 @@ function Server (listener) { if (!peerInfo) return; var s = new Stream(peerInfo.fd); - s.remoteAddress = peerInfo.remoteAddress; - s.remotePort = peerInfo.remotePort; + s.remoteAddress = peerInfo.address; + s.remotePort = peerInfo.port; s.type = self.type; s.server = self; s.resume(); @@ -750,25 +753,27 @@ exports.createServer = function (listener) { return new Server(listener); }; -/* This function does both an ipv4 and ipv6 look up. - * It first tries the ipv4 look up, if that fails, then it does the ipv6. - */ +// This function does both an ipv4 and ipv6 look up. +// It first tries the ipv4 look up, if that fails, then it does the ipv6. +// callback(dn, isV4 function lookupDomainName (dn, callback) { - if (!needsLookup(dn)) { + var kind = isIP(dn); + debug('kind = ' + kind); + if (kind) { // Always wait until the next tick this is so people can do // // server.listen(8000); // server.addListener('listening', fn); // // Marginally slower, but a lot fewer WTFs. - process.nextTick(function () { callback(dn); }) + process.nextTick(function () { callback(dn, kind == 4 ? true : false); }) } else { debug("getaddrinfo 4 " + dn); getaddrinfo(dn, 4, function (r4) { if (r4 instanceof Error) throw r4; if (r4.length > 0) { debug("getaddrinfo 4 found " + r4); - callback(r4[0]); + callback(r4[0], true); } else { debug("getaddrinfo 6 " + dn); getaddrinfo(dn, 6, function (r6) { @@ -777,7 +782,7 @@ function lookupDomainName (dn, callback) { throw new Error("No address associated with hostname " + dn); } debug("getaddrinfo 6 found " + r6); - callback(r6[0]); + callback(r6[0], false); }); } }); @@ -797,13 +802,6 @@ Server.prototype.listen = function () { var self = this; if (self.fd) throw new Error('Server already opened'); - function doListen () { - listen(self.fd, 128); - self.watcher.set(self.fd, true, false); - self.watcher.start(); - self.emit("listening"); - } - if (typeof(arguments[0]) == 'string') { // the first argument specifies a path self.fd = socket('unix'); @@ -815,7 +813,7 @@ Server.prototype.listen = function () { if (err) { if (err.errno == ENOENT) { bind(self.fd, path); - doListen(); + self._doListen(); } else { throw r; } @@ -828,30 +826,41 @@ Server.prototype.listen = function () { throw err; } else { bind(self.fd, path); - doListen(); + self._doListen(); } }); } } }); - } else if (!arguments[0]) { + } else if (!arguments[1]) { // Don't bind(). OS will assign a port with INADDR_ANY. // The port can be found with server.address() - self.fd = socket('tcp'); - self.type = 'tcp'; - doListen(); + self.type = 'tcp4'; + self.fd = socket(self.type); + bind(self.fd, arguments[0]); + process.nextTick(function () { + self._doListen(); + }); } else { // the first argument is the port, the second an IP - self.fd = socket('tcp'); - self.type = 'tcp'; var port = arguments[0]; - lookupDomainName(arguments[1], function (ip) { + lookupDomainName(arguments[1], function (ip, isV4) { + self.type = isV4 ? 'tcp4' : 'tcp6'; + self.fd = socket(self.type); bind(self.fd, port, ip); - doListen(); + self._doListen(); }); } }; +Server.prototype._doListen = function () { + listen(this.fd, 128); + this.watcher.set(this.fd, true, false); + this.watcher.start(); + this.emit("listening"); +} + + Server.prototype.address = function () { return getsockname(this.fd); diff --git a/src/node_net2.cc b/src/node_net2.cc index 2ddeb51ada..de32dc419f 100644 --- a/src/node_net2.cc +++ b/src/node_net2.cc @@ -38,8 +38,6 @@ static Persistent errno_symbol; static Persistent syscall_symbol; static Persistent fd_symbol; -static Persistent remote_address_symbol; -static Persistent remote_port_symbol; static Persistent address_symbol; static Persistent port_symbol; static Persistent type_symbol; @@ -467,20 +465,27 @@ static Handle Socket(const Arguments& args) { HandleScope scope; // default to TCP - int domain = PF_INET6; + int domain = PF_INET; int type = SOCK_STREAM; if (args[0]->IsString()) { String::Utf8Value t(args[0]->ToString()); + // FIXME optimize this cascade. if (0 == strcasecmp(*t, "TCP")) { - domain = PF_INET6; + domain = PF_INET; type = SOCK_STREAM; - } else if (0 == strcasecmp(*t, "UDP")) { + } else if (0 == strcasecmp(*t, "TCP4")) { + domain = PF_INET; + type = SOCK_STREAM; + } else if (0 == strcasecmp(*t, "TCP6")) { domain = PF_INET6; - type = SOCK_DGRAM; + type = SOCK_STREAM; } else if (0 == strcasecmp(*t, "UNIX")) { domain = PF_UNIX; type = SOCK_STREAM; + } else if (0 == strcasecmp(*t, "UDP")) { + domain = PF_INET6; + type = SOCK_DGRAM; } else { return ThrowException(Exception::Error( String::New("Unknown socket type."))); @@ -505,13 +510,14 @@ static Handle Socket(const Arguments& args) { // (yes this is all to avoid one small heap alloc) static struct sockaddr *addr; static socklen_t addrlen; -static struct sockaddr_un un; -static struct sockaddr_in6 in6; static inline Handle ParseAddressArgs(Handle first, Handle second, - struct in6_addr default_addr - ) { - if (first->IsString() && second->IsUndefined()) { + bool is_bind) { + static struct sockaddr_un un; + static struct sockaddr_in in; + static struct sockaddr_in6 in6; + + if (first->IsString() && !second->IsString()) { // UNIX String::Utf8Value path(first->ToString()); @@ -528,36 +534,32 @@ static inline Handle ParseAddressArgs(Handle first, } else { // TCP or UDP - int port = first->Int32Value(); + memset(&in, 0, sizeof in); memset(&in6, 0, sizeof in6); + + int port = first->Int32Value(); + in.sin_port = in6.sin6_port = htons(port); + in.sin_family = AF_INET; + in6.sin6_family = AF_INET6; + + bool is_ipv4 = true; + if (!second->IsString()) { - in6.sin6_addr = default_addr; + in.sin_addr.s_addr = htonl(is_bind ? INADDR_ANY : INADDR_LOOPBACK); + in6.sin6_addr = is_bind ? in6addr_any : in6addr_loopback; } else { String::Utf8Value ip(second->ToString()); - char ipv6[255] = "::FFFF:"; - - if (inet_pton(AF_INET, *ip, &(in6.sin6_addr)) > 0) { - // If this is an IPv4 address then we need to change it - // to the IPv4-mapped-on-IPv6 format which looks like - // ::FFFF: - // For more information see "Address Format" ipv6(7) and - // "BUGS" in inet_pton(3) - strcat(ipv6, *ip); - } else { - strcpy(ipv6, *ip); - } - - if (inet_pton(AF_INET6, ipv6, &(in6.sin6_addr)) <= 0) { - return ErrnoException(errno, "inet_pton", "Invalid IP Address"); + if (inet_pton(AF_INET, *ip, &(in.sin_addr)) <= 0) { + is_ipv4 = false; + if (inet_pton(AF_INET6, *ip, &(in6.sin6_addr)) <= 0) { + return ErrnoException(errno, "inet_pton", "Invalid IP Address"); + } } } - in6.sin6_family = AF_INET6; - in6.sin6_port = htons(port); - - addr = (struct sockaddr*)&in6; - addrlen = sizeof in6; + addr = is_ipv4 ? (struct sockaddr*)&in : (struct sockaddr*)&in6; + addrlen = is_ipv4 ? sizeof in : sizeof in6; } return Handle(); } @@ -578,7 +580,7 @@ static Handle Bind(const Arguments& args) { FD_ARG(args[0]) - Handle error = ParseAddressArgs(args[1], args[2], in6addr_any); + Handle error = ParseAddressArgs(args[1], args[2], true); if (!error.IsEmpty()) return ThrowException(error); int flags = 1; @@ -658,7 +660,7 @@ static Handle Connect(const Arguments& args) { FD_ARG(args[0]) - Handle error = ParseAddressArgs(args[1], args[2], in6addr_loopback); + Handle error = ParseAddressArgs(args[1], args[2], false); if (!error.IsEmpty()) return ThrowException(error); int r = connect(fd, addr, addrlen); @@ -671,6 +673,31 @@ static Handle Connect(const Arguments& args) { } +#define ADDRESS_TO_JS(info, address_storage) \ +do { \ + char ip[INET6_ADDRSTRLEN]; \ + int port; \ + struct sockaddr_in *a4; \ + struct sockaddr_in6 *a6; \ + switch ((address_storage).ss_family) { \ + case AF_INET6: \ + a6 = (struct sockaddr_in6*)&(address_storage); \ + inet_ntop(AF_INET6, &(a6->sin6_addr), ip, INET6_ADDRSTRLEN); \ + port = ntohs(a6->sin6_port); \ + (info)->Set(address_symbol, String::New(ip)); \ + (info)->Set(port_symbol, Integer::New(port)); \ + break; \ + case AF_INET: \ + a4 = (struct sockaddr_in*)&(address_storage); \ + inet_ntop(AF_INET, &(a4->sin_addr), ip, INET6_ADDRSTRLEN); \ + port = ntohs(a4->sin_port); \ + (info)->Set(address_symbol, String::New(ip)); \ + (info)->Set(port_symbol, Integer::New(port)); \ + break; \ + } \ +} while (0) + + static Handle GetSockName(const Arguments& args) { HandleScope scope; @@ -687,17 +714,7 @@ static Handle GetSockName(const Arguments& args) { Local info = Object::New(); - if (address_storage.ss_family == AF_INET6) { - struct sockaddr_in6 *a = (struct sockaddr_in6*)&address_storage; - - char ip[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(a->sin6_addr), ip, INET6_ADDRSTRLEN); - - int port = ntohs(a->sin6_port); - - info->Set(address_symbol, String::New(ip)); - info->Set(port_symbol, Integer::New(port)); - } + ADDRESS_TO_JS(info, address_storage); return scope.Close(info); } @@ -719,17 +736,7 @@ static Handle GetPeerName(const Arguments& args) { Local info = Object::New(); - if (address_storage.ss_family == AF_INET6) { - struct sockaddr_in6 *a = (struct sockaddr_in6*)&address_storage; - - char ip[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(a->sin6_addr), ip, INET6_ADDRSTRLEN); - - int port = ntohs(a->sin6_port); - - info->Set(remote_address_symbol, String::New(ip)); - info->Set(remote_port_symbol, Integer::New(port)); - } + ADDRESS_TO_JS(info, address_storage); return scope.Close(info); } @@ -753,8 +760,8 @@ static Handle Listen(const Arguments& args) { // var peerInfo = t.accept(server_fd); // // peerInfo.fd -// peerInfo.remoteAddress -// peerInfo.remotePort +// peerInfo.address +// peerInfo.port // // Returns a new nonblocking socket fd. If the listen queue is empty the // function returns null (wait for server_fd to become readable and try @@ -784,17 +791,7 @@ static Handle Accept(const Arguments& args) { peer_info->Set(fd_symbol, Integer::New(peer_fd)); - if (address_storage.ss_family == AF_INET6) { - struct sockaddr_in6 *a = (struct sockaddr_in6*)&address_storage; - - char ip[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(a->sin6_addr), ip, INET6_ADDRSTRLEN); - - int port = ntohs(a->sin6_port); - - peer_info->Set(remote_address_symbol, String::New(ip)); - peer_info->Set(remote_port_symbol, Integer::New(port)); - } + ADDRESS_TO_JS(peer_info, address_storage); return scope.Close(peer_info); } @@ -1226,28 +1223,28 @@ static Handle GetAddrInfo(const Arguments& args) { } -static Handle NeedsLookup(const Arguments& args) { +static Handle IsIP(const Arguments& args) { HandleScope scope; - - if (args[0]->IsNull() || args[0]->IsUndefined()) return False(); + + if (!args[0]->IsString()) { + return scope.Close(Integer::New(4)); + } String::Utf8Value s(args[0]->ToString()); // avoiding buffer overflows in the following strcat // 2001:0db8:85a3:08d3:1319:8a2e:0370:7334 // 39 = max ipv6 address. - if (s.length() > INET6_ADDRSTRLEN) return True(); + if (s.length() > INET6_ADDRSTRLEN) { + return scope.Close(Integer::New(0)); + } struct sockaddr_in6 a; - if (inet_pton(AF_INET, *s, &(a.sin6_addr)) > 0) return False(); - if (inet_pton(AF_INET6, *s, &(a.sin6_addr)) > 0) return False(); - - char ipv6[255] = "::FFFF:"; - strcat(ipv6, *s); - if (inet_pton(AF_INET6, ipv6, &(a.sin6_addr)) > 0) return False(); + if (inet_pton(AF_INET, *s, &(a.sin6_addr)) > 0) return scope.Close(Integer::New(4)); + if (inet_pton(AF_INET6, *s, &(a.sin6_addr)) > 0) return scope.Close(Integer::New(6)); - return True(); + return scope.Close(Integer::New(0)); } @@ -1291,7 +1288,7 @@ void InitNet2(Handle target) { NODE_SET_METHOD(target, "getsockname", GetSockName); NODE_SET_METHOD(target, "getpeername", GetPeerName); NODE_SET_METHOD(target, "getaddrinfo", GetAddrInfo); - NODE_SET_METHOD(target, "needsLookup", NeedsLookup); + NODE_SET_METHOD(target, "isIP", IsIP); NODE_SET_METHOD(target, "errnoException", CreateErrnoException); target->Set(String::NewSymbol("ENOENT"), Integer::New(ENOENT)); @@ -1305,8 +1302,6 @@ void InitNet2(Handle target) { errno_symbol = NODE_PSYMBOL("errno"); syscall_symbol = NODE_PSYMBOL("syscall"); fd_symbol = NODE_PSYMBOL("fd"); - remote_address_symbol = NODE_PSYMBOL("remoteAddress"); - remote_port_symbol = NODE_PSYMBOL("remotePort"); address_symbol = NODE_PSYMBOL("address"); port_symbol = NODE_PSYMBOL("port"); } diff --git a/test/pummel/test-tcp-pingpong.js b/test/pummel/test-tcp-pingpong.js index 73d36c82d9..c9a66ffc91 100644 --- a/test/pummel/test-tcp-pingpong.js +++ b/test/pummel/test-tcp-pingpong.js @@ -11,10 +11,12 @@ function pingPongTest (port, host, on_complete) { var server = net.createServer(function (socket) { assert.equal(true, socket.remoteAddress !== null); assert.equal(true, socket.remoteAddress !== undefined); - if (host === "127.0.0.1") + if (host === "127.0.0.1" || host === "localhost" || !host) { assert.equal(socket.remoteAddress, "127.0.0.1"); - else if (host == null) + } else { + puts('host = ' + host + ', remoteAddress = ' + socket.remoteAddress); assert.equal(socket.remoteAddress, "::1"); + } socket.setEncoding("utf8"); socket.setNoDelay();