1 | /*=========================================================================*\ |
2 | * TCP object |
3 | * LuaSocket toolkit |
4 | \*=========================================================================*/ |
5 | #include <string.h> |
6 | |
7 | #include "lua.h" |
8 | #include "lauxlib.h" |
9 | #include "compat.h" |
10 | |
11 | #include "auxiliar.h" |
12 | #include "socket.h" |
13 | #include "inet.h" |
14 | #include "options.h" |
15 | #include "tcp.h" |
16 | |
17 | /*=========================================================================*\ |
18 | * Internal function prototypes |
19 | \*=========================================================================*/ |
20 | static int global_create(lua_State *L); |
21 | static int global_create4(lua_State *L); |
22 | static int global_create6(lua_State *L); |
23 | static int global_connect(lua_State *L); |
24 | static int meth_connect(lua_State *L); |
25 | static int meth_listen(lua_State *L); |
26 | static int meth_getfamily(lua_State *L); |
27 | static int meth_bind(lua_State *L); |
28 | static int meth_send(lua_State *L); |
29 | static int meth_getstats(lua_State *L); |
30 | static int meth_setstats(lua_State *L); |
31 | static int meth_getsockname(lua_State *L); |
32 | static int meth_getpeername(lua_State *L); |
33 | static int meth_shutdown(lua_State *L); |
34 | static int meth_receive(lua_State *L); |
35 | static int meth_accept(lua_State *L); |
36 | static int meth_close(lua_State *L); |
37 | static int meth_getoption(lua_State *L); |
38 | static int meth_setoption(lua_State *L); |
39 | static int meth_gettimeout(lua_State *L); |
40 | static int meth_settimeout(lua_State *L); |
41 | static int meth_getfd(lua_State *L); |
42 | static int meth_setfd(lua_State *L); |
43 | static int meth_dirty(lua_State *L); |
44 | |
45 | /* tcp object methods */ |
46 | static luaL_Reg tcp_methods[] = { |
47 | {"__gc" , meth_close}, |
48 | {"__tostring" , auxiliar_tostring}, |
49 | {"accept" , meth_accept}, |
50 | {"bind" , meth_bind}, |
51 | {"close" , meth_close}, |
52 | {"connect" , meth_connect}, |
53 | {"dirty" , meth_dirty}, |
54 | {"getfamily" , meth_getfamily}, |
55 | {"getfd" , meth_getfd}, |
56 | {"getoption" , meth_getoption}, |
57 | {"getpeername" , meth_getpeername}, |
58 | {"getsockname" , meth_getsockname}, |
59 | {"getstats" , meth_getstats}, |
60 | {"setstats" , meth_setstats}, |
61 | {"listen" , meth_listen}, |
62 | {"receive" , meth_receive}, |
63 | {"send" , meth_send}, |
64 | {"setfd" , meth_setfd}, |
65 | {"setoption" , meth_setoption}, |
66 | {"setpeername" , meth_connect}, |
67 | {"setsockname" , meth_bind}, |
68 | {"settimeout" , meth_settimeout}, |
69 | {"gettimeout" , meth_gettimeout}, |
70 | {"shutdown" , meth_shutdown}, |
71 | {NULL, NULL} |
72 | }; |
73 | |
74 | /* socket option handlers */ |
75 | static t_opt optget[] = { |
76 | {"keepalive" , opt_get_keepalive}, |
77 | {"reuseaddr" , opt_get_reuseaddr}, |
78 | {"reuseport" , opt_get_reuseport}, |
79 | {"tcp-nodelay" , opt_get_tcp_nodelay}, |
80 | {"linger" , opt_get_linger}, |
81 | {"error" , opt_get_error}, |
82 | {NULL, NULL} |
83 | }; |
84 | |
85 | static t_opt optset[] = { |
86 | {"keepalive" , opt_set_keepalive}, |
87 | {"reuseaddr" , opt_set_reuseaddr}, |
88 | {"reuseport" , opt_set_reuseport}, |
89 | {"tcp-nodelay" , opt_set_tcp_nodelay}, |
90 | {"ipv6-v6only" , opt_set_ip6_v6only}, |
91 | {"linger" , opt_set_linger}, |
92 | {NULL, NULL} |
93 | }; |
94 | |
95 | /* functions in library namespace */ |
96 | static luaL_Reg func[] = { |
97 | {"tcp" , global_create}, |
98 | {"tcp4" , global_create4}, |
99 | {"tcp6" , global_create6}, |
100 | {"connect" , global_connect}, |
101 | {NULL, NULL} |
102 | }; |
103 | |
104 | /*-------------------------------------------------------------------------*\ |
105 | * Initializes module |
106 | \*-------------------------------------------------------------------------*/ |
107 | int tcp_open(lua_State *L) |
108 | { |
109 | /* create classes */ |
110 | auxiliar_newclass(L, "tcp{master}" , tcp_methods); |
111 | auxiliar_newclass(L, "tcp{client}" , tcp_methods); |
112 | auxiliar_newclass(L, "tcp{server}" , tcp_methods); |
113 | /* create class groups */ |
114 | auxiliar_add2group(L, "tcp{master}" , "tcp{any}" ); |
115 | auxiliar_add2group(L, "tcp{client}" , "tcp{any}" ); |
116 | auxiliar_add2group(L, "tcp{server}" , "tcp{any}" ); |
117 | /* define library functions */ |
118 | luaL_setfuncs(L, func, 0); |
119 | return 0; |
120 | } |
121 | |
122 | /*=========================================================================*\ |
123 | * Lua methods |
124 | \*=========================================================================*/ |
125 | /*-------------------------------------------------------------------------*\ |
126 | * Just call buffered IO methods |
127 | \*-------------------------------------------------------------------------*/ |
128 | static int meth_send(lua_State *L) { |
129 | p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}" , 1); |
130 | return buffer_meth_send(L, &tcp->buf); |
131 | } |
132 | |
133 | static int meth_receive(lua_State *L) { |
134 | p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}" , 1); |
135 | return buffer_meth_receive(L, &tcp->buf); |
136 | } |
137 | |
138 | static int meth_getstats(lua_State *L) { |
139 | p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}" , 1); |
140 | return buffer_meth_getstats(L, &tcp->buf); |
141 | } |
142 | |
143 | static int meth_setstats(lua_State *L) { |
144 | p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}" , 1); |
145 | return buffer_meth_setstats(L, &tcp->buf); |
146 | } |
147 | |
148 | /*-------------------------------------------------------------------------*\ |
149 | * Just call option handler |
150 | \*-------------------------------------------------------------------------*/ |
151 | static int meth_getoption(lua_State *L) |
152 | { |
153 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
154 | return opt_meth_getoption(L, optget, &tcp->sock); |
155 | } |
156 | |
157 | static int meth_setoption(lua_State *L) |
158 | { |
159 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
160 | return opt_meth_setoption(L, optset, &tcp->sock); |
161 | } |
162 | |
163 | /*-------------------------------------------------------------------------*\ |
164 | * Select support methods |
165 | \*-------------------------------------------------------------------------*/ |
166 | static int meth_getfd(lua_State *L) |
167 | { |
168 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
169 | lua_pushnumber(L, (int) tcp->sock); |
170 | return 1; |
171 | } |
172 | |
173 | /* this is very dangerous, but can be handy for those that are brave enough */ |
174 | static int meth_setfd(lua_State *L) |
175 | { |
176 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
177 | tcp->sock = (t_socket) luaL_checknumber(L, 2); |
178 | return 0; |
179 | } |
180 | |
181 | static int meth_dirty(lua_State *L) |
182 | { |
183 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
184 | lua_pushboolean(L, !buffer_isempty(&tcp->buf)); |
185 | return 1; |
186 | } |
187 | |
188 | /*-------------------------------------------------------------------------*\ |
189 | * Waits for and returns a client object attempting connection to the |
190 | * server object |
191 | \*-------------------------------------------------------------------------*/ |
192 | static int meth_accept(lua_State *L) |
193 | { |
194 | p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}" , 1); |
195 | p_timeout tm = timeout_markstart(&server->tm); |
196 | t_socket sock; |
197 | const char *err = inet_tryaccept(&server->sock, server->family, &sock, tm); |
198 | /* if successful, push client socket */ |
199 | if (err == NULL) { |
200 | p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); |
201 | auxiliar_setclass(L, "tcp{client}" , -1); |
202 | /* initialize structure fields */ |
203 | memset(clnt, 0, sizeof(t_tcp)); |
204 | socket_setnonblocking(&sock); |
205 | clnt->sock = sock; |
206 | io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv, |
207 | (p_error) socket_ioerror, &clnt->sock); |
208 | timeout_init(&clnt->tm, -1, -1); |
209 | buffer_init(&clnt->buf, &clnt->io, &clnt->tm); |
210 | clnt->family = server->family; |
211 | return 1; |
212 | } else { |
213 | lua_pushnil(L); |
214 | lua_pushstring(L, err); |
215 | return 2; |
216 | } |
217 | } |
218 | |
219 | /*-------------------------------------------------------------------------*\ |
220 | * Binds an object to an address |
221 | \*-------------------------------------------------------------------------*/ |
222 | static int meth_bind(lua_State *L) { |
223 | p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}" , 1); |
224 | const char *address = luaL_checkstring(L, 2); |
225 | const char *port = luaL_checkstring(L, 3); |
226 | const char *err; |
227 | struct addrinfo bindhints; |
228 | memset(&bindhints, 0, sizeof(bindhints)); |
229 | bindhints.ai_socktype = SOCK_STREAM; |
230 | bindhints.ai_family = tcp->family; |
231 | bindhints.ai_flags = AI_PASSIVE; |
232 | err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints); |
233 | if (err) { |
234 | lua_pushnil(L); |
235 | lua_pushstring(L, err); |
236 | return 2; |
237 | } |
238 | lua_pushnumber(L, 1); |
239 | return 1; |
240 | } |
241 | |
242 | /*-------------------------------------------------------------------------*\ |
243 | * Turns a master tcp object into a client object. |
244 | \*-------------------------------------------------------------------------*/ |
245 | static int meth_connect(lua_State *L) { |
246 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
247 | const char *address = luaL_checkstring(L, 2); |
248 | const char *port = luaL_checkstring(L, 3); |
249 | struct addrinfo connecthints; |
250 | const char *err; |
251 | memset(&connecthints, 0, sizeof(connecthints)); |
252 | connecthints.ai_socktype = SOCK_STREAM; |
253 | /* make sure we try to connect only to the same family */ |
254 | connecthints.ai_family = tcp->family; |
255 | timeout_markstart(&tcp->tm); |
256 | err = inet_tryconnect(&tcp->sock, &tcp->family, address, port, |
257 | &tcp->tm, &connecthints); |
258 | /* have to set the class even if it failed due to non-blocking connects */ |
259 | auxiliar_setclass(L, "tcp{client}" , 1); |
260 | if (err) { |
261 | lua_pushnil(L); |
262 | lua_pushstring(L, err); |
263 | return 2; |
264 | } |
265 | lua_pushnumber(L, 1); |
266 | return 1; |
267 | } |
268 | |
269 | /*-------------------------------------------------------------------------*\ |
270 | * Closes socket used by object |
271 | \*-------------------------------------------------------------------------*/ |
272 | static int meth_close(lua_State *L) |
273 | { |
274 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
275 | socket_destroy(&tcp->sock); |
276 | lua_pushnumber(L, 1); |
277 | return 1; |
278 | } |
279 | |
280 | /*-------------------------------------------------------------------------*\ |
281 | * Returns family as string |
282 | \*-------------------------------------------------------------------------*/ |
283 | static int meth_getfamily(lua_State *L) |
284 | { |
285 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
286 | if (tcp->family == AF_INET6) { |
287 | lua_pushliteral(L, "inet6" ); |
288 | return 1; |
289 | } else if (tcp->family == AF_INET) { |
290 | lua_pushliteral(L, "inet4" ); |
291 | return 1; |
292 | } else { |
293 | lua_pushliteral(L, "inet4" ); |
294 | return 1; |
295 | } |
296 | } |
297 | |
298 | /*-------------------------------------------------------------------------*\ |
299 | * Puts the sockt in listen mode |
300 | \*-------------------------------------------------------------------------*/ |
301 | static int meth_listen(lua_State *L) |
302 | { |
303 | p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}" , 1); |
304 | int backlog = (int) luaL_optnumber(L, 2, 32); |
305 | int err = socket_listen(&tcp->sock, backlog); |
306 | if (err != IO_DONE) { |
307 | lua_pushnil(L); |
308 | lua_pushstring(L, socket_strerror(err)); |
309 | return 2; |
310 | } |
311 | /* turn master object into a server object */ |
312 | auxiliar_setclass(L, "tcp{server}" , 1); |
313 | lua_pushnumber(L, 1); |
314 | return 1; |
315 | } |
316 | |
317 | /*-------------------------------------------------------------------------*\ |
318 | * Shuts the connection down partially |
319 | \*-------------------------------------------------------------------------*/ |
320 | static int meth_shutdown(lua_State *L) |
321 | { |
322 | /* SHUT_RD, SHUT_WR, SHUT_RDWR have the value 0, 1, 2, so we can use method index directly */ |
323 | static const char* methods[] = { "receive" , "send" , "both" , NULL }; |
324 | p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}" , 1); |
325 | int how = luaL_checkoption(L, 2, "both" , methods); |
326 | socket_shutdown(&tcp->sock, how); |
327 | lua_pushnumber(L, 1); |
328 | return 1; |
329 | } |
330 | |
331 | /*-------------------------------------------------------------------------*\ |
332 | * Just call inet methods |
333 | \*-------------------------------------------------------------------------*/ |
334 | static int meth_getpeername(lua_State *L) |
335 | { |
336 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
337 | return inet_meth_getpeername(L, &tcp->sock, tcp->family); |
338 | } |
339 | |
340 | static int meth_getsockname(lua_State *L) |
341 | { |
342 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
343 | return inet_meth_getsockname(L, &tcp->sock, tcp->family); |
344 | } |
345 | |
346 | /*-------------------------------------------------------------------------*\ |
347 | * Just call tm methods |
348 | \*-------------------------------------------------------------------------*/ |
349 | static int meth_settimeout(lua_State *L) |
350 | { |
351 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
352 | return timeout_meth_settimeout(L, &tcp->tm); |
353 | } |
354 | |
355 | static int meth_gettimeout(lua_State *L) |
356 | { |
357 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}" , 1); |
358 | return timeout_meth_gettimeout(L, &tcp->tm); |
359 | } |
360 | |
361 | /*=========================================================================*\ |
362 | * Library functions |
363 | \*=========================================================================*/ |
364 | /*-------------------------------------------------------------------------*\ |
365 | * Creates a master tcp object |
366 | \*-------------------------------------------------------------------------*/ |
367 | static int tcp_create(lua_State *L, int family) { |
368 | p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); |
369 | memset(tcp, 0, sizeof(t_tcp)); |
370 | /* set its type as master object */ |
371 | auxiliar_setclass(L, "tcp{master}" , -1); |
372 | /* if family is AF_UNSPEC, we leave the socket invalid and |
373 | * store AF_UNSPEC into family. This will allow it to later be |
374 | * replaced with an AF_INET6 or AF_INET socket upon first use. */ |
375 | tcp->sock = SOCKET_INVALID; |
376 | tcp->family = family; |
377 | io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, |
378 | (p_error) socket_ioerror, &tcp->sock); |
379 | timeout_init(&tcp->tm, -1, -1); |
380 | buffer_init(&tcp->buf, &tcp->io, &tcp->tm); |
381 | if (family != AF_UNSPEC) { |
382 | const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0); |
383 | if (err != NULL) { |
384 | lua_pushnil(L); |
385 | lua_pushstring(L, err); |
386 | return 2; |
387 | } |
388 | socket_setnonblocking(&tcp->sock); |
389 | } |
390 | return 1; |
391 | } |
392 | |
393 | static int global_create(lua_State *L) { |
394 | return tcp_create(L, AF_UNSPEC); |
395 | } |
396 | |
397 | static int global_create4(lua_State *L) { |
398 | return tcp_create(L, AF_INET); |
399 | } |
400 | |
401 | static int global_create6(lua_State *L) { |
402 | return tcp_create(L, AF_INET6); |
403 | } |
404 | |
405 | static int global_connect(lua_State *L) { |
406 | const char *remoteaddr = luaL_checkstring(L, 1); |
407 | const char *remoteserv = luaL_checkstring(L, 2); |
408 | const char *localaddr = luaL_optstring(L, 3, NULL); |
409 | const char *localserv = luaL_optstring(L, 4, "0" ); |
410 | int family = inet_optfamily(L, 5, "unspec" ); |
411 | p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); |
412 | struct addrinfo bindhints, connecthints; |
413 | const char *err = NULL; |
414 | /* initialize tcp structure */ |
415 | memset(tcp, 0, sizeof(t_tcp)); |
416 | io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, |
417 | (p_error) socket_ioerror, &tcp->sock); |
418 | timeout_init(&tcp->tm, -1, -1); |
419 | buffer_init(&tcp->buf, &tcp->io, &tcp->tm); |
420 | tcp->sock = SOCKET_INVALID; |
421 | tcp->family = AF_UNSPEC; |
422 | /* allow user to pick local address and port */ |
423 | memset(&bindhints, 0, sizeof(bindhints)); |
424 | bindhints.ai_socktype = SOCK_STREAM; |
425 | bindhints.ai_family = family; |
426 | bindhints.ai_flags = AI_PASSIVE; |
427 | if (localaddr) { |
428 | err = inet_trybind(&tcp->sock, &tcp->family, localaddr, |
429 | localserv, &bindhints); |
430 | if (err) { |
431 | lua_pushnil(L); |
432 | lua_pushstring(L, err); |
433 | return 2; |
434 | } |
435 | } |
436 | /* try to connect to remote address and port */ |
437 | memset(&connecthints, 0, sizeof(connecthints)); |
438 | connecthints.ai_socktype = SOCK_STREAM; |
439 | /* make sure we try to connect only to the same family */ |
440 | connecthints.ai_family = tcp->family; |
441 | err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv, |
442 | &tcp->tm, &connecthints); |
443 | if (err) { |
444 | socket_destroy(&tcp->sock); |
445 | lua_pushnil(L); |
446 | lua_pushstring(L, err); |
447 | return 2; |
448 | } |
449 | auxiliar_setclass(L, "tcp{client}" , -1); |
450 | return 1; |
451 | } |
452 | |