blob: a1587fa31d2bb1840b75f83aeb17826645d3c9c7 [file] [log] [blame]
/*
* TCP sockets binding example.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#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);
}