Last active
August 29, 2015 14:07
-
-
Save LogIN-/c9372c5dafc17cdf9ce8 to your computer and use it in GitHub Desktop.
Check free port node native addon
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <node.h> | |
| #include <v8.h> | |
| #include <stdio.h> | |
| #include <time.h> | |
| #include <errno.h> | |
| #include <string.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <sys/types.h> | |
| #include <sys/time.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <arpa/inet.h> | |
| using namespace node; | |
| using namespace v8; | |
| // Allocate a new TCP server socket, and return | |
| // its handler | |
| int allocateFreePort() { | |
| int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | |
| if (sock < 0) { | |
| if (errno == EMFILE) { | |
| perror("Too many open files..."); | |
| return 0; | |
| } | |
| perror("Failed to allocate free port."); | |
| exit(-1); | |
| } | |
| int optval = 1; | |
| setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); | |
| return sock; | |
| } | |
| // Check whether the provided TCP port is available | |
| // at the moment and return 1 if it's avaiable, zero otherwise | |
| int checkFreePort(int port) { | |
| int sock = allocateFreePort(); | |
| struct sockaddr_in addr; | |
| memset(&addr, 0, sizeof(addr)); | |
| addr.sin_family = AF_INET; | |
| addr.sin_port = htons(port); | |
| addr.sin_addr.s_addr = htonl(inet_addr("0.0.0.0")); | |
| int error = bind(sock, (struct sockaddr*) &addr, sizeof(addr)); | |
| return error == 0; | |
| } | |
| Handle<Value> findFreePort(const Arguments& args) { | |
| HandleScope scope; | |
| struct timeval tv; | |
| gettimeofday(&tv, 0); | |
| srand(tv.tv_usec); | |
| struct sockaddr_in addr; | |
| int attempts; | |
| int port; | |
| for (attempts = 0; attempts < 100; ++attempts) { | |
| port = 5000 + rand() % 100; | |
| if (checkFreePort(port)) { | |
| return scope.Close(Number::New(port)); | |
| } | |
| } | |
| socklen_t len = sizeof(addr); | |
| addr.sin_port = 0; | |
| while (addr.sin_port < 1024) { | |
| usleep(10); | |
| if (++attempts > 500) { | |
| ThrowException(Exception::TypeError(String::New("Failed to reserve free port."))); | |
| return scope.Close(Undefined()); | |
| } | |
| int sock = allocateFreePort(); | |
| if (sock == 0) { | |
| continue; | |
| } | |
| if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) != 0) { | |
| if (errno == EADDRINUSE) { | |
| /* address already in use */ | |
| continue; | |
| } | |
| ThrowException(Exception::TypeError(String::New("Failed bind() free port."))); | |
| return scope.Close(Undefined()); | |
| } | |
| if (getsockname(sock, (struct sockaddr*) &addr, &len) != 0) { | |
| ThrowException(Exception::TypeError(String::New("Failed getsockname() ..."))); | |
| return scope.Close(Undefined()); | |
| } | |
| } | |
| return scope.Close(Number::New(addr.sin_port)); | |
| } | |
| void init(Handle<Object> target) { | |
| target->Set(String::NewSymbol("findFreePort"), | |
| FunctionTemplate::New(findFreePort)->GetFunction()); | |
| } | |
| NODE_MODULE(tue, init) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment