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