diff --git a/src/node.cc b/src/node.cc index d150e9e5b4..81a100ad36 100644 --- a/src/node.cc +++ b/src/node.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -856,6 +857,8 @@ static Local Load(int argc, char *argv[]) { Stat::Initialize(process); // stat.cc SignalHandler::Initialize(process); // signal_handler.cc + InitNet2(process); // net2.cc + Stdio::Initialize(process); // stdio.cc ChildProcess::Initialize(process); // child_process.cc DefineConstants(process); // constants.cc diff --git a/src/node_net2.cc b/src/node_net2.cc new file mode 100644 index 0000000000..6ced4a26ce --- /dev/null +++ b/src/node_net2.cc @@ -0,0 +1,178 @@ +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include /* inet_pton */ + +#include + + + +namespace node { + +using namespace v8; + +static Persistent errno_symbol; +static Persistent syscall_symbol; + +static inline Local ErrnoException(int errorno, const char *syscall, const char *msg = "") { + if (!msg[0]) msg = strerror(errorno); + Local e = Exception::Error(String::NewSymbol(msg)); + Local obj = e->ToObject(); + obj->Set(errno_symbol, Integer::New(errorno)); + obj->Set(syscall_symbol, String::NewSymbol(syscall)); + return e; +} + +// Creates a new non-blocking socket fd +// t.socket("TCP"); +// t.socket("UNIX"); +// t.socket("UDP"); +static Handle Socket(const Arguments& args) { + HandleScope scope; + + // default to TCP + int domain = PF_INET6; + int type = SOCK_STREAM; + + if (args[0]->IsString()) { + String::Utf8Value t(args[0]->ToString()); + if (0 == strcasecmp(*t, "TCP")) { + domain = PF_INET6; + type = SOCK_STREAM; + } else if (0 == strcasecmp(*t, "UDP")) { + domain = PF_INET6; + type = SOCK_DGRAM; + } else if (0 == strcasecmp(*t, "UNIX")) { + domain = PF_UNIX; + type = SOCK_STREAM; + } else { + return ThrowException(Exception::Error( + String::New("Unknown socket type."))); + } + } + + int fd = socket(domain, type, 0); + + if (fd < 0) return ThrowException(ErrnoException(errno, "socket")); + + int fcntl_errno; + + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + fcntl_errno = errno; + close(fd); + return ThrowException(ErrnoException(fcntl_errno, "fcntl")); + } + + flags |= O_NONBLOCK; + + if (fcntl(fd, F_SETFL, flags) == -1) { + fcntl_errno = errno; + close(fd); + return ThrowException(ErrnoException(fcntl_errno, "fcntl")); + } + + return scope.Close(Integer::New(fd)); +} + +// 2 arguments means connect with unix +// t.connect(fd, "/tmp/socket") +// +// 3 arguments means connect with TCP or UDP +// t.connect(fd, "127.0.0.1", 80) +static Handle Connect(const Arguments& args) { + HandleScope scope; + + if (!args[0]->IsInt32()) { + return ThrowException(Exception::TypeError( + String::New("First argument should be file descriptor"))); + } + + if (args.Length() < 2) { + return ThrowException(Exception::TypeError( + String::New("Must have at least two args"))); + } + + int fd = args[0]->Int32Value(); + + struct sockaddr *addr; + socklen_t addrlen; + + if (args.Length() == 2) { + // UNIX + String::Utf8Value path(args[1]->ToString()); + + struct sockaddr_un un = {0}; + + if (path.length() > sizeof un.sun_path) { + return ThrowException(Exception::Error(String::New("Socket path too long"))); + } + + un.sun_family = AF_UNIX; + strcpy(un.sun_path, *path); + + addr = (struct sockaddr*)&un; + addrlen = path.length() + sizeof(un.sun_family); + + } else { + // TCP or UDP + String::Utf8Value ip(args[1]->ToString()); + int port = args[2]->Int32Value(); + + struct sockaddr_in6 in6 = {0}; + + 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 ThrowException( + ErrnoException(errno, "inet_pton", "Invalid IP Address")); + } + + in6.sin6_family = AF_INET6; + in6.sin6_port = htons(port); + + addr = (struct sockaddr*)&in6; + addrlen = sizeof in6; + } + + int r = connect(fd, addr, addrlen); + + if (r < 0 && errno != EINPROGRESS) { + return ThrowException(ErrnoException(errno, "connect")); + } + + return Undefined(); +} + + +void InitNet2(Handle target) { + HandleScope scope; + + NODE_SET_METHOD(target, "socket", Socket); + NODE_SET_METHOD(target, "connect", Connect); + + errno_symbol = NODE_PSYMBOL("errno"); + syscall_symbol = NODE_PSYMBOL("syscall"); +} + +} // namespace node diff --git a/src/node_net2.h b/src/node_net2.h new file mode 100644 index 0000000000..cd3e9f083a --- /dev/null +++ b/src/node_net2.h @@ -0,0 +1,12 @@ +#ifndef NODE_NET2 +#define NODE_NET2 + +#include + +namespace node { + +void InitNet2(v8::Handle target); + +} + +#endif // NODE_NET2 diff --git a/wscript b/wscript index 773a17fb77..d2e13c9339 100644 --- a/wscript +++ b/wscript @@ -323,6 +323,7 @@ def build(bld): node.source = """ src/node.cc src/node_buffer.cc + src/node_net2.cc src/node_io_watcher.cc src/node_child_process.cc src/node_constants.cc