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\*=========================================================================*/
20static int global_create(lua_State *L);
21static int global_create4(lua_State *L);
22static int global_create6(lua_State *L);
23static int global_connect(lua_State *L);
24static int meth_connect(lua_State *L);
25static int meth_listen(lua_State *L);
26static int meth_getfamily(lua_State *L);
27static int meth_bind(lua_State *L);
28static int meth_send(lua_State *L);
29static int meth_getstats(lua_State *L);
30static int meth_setstats(lua_State *L);
31static int meth_getsockname(lua_State *L);
32static int meth_getpeername(lua_State *L);
33static int meth_shutdown(lua_State *L);
34static int meth_receive(lua_State *L);
35static int meth_accept(lua_State *L);
36static int meth_close(lua_State *L);
37static int meth_getoption(lua_State *L);
38static int meth_setoption(lua_State *L);
39static int meth_gettimeout(lua_State *L);
40static int meth_settimeout(lua_State *L);
41static int meth_getfd(lua_State *L);
42static int meth_setfd(lua_State *L);
43static int meth_dirty(lua_State *L);
44
45/* tcp object methods */
46static 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 */
75static 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
85static 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 */
96static 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\*-------------------------------------------------------------------------*/
107int 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\*-------------------------------------------------------------------------*/
128static 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
133static 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
138static 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
143static 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\*-------------------------------------------------------------------------*/
151static 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
157static 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\*-------------------------------------------------------------------------*/
166static 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 */
174static 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
181static 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\*-------------------------------------------------------------------------*/
192static 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\*-------------------------------------------------------------------------*/
222static 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\*-------------------------------------------------------------------------*/
245static 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\*-------------------------------------------------------------------------*/
272static 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\*-------------------------------------------------------------------------*/
283static 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\*-------------------------------------------------------------------------*/
301static 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\*-------------------------------------------------------------------------*/
320static 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\*-------------------------------------------------------------------------*/
334static 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
340static 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\*-------------------------------------------------------------------------*/
349static 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
355static 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\*-------------------------------------------------------------------------*/
367static 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
393static int global_create(lua_State *L) {
394 return tcp_create(L, AF_UNSPEC);
395}
396
397static int global_create4(lua_State *L) {
398 return tcp_create(L, AF_INET);
399}
400
401static int global_create6(lua_State *L) {
402 return tcp_create(L, AF_INET6);
403}
404
405static 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