1 /*
2  *  TCP sockets binding example.
3  */
4 
5 #define _GNU_SOURCE
6 #include <errno.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <netdb.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <time.h>
16 
17 #include "duktape.h"
18 
19 #define  ERROR_FROM_ERRNO(ctx)  do { \
20 		duk_error(ctx, DUK_ERR_ERROR, "%s (errno=%d)", strerror(errno), errno); \
21 	} while (0)
22 
set_nonblocking(duk_context * ctx,int fd)23 static void set_nonblocking(duk_context *ctx, int fd) {
24 	int rc;
25 	int flags;
26 
27 	rc = fcntl(fd, F_GETFL);
28 	if (rc < 0) {
29 		ERROR_FROM_ERRNO(ctx);
30 	}
31 	flags = rc;
32 
33 	flags |= O_NONBLOCK;
34 
35 	rc = fcntl(fd, F_SETFL, flags);
36 	if (rc < 0) {
37 		ERROR_FROM_ERRNO(ctx);
38 	}
39 }
40 
set_reuseaddr(duk_context * ctx,int fd)41 static void set_reuseaddr(duk_context *ctx, int fd) {
42 	int val;
43 	int rc;
44 
45 	val = 1;
46 	rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &val, sizeof(val));
47 	if (rc != 0) {
48 		ERROR_FROM_ERRNO(ctx);
49 	}
50 }
51 
52 #ifdef __APPLE__
set_nosigpipe(duk_context * ctx,int fd)53 static void set_nosigpipe(duk_context *ctx, int fd) {
54 	int val;
55 	int rc;
56 
57 	val = 1;
58 	rc = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const void *) &val, sizeof(val));
59 	if (rc != 0) {
60 		ERROR_FROM_ERRNO(ctx);
61 	}
62 }
63 #endif
64 
socket_create_server_socket(duk_context * ctx)65 static int socket_create_server_socket(duk_context *ctx) {
66 	const char *addr = duk_to_string(ctx, 0);
67 	int port = duk_to_int(ctx, 1);
68 	int sock;
69 	struct sockaddr_in sockaddr;
70 	struct hostent *ent;
71 	struct in_addr **addr_list;
72 	struct in_addr *addr_inet;
73 	int i;
74 	int rc;
75 
76 	sock = socket(AF_INET, SOCK_STREAM, 0);
77 	if (sock < 0) {
78 		ERROR_FROM_ERRNO(ctx);
79 	}
80 
81 	set_nonblocking(ctx, sock);
82 	set_reuseaddr(ctx, sock);
83 #ifdef __APPLE__
84 	set_nosigpipe(ctx, sock);
85 #endif
86 
87 	ent = gethostbyname(addr);
88 	if (!ent) {
89 		ERROR_FROM_ERRNO(ctx);
90 	}
91 
92 	addr_list = (struct in_addr **) ent->h_addr_list;
93 	addr_inet = NULL;
94 	for (i = 0; addr_list[i]; i++) {
95 		addr_inet = addr_list[i];
96 		break;
97 	}
98 	if (!addr_inet) {
99 		duk_error(ctx, DUK_ERR_ERROR, "cannot resolve %s", addr);
100 	}
101 
102 	memset(&sockaddr, 0, sizeof(sockaddr));
103 	sockaddr.sin_family = AF_INET;
104 	sockaddr.sin_port = htons(port);
105 	sockaddr.sin_addr = *addr_inet;
106 
107 	rc = bind(sock, (const struct sockaddr *) &sockaddr, sizeof(sockaddr));
108 	if (rc < 0) {
109 		ERROR_FROM_ERRNO(ctx);
110 	}
111 
112 	rc = listen(sock, 10 /*backlog*/);
113 	if (rc < 0) {
114 		(void) close(sock);
115 		ERROR_FROM_ERRNO(ctx);
116 	}
117 
118 	duk_push_int(ctx, sock);
119 	return 1;
120 }
121 
socket_close(duk_context * ctx)122 static int socket_close(duk_context *ctx) {
123 	int sock = duk_to_int(ctx, 0);
124 	int rc;
125 
126 	rc = close(sock);
127 	if (rc < 0) {
128 		ERROR_FROM_ERRNO(ctx);
129 	}
130 	return 0;
131 }
132 
socket_accept(duk_context * ctx)133 static int socket_accept(duk_context *ctx) {
134 	int sock = duk_to_int(ctx, 0);
135 	int rc;
136 	struct sockaddr_in addr;
137 	socklen_t addrlen;
138 
139 	memset(&addr, 0, sizeof(addr));
140 	addr.sin_family = AF_INET;
141 	addrlen = sizeof(addr);
142 
143 	rc = accept(sock, (struct sockaddr *) &addr, &addrlen);
144 	if (rc < 0) {
145 		ERROR_FROM_ERRNO(ctx);
146 	}
147 
148 	set_nonblocking(ctx, sock);
149 #ifdef __APPLE__
150 	set_nosigpipe(ctx, sock);
151 #endif
152 
153 	if (addrlen == sizeof(addr)) {
154 		uint32_t tmp = ntohl(addr.sin_addr.s_addr);
155 
156 		duk_push_object(ctx);
157 
158 		duk_push_string(ctx, "fd");
159 		duk_push_int(ctx, rc);
160 		duk_put_prop(ctx, -3);
161 		duk_push_string(ctx, "addr");
162 		duk_push_sprintf(ctx, "%d.%d.%d.%d", ((tmp >> 24) & 0xff), ((tmp >> 16) & 0xff), ((tmp >> 8) & 0xff), (tmp & 0xff));
163 		duk_put_prop(ctx, -3);
164 		duk_push_string(ctx, "port");
165 		duk_push_int(ctx, ntohs(addr.sin_port));
166 		duk_put_prop(ctx, -3);
167 
168 		return 1;
169 	}
170 
171 	return 0;
172 }
173 
socket_connect(duk_context * ctx)174 static int socket_connect(duk_context *ctx) {
175 	const char *addr = duk_to_string(ctx, 0);
176 	int port = duk_to_int(ctx, 1);
177 	int sock;
178 	struct sockaddr_in sockaddr;
179 	struct hostent *ent;
180 	struct in_addr **addr_list;
181 	struct in_addr *addr_inet;
182 	int i;
183 	int rc;
184 
185 	sock = socket(AF_INET, SOCK_STREAM, 0);
186 	if (sock < 0) {
187 		ERROR_FROM_ERRNO(ctx);
188 	}
189 
190 	set_nonblocking(ctx, sock);
191 #ifdef __APPLE__
192 	set_nosigpipe(ctx, sock);
193 #endif
194 
195 	ent = gethostbyname(addr);
196 	if (!ent) {
197 		ERROR_FROM_ERRNO(ctx);
198 	}
199 
200 	addr_list = (struct in_addr **) ent->h_addr_list;
201 	addr_inet = NULL;
202 	for (i = 0; addr_list[i]; i++) {
203 		addr_inet = addr_list[i];
204 		break;
205 	}
206 	if (!addr_inet) {
207 		duk_error(ctx, DUK_ERR_ERROR, "cannot resolve %s", addr);
208 	}
209 
210 	memset(&sockaddr, 0, sizeof(sockaddr));
211 	sockaddr.sin_family = AF_INET;
212 	sockaddr.sin_port = htons(port);
213 	sockaddr.sin_addr = *addr_inet;
214 
215 	rc = connect(sock, (const struct sockaddr *) &sockaddr, (socklen_t) sizeof(sockaddr));
216 	if (rc < 0) {
217 		if (errno == EINPROGRESS) {
218 #if 0
219 			fprintf(stderr, "connect() returned EINPROGRESS as expected, need to poll writability\n");
220 			fflush(stderr);
221 #endif
222 		} else {
223 			ERROR_FROM_ERRNO(ctx);
224 		}
225 	}
226 
227 	duk_push_int(ctx, sock);
228 	return 1;
229 }
230 
socket_read(duk_context * ctx)231 static int socket_read(duk_context *ctx) {
232 	int sock = duk_to_int(ctx, 0);
233 	char readbuf[1024];
234 	int rc;
235 	void *data;
236 
237 	rc = recvfrom(sock, (void *) readbuf, sizeof(readbuf), 0, NULL, NULL);
238 	if (rc < 0) {
239 		ERROR_FROM_ERRNO(ctx);
240 	}
241 
242 	data = duk_push_fixed_buffer(ctx, rc);
243 	memcpy(data, readbuf, rc);
244 	return 1;
245 }
246 
socket_write(duk_context * ctx)247 static int socket_write(duk_context *ctx) {
248 	int sock = duk_to_int(ctx, 0);
249 	const char *data;
250 	size_t len;
251 	ssize_t rc;
252 
253 	data = duk_to_buffer(ctx, 1, &len);
254 
255 	/* MSG_NOSIGNAL: avoid SIGPIPE */
256 #ifdef __APPLE__
257 	rc = sendto(sock, (void *) data, len, 0, NULL, 0);
258 #else
259 	rc = sendto(sock, (void *) data, len, MSG_NOSIGNAL, NULL, 0);
260 #endif
261 	if (rc < 0) {
262 		ERROR_FROM_ERRNO(ctx);
263 	}
264 
265 	duk_push_int(ctx, rc);
266 	return 1;
267 }
268 
269 static duk_function_list_entry socket_funcs[] = {
270 	{ "createServerSocket", socket_create_server_socket, 2 },
271 	{ "close", socket_close, 1 },
272 	{ "accept", socket_accept, 1 },
273 	{ "connect", socket_connect, 2 },
274 	{ "read", socket_read, 1 },
275 	{ "write", socket_write, 2 },
276 	{ NULL, NULL, 0 }
277 };
278 
socket_register(duk_context * ctx)279 void socket_register(duk_context *ctx) {
280 	/* Set global 'Socket'. */
281 	duk_push_global_object(ctx);
282 	duk_push_object(ctx);
283 	duk_put_function_list(ctx, -1, socket_funcs);
284 	duk_put_prop_string(ctx, -2, "Socket");
285 	duk_pop(ctx);
286 }
287