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 \*-------------------------------------------------------------------------*/
tcp_open(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_send(lua_State * L)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
meth_receive(lua_State * L)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
meth_getstats(lua_State * L)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
meth_setstats(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_getoption(lua_State * L)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
meth_setoption(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_getfd(lua_State * L)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 */
meth_setfd(lua_State * L)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
meth_dirty(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_accept(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_bind(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_connect(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_close(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_getfamily(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_listen(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_shutdown(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_getpeername(lua_State * L)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
meth_getsockname(lua_State * L)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 \*-------------------------------------------------------------------------*/
meth_settimeout(lua_State * L)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
meth_gettimeout(lua_State * L)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 \*-------------------------------------------------------------------------*/
tcp_create(lua_State * L,int family)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
global_create(lua_State * L)393 static int global_create(lua_State *L) {
394 return tcp_create(L, AF_UNSPEC);
395 }
396
global_create4(lua_State * L)397 static int global_create4(lua_State *L) {
398 return tcp_create(L, AF_INET);
399 }
400
global_create6(lua_State * L)401 static int global_create6(lua_State *L) {
402 return tcp_create(L, AF_INET6);
403 }
404
global_connect(lua_State * L)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