]>
Commit | Line | Data |
---|---|---|
1e59de90 TL |
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 | ||
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 | ||
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__ | |
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | } |