/* * TCP sockets binding example. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "duktape.h" #define ERROR_FROM_ERRNO(ctx) do { \ duk_error(ctx, DUK_ERR_ERROR, "%s (errno=%d)", strerror(errno), errno); \ } while (0) static void set_nonblocking(duk_context *ctx, int fd) { int rc; int flags; rc = fcntl(fd, F_GETFL); if (rc < 0) { ERROR_FROM_ERRNO(ctx); } flags = rc; flags |= O_NONBLOCK; rc = fcntl(fd, F_SETFL, flags); if (rc < 0) { ERROR_FROM_ERRNO(ctx); } } static void set_reuseaddr(duk_context *ctx, int fd) { int val; int rc; val = 1; rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &val, sizeof(val)); if (rc != 0) { ERROR_FROM_ERRNO(ctx); } } #ifdef __APPLE__ static void set_nosigpipe(duk_context *ctx, int fd) { int val; int rc; val = 1; rc = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const void *) &val, sizeof(val)); if (rc != 0) { ERROR_FROM_ERRNO(ctx); } } #endif static int socket_create_server_socket(duk_context *ctx) { const char *addr = duk_to_string(ctx, 0); int port = duk_to_int(ctx, 1); int sock; struct sockaddr_in sockaddr; struct hostent *ent; struct in_addr **addr_list; struct in_addr *addr_inet; int i; int rc; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { ERROR_FROM_ERRNO(ctx); } set_nonblocking(ctx, sock); set_reuseaddr(ctx, sock); #ifdef __APPLE__ set_nosigpipe(ctx, sock); #endif ent = gethostbyname(addr); if (!ent) { ERROR_FROM_ERRNO(ctx); } addr_list = (struct in_addr **) ent->h_addr_list; addr_inet = NULL; for (i = 0; addr_list[i]; i++) { addr_inet = addr_list[i]; break; } if (!addr_inet) { duk_error(ctx, DUK_ERR_ERROR, "cannot resolve %s", addr); } memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(port); sockaddr.sin_addr = *addr_inet; rc = bind(sock, (const struct sockaddr *) &sockaddr, sizeof(sockaddr)); if (rc < 0) { ERROR_FROM_ERRNO(ctx); } rc = listen(sock, 10 /*backlog*/); if (rc < 0) { (void) close(sock); ERROR_FROM_ERRNO(ctx); } duk_push_int(ctx, sock); return 1; } static int socket_close(duk_context *ctx) { int sock = duk_to_int(ctx, 0); int rc; rc = close(sock); if (rc < 0) { ERROR_FROM_ERRNO(ctx); } return 0; } static int socket_accept(duk_context *ctx) { int sock = duk_to_int(ctx, 0); int rc; struct sockaddr_in addr; socklen_t addrlen; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addrlen = sizeof(addr); rc = accept(sock, (struct sockaddr *) &addr, &addrlen); if (rc < 0) { ERROR_FROM_ERRNO(ctx); } set_nonblocking(ctx, sock); #ifdef __APPLE__ set_nosigpipe(ctx, sock); #endif if (addrlen == sizeof(addr)) { uint32_t tmp = ntohl(addr.sin_addr.s_addr); duk_push_object(ctx); duk_push_string(ctx, "fd"); duk_push_int(ctx, rc); duk_put_prop(ctx, -3); duk_push_string(ctx, "addr"); duk_push_sprintf(ctx, "%d.%d.%d.%d", ((tmp >> 24) & 0xff), ((tmp >> 16) & 0xff), ((tmp >> 8) & 0xff), (tmp & 0xff)); duk_put_prop(ctx, -3); duk_push_string(ctx, "port"); duk_push_int(ctx, ntohs(addr.sin_port)); duk_put_prop(ctx, -3); return 1; } return 0; } static int socket_connect(duk_context *ctx) { const char *addr = duk_to_string(ctx, 0); int port = duk_to_int(ctx, 1); int sock; struct sockaddr_in sockaddr; struct hostent *ent; struct in_addr **addr_list; struct in_addr *addr_inet; int i; int rc; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { ERROR_FROM_ERRNO(ctx); } set_nonblocking(ctx, sock); #ifdef __APPLE__ set_nosigpipe(ctx, sock); #endif ent = gethostbyname(addr); if (!ent) { ERROR_FROM_ERRNO(ctx); } addr_list = (struct in_addr **) ent->h_addr_list; addr_inet = NULL; for (i = 0; addr_list[i]; i++) { addr_inet = addr_list[i]; break; } if (!addr_inet) { duk_error(ctx, DUK_ERR_ERROR, "cannot resolve %s", addr); } memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(port); sockaddr.sin_addr = *addr_inet; rc = connect(sock, (const struct sockaddr *) &sockaddr, (socklen_t) sizeof(sockaddr)); if (rc < 0) { if (errno == EINPROGRESS) { #if 0 fprintf(stderr, "connect() returned EINPROGRESS as expected, need to poll writability\n"); fflush(stderr); #endif } else { ERROR_FROM_ERRNO(ctx); } } duk_push_int(ctx, sock); return 1; } static int socket_read(duk_context *ctx) { int sock = duk_to_int(ctx, 0); char readbuf[1024]; int rc; void *data; rc = recvfrom(sock, (void *) readbuf, sizeof(readbuf), 0, NULL, NULL); if (rc < 0) { ERROR_FROM_ERRNO(ctx); } data = duk_push_fixed_buffer(ctx, rc); memcpy(data, readbuf, rc); return 1; } static int socket_write(duk_context *ctx) { int sock = duk_to_int(ctx, 0); const char *data; size_t len; ssize_t rc; data = duk_to_buffer(ctx, 1, &len); /* MSG_NOSIGNAL: avoid SIGPIPE */ #ifdef __APPLE__ rc = sendto(sock, (void *) data, len, 0, NULL, 0); #else rc = sendto(sock, (void *) data, len, MSG_NOSIGNAL, NULL, 0); #endif if (rc < 0) { ERROR_FROM_ERRNO(ctx); } duk_push_int(ctx, rc); return 1; } static duk_function_list_entry socket_funcs[] = { { "createServerSocket", socket_create_server_socket, 2 }, { "close", socket_close, 1 }, { "accept", socket_accept, 1 }, { "connect", socket_connect, 2 }, { "read", socket_read, 1 }, { "write", socket_write, 2 }, { NULL, NULL, 0 } }; void socket_register(duk_context *ctx) { /* Set global 'Socket'. */ duk_push_global_object(ctx); duk_push_object(ctx); duk_put_function_list(ctx, -1, socket_funcs); duk_put_prop_string(ctx, -2, "Socket"); duk_pop(ctx); }