diff options
Diffstat (limited to 'src/tcp.c')
-rw-r--r-- | src/tcp.c | 181 |
1 files changed, 177 insertions, 4 deletions
@@ -2,7 +2,7 @@ | |||
2 | * TCP object | 2 | * TCP object |
3 | * LuaSocket toolkit | 3 | * LuaSocket toolkit |
4 | * | 4 | * |
5 | * RCS ID: $Id$ | 5 | * RCS ID: $Id: tcp.c,v 1.42 2009/05/27 09:31:35 diego Exp $ |
6 | \*=========================================================================*/ | 6 | \*=========================================================================*/ |
7 | #include <string.h> | 7 | #include <string.h> |
8 | 8 | ||
@@ -19,6 +19,8 @@ | |||
19 | * Internal function prototypes | 19 | * Internal function prototypes |
20 | \*=========================================================================*/ | 20 | \*=========================================================================*/ |
21 | static int global_create(lua_State *L); | 21 | static int global_create(lua_State *L); |
22 | static int global_connect6(lua_State *L); | ||
23 | static int global_bind6(lua_State *L); | ||
22 | static int meth_connect(lua_State *L); | 24 | static int meth_connect(lua_State *L); |
23 | static int meth_listen(lua_State *L); | 25 | static int meth_listen(lua_State *L); |
24 | static int meth_bind(lua_State *L); | 26 | static int meth_bind(lua_State *L); |
@@ -75,6 +77,8 @@ static t_opt optset[] = { | |||
75 | /* functions in library namespace */ | 77 | /* functions in library namespace */ |
76 | static luaL_reg func[] = { | 78 | static luaL_reg func[] = { |
77 | {"tcp", global_create}, | 79 | {"tcp", global_create}, |
80 | {"connect6", global_connect6}, | ||
81 | {"bind6", global_bind6}, | ||
78 | {NULL, NULL} | 82 | {NULL, NULL} |
79 | }; | 83 | }; |
80 | 84 | ||
@@ -208,6 +212,7 @@ static int meth_bind(lua_State *L) | |||
208 | \*-------------------------------------------------------------------------*/ | 212 | \*-------------------------------------------------------------------------*/ |
209 | static int meth_connect(lua_State *L) | 213 | static int meth_connect(lua_State *L) |
210 | { | 214 | { |
215 | |||
211 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | 216 | p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); |
212 | const char *address = luaL_checkstring(L, 2); | 217 | const char *address = luaL_checkstring(L, 2); |
213 | unsigned short port = (unsigned short) luaL_checknumber(L, 3); | 218 | unsigned short port = (unsigned short) luaL_checknumber(L, 3); |
@@ -220,7 +225,6 @@ static int meth_connect(lua_State *L) | |||
220 | lua_pushstring(L, err); | 225 | lua_pushstring(L, err); |
221 | return 2; | 226 | return 2; |
222 | } | 227 | } |
223 | /* turn master object into a client object */ | ||
224 | lua_pushnumber(L, 1); | 228 | lua_pushnumber(L, 1); |
225 | return 1; | 229 | return 1; |
226 | } | 230 | } |
@@ -313,8 +317,7 @@ static int meth_settimeout(lua_State *L) | |||
313 | /*-------------------------------------------------------------------------*\ | 317 | /*-------------------------------------------------------------------------*\ |
314 | * Creates a master tcp object | 318 | * Creates a master tcp object |
315 | \*-------------------------------------------------------------------------*/ | 319 | \*-------------------------------------------------------------------------*/ |
316 | static int global_create(lua_State *L) | 320 | static int global_create(lua_State *L) { |
317 | { | ||
318 | t_socket sock; | 321 | t_socket sock; |
319 | const char *err = inet_trycreate(&sock, SOCK_STREAM); | 322 | const char *err = inet_trycreate(&sock, SOCK_STREAM); |
320 | /* try to allocate a system socket */ | 323 | /* try to allocate a system socket */ |
@@ -337,3 +340,173 @@ static int global_create(lua_State *L) | |||
337 | return 2; | 340 | return 2; |
338 | } | 341 | } |
339 | } | 342 | } |
343 | |||
344 | static const char *trybind6(const char *localaddr, const char *localserv, | ||
345 | struct addrinfo *bindhints, p_tcp tcp) { | ||
346 | struct addrinfo *iterator = NULL, *resolved = NULL; | ||
347 | const char *err = NULL; | ||
348 | /* translate luasocket special values to C */ | ||
349 | if (strcmp(localaddr, "*") == 0) localaddr = NULL; | ||
350 | if (!localserv) localserv = "0"; | ||
351 | /* try resolving */ | ||
352 | err = socket_gaistrerror(getaddrinfo(localaddr, localserv, | ||
353 | bindhints, &resolved)); | ||
354 | if (err) { | ||
355 | if (resolved) freeaddrinfo(resolved); | ||
356 | return err; | ||
357 | } | ||
358 | /* iterate over resolved addresses until one is good */ | ||
359 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { | ||
360 | /* create a new socket each time because parameters | ||
361 | * may have changed */ | ||
362 | const char *err = socket_strerror(socket_create(&tcp->sock, | ||
363 | iterator->ai_family, iterator->ai_socktype, | ||
364 | iterator->ai_protocol)); | ||
365 | /* if failed to create socket, bail out */ | ||
366 | if (err != NULL) { | ||
367 | freeaddrinfo(resolved); | ||
368 | return err; | ||
369 | } | ||
370 | /* all sockets are set as non-blocking initially */ | ||
371 | socket_setnonblocking(&tcp->sock); | ||
372 | /* try binding to local address */ | ||
373 | err = socket_strerror(socket_bind(&tcp->sock, | ||
374 | (SA *) iterator->ai_addr, | ||
375 | iterator->ai_addrlen)); | ||
376 | /* if faiiled, we try the next one */ | ||
377 | if (err != NULL) socket_destroy(&tcp->sock); | ||
378 | /* if success, we abort loop */ | ||
379 | else break; | ||
380 | } | ||
381 | /* at this point, if err is not set, se succeeded */ | ||
382 | if (err == NULL) { | ||
383 | /* save family of chosen local address */ | ||
384 | bindhints->ai_family = iterator->ai_family; | ||
385 | } | ||
386 | /* cleanup and return error */ | ||
387 | freeaddrinfo(resolved); | ||
388 | return err; | ||
389 | } | ||
390 | |||
391 | static int global_bind6(lua_State *L) { | ||
392 | const char *localaddr = luaL_checkstring(L, 1); | ||
393 | const char *localserv = luaL_checkstring(L, 2); | ||
394 | int backlog = luaL_checkint(L, 3); | ||
395 | p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); | ||
396 | struct addrinfo bindhints; | ||
397 | const char *err = NULL; | ||
398 | /* initialize tcp structure */ | ||
399 | io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, | ||
400 | (p_error) socket_ioerror, &tcp->sock); | ||
401 | timeout_init(&tcp->tm, -1, -1); | ||
402 | buffer_init(&tcp->buf, &tcp->io, &tcp->tm); | ||
403 | tcp->sock = SOCKET_INVALID; | ||
404 | /* try binding to local address */ | ||
405 | memset(&bindhints, 0, sizeof(bindhints)); | ||
406 | bindhints.ai_socktype = SOCK_STREAM; | ||
407 | bindhints.ai_family = PF_UNSPEC; | ||
408 | bindhints.ai_flags = AI_PASSIVE; | ||
409 | err = trybind6(localaddr, localserv, &bindhints, tcp); | ||
410 | if (err == NULL) { | ||
411 | /* all server sockets initially with reuseaddr set */ | ||
412 | int val = 1; | ||
413 | setsockopt(tcp->sock, SOL_SOCKET, SO_REUSEADDR, | ||
414 | (char *) &val, sizeof(val)); | ||
415 | /* set the backlog and listen */ | ||
416 | err = socket_strerror(socket_listen(&tcp->sock, backlog)); | ||
417 | if (err) { | ||
418 | socket_destroy(&tcp->sock); | ||
419 | lua_pushnil(L); | ||
420 | lua_pushstring(L, err); | ||
421 | return 2; | ||
422 | } | ||
423 | auxiliar_setclass(L, "tcp{server}", -1); | ||
424 | return 1; | ||
425 | } else { | ||
426 | lua_pushnil(L); | ||
427 | lua_pushstring(L, err); | ||
428 | return 2; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | static const char *tryconnect6(const char *remoteaddr, const char *remoteserv, | ||
433 | struct addrinfo *connecthints, p_tcp tcp) { | ||
434 | struct addrinfo *iterator = NULL, *resolved = NULL; | ||
435 | const char *err = NULL; | ||
436 | /* try resolving */ | ||
437 | err = socket_gaistrerror(getaddrinfo(remoteaddr, remoteserv, | ||
438 | connecthints, &resolved)); | ||
439 | if (err != NULL) { | ||
440 | if (resolved) freeaddrinfo(resolved); | ||
441 | return err; | ||
442 | } | ||
443 | /* iterate over all returned addresses trying to connect */ | ||
444 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { | ||
445 | p_timeout tm = timeout_markstart(&tcp->tm); | ||
446 | /* create new socket if one wasn't created by the bind stage */ | ||
447 | if (tcp->sock == SOCKET_INVALID) { | ||
448 | const char *err = socket_strerror(socket_create(&tcp->sock, | ||
449 | iterator->ai_family, iterator->ai_socktype, | ||
450 | iterator->ai_protocol)); | ||
451 | if (err != NULL) { | ||
452 | freeaddrinfo(resolved); | ||
453 | return err; | ||
454 | } | ||
455 | /* all sockets initially non-blocking */ | ||
456 | socket_setnonblocking(&tcp->sock); | ||
457 | } | ||
458 | /* finally try connecting to remote address */ | ||
459 | err = socket_strerror(socket_connect(&tcp->sock, | ||
460 | (SA *) iterator->ai_addr, | ||
461 | iterator->ai_addrlen, tm)); | ||
462 | /* if success, break out of loop */ | ||
463 | if (err == NULL) break; | ||
464 | } | ||
465 | |||
466 | freeaddrinfo(resolved); | ||
467 | /* here, if err is set, we failed */ | ||
468 | return err; | ||
469 | } | ||
470 | |||
471 | static int global_connect6(lua_State *L) { | ||
472 | const char *remoteaddr = luaL_checkstring(L, 1); | ||
473 | const char *remoteserv = luaL_checkstring(L, 2); | ||
474 | const char *localaddr = luaL_optstring(L, 3, NULL); | ||
475 | const char *localserv = luaL_optstring(L, 4, "0"); | ||
476 | p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); | ||
477 | struct addrinfo bindhints, connecthints; | ||
478 | const char *err = NULL; | ||
479 | /* initialize tcp structure */ | ||
480 | io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, | ||
481 | (p_error) socket_ioerror, &tcp->sock); | ||
482 | timeout_init(&tcp->tm, -1, -1); | ||
483 | buffer_init(&tcp->buf, &tcp->io, &tcp->tm); | ||
484 | tcp->sock = SOCKET_INVALID; | ||
485 | /* allow user to pick local address and port */ | ||
486 | memset(&bindhints, 0, sizeof(bindhints)); | ||
487 | bindhints.ai_socktype = SOCK_STREAM; | ||
488 | bindhints.ai_family = PF_UNSPEC; | ||
489 | bindhints.ai_flags = AI_PASSIVE; | ||
490 | if (localaddr) { | ||
491 | err = trybind6(localaddr, localserv, &bindhints, tcp); | ||
492 | if (err) { | ||
493 | lua_pushnil(L); | ||
494 | lua_pushstring(L, err); | ||
495 | return 2; | ||
496 | } | ||
497 | } | ||
498 | /* try to connect to remote address and port */ | ||
499 | memset(&connecthints, 0, sizeof(connecthints)); | ||
500 | connecthints.ai_socktype = SOCK_STREAM; | ||
501 | /* make sure we try to connect only to the same family */ | ||
502 | connecthints.ai_family = bindhints.ai_family; | ||
503 | err = tryconnect6(remoteaddr, remoteserv, &connecthints, tcp); | ||
504 | if (err) { | ||
505 | socket_destroy(&tcp->sock); | ||
506 | lua_pushnil(L); | ||
507 | lua_pushstring(L, err); | ||
508 | return 2; | ||
509 | } | ||
510 | auxiliar_setclass(L, "tcp{client}", -1); | ||
511 | return 1; | ||
512 | } | ||