diff options
Diffstat (limited to 'src/l_tcp.c')
-rw-r--r-- | src/l_tcp.c | 168 |
1 files changed, 150 insertions, 18 deletions
diff --git a/src/l_tcp.c b/src/l_tcp.c index 158a8ba..c00921e 100644 --- a/src/l_tcp.c +++ b/src/l_tcp.c @@ -40,6 +40,9 @@ #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> +#include <unistd.h> +#include <fcntl.h> +#include <netinet/tcp.h> #include "l_tcp.h" @@ -82,6 +85,29 @@ static char* tcp_endpoint_to_string(tcp_endpoint_t* e) return ret; } +static struct addrinfo* tcp_resolve_endpoint(const char* addr, const char* port, int af, int passive) +{ + struct addrinfo hints, *res; + res = NULL; + memset (&hints, 0, sizeof (hints)); + hints.ai_socktype = SOCK_STREAM; + if(passive) + hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + switch(af) { + case 4: hints.ai_family = AF_INET; break; + case 6: hints.ai_family = AF_INET6; break; + default: hints.ai_family = AF_UNSPEC; break; + } + + int errcode = getaddrinfo(addr, port, &hints, &res); + if(errcode != 0 || !res) { + log_printf(ERROR, "tcp: resolver error: %s", errcode ? gai_strerror(errcode):"no address found!"); + return NULL; + } + + return res; +} + static int init_listener(tcp_endpoint_t* end) { int fd = socket(end->addr_.ss_family, SOCK_STREAM, 0); @@ -127,20 +153,8 @@ static int l_tcp_server(lua_State *L) const char* port = luaL_checkstring(L, 2); int af = luaL_optint(L, 3, 0); - struct addrinfo hints, *res; - res = NULL; - memset (&hints, 0, sizeof (hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - switch(af) { - case 4: hints.ai_family = AF_INET; break; - case 6: hints.ai_family = AF_INET6; break; - default: hints.ai_family = AF_UNSPEC; break; - } - - int errcode = getaddrinfo(addr, port, &hints, &res); - if(errcode != 0 || !res) { - log_printf(ERROR, "tcp: resolver error: %s", errcode ? gai_strerror(errcode):"no address found!"); + struct addrinfo *res = tcp_resolve_endpoint(addr, port, af, 1); + if(!res) { lua_pushnil(L); lua_pushstring(L, "error at tcp-server initialization"); return 2; @@ -199,16 +213,134 @@ static int l_tcp_accept(lua_State *L) return 2; } +static int init_client(tcp_endpoint_t remote, tcp_endpoint_t source) +{ + int fd = socket(remote.addr_.ss_family, SOCK_STREAM, 0); + if(fd < 0) { + log_printf(INFO, "error on socket(): %s", strerror(errno)); + return -1; + } + + int on = 1; + if(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on))) { + log_printf(ERROR, "error on setsockopt(): %s", strerror(errno)); + close(fd); + return -1; + } + + if(fcntl(fd, F_SETFL, O_NONBLOCK)) { + log_printf(ERROR, "error on fcntl(): %s", strerror(errno)); + close(fd); + return -1; + } + + if(source.addr_.ss_family != AF_UNSPEC) { + if(bind(fd, (struct sockaddr *)&(source.addr_), source.len_)==-1) { + log_printf(INFO, "error on bind(): %s", strerror(errno)); + close(fd); + return -1; + } + } + + return fd; +} + static int l_tcp_client(lua_State *L) { - // TODO: implement this - return 0; + const char* raddr = luaL_checkstring(L, 1); + const char* rport = luaL_checkstring(L, 2); + int af = luaL_optint(L, 3, 0); + const char* saddr = luaL_optstring(L, 4, NULL); + + tcp_endpoint_t remote, source; + memset(&remote, 0, sizeof(tcp_endpoint_t)); + memset(&source, 0, sizeof(tcp_endpoint_t)); + source.addr_.ss_family = AF_UNSPEC; + + struct addrinfo *res = tcp_resolve_endpoint(raddr, rport, af, 0); + if(!res) { + lua_pushnil(L); + lua_pushstring(L, "error at tcp-client initialization"); + return 2; + } + memcpy(&(remote.addr_), res->ai_addr, res->ai_addrlen); + remote.len_ = res->ai_addrlen; + freeaddrinfo(res); + + if(saddr) { + res = tcp_resolve_endpoint(saddr, NULL, af, 0); + if(!res) { + lua_pushnil(L); + lua_pushstring(L, "error at tcp-client initialization"); + return 2; + } + memcpy(&(source.addr_), res->ai_addr, res->ai_addrlen); + source.len_ = res->ai_addrlen; + freeaddrinfo(res); + } + + int fd = init_client(remote, source); + if(fd < 0) { + lua_pushnil(L); + lua_pushstring(L, "error at tcp-client initialization"); + return 2; + } + + lua_newtable(L); + + lua_pushliteral(L, "remote_end"); + tcp_endpoint_t* re = newtcpendudata(L); + memcpy(re, &remote, sizeof(tcp_endpoint_t)); + lua_settable(L, -3); + + if(source.addr_.ss_family != AF_UNSPEC) { + lua_pushliteral(L, "source_end"); + tcp_endpoint_t* se = newtcpendudata(L); + memcpy(se, &source, sizeof(tcp_endpoint_t)); + lua_settable(L, -3); + } + + lua_pushliteral(L, "fd"); + lua_pushinteger(L, fd); + lua_settable(L, -3); + + if(connect(fd, (struct sockaddr *)&(remote.addr_), remote.len_)==-1) { + if(errno == EINPROGRESS) { + lua_pushboolean(L, 0); + return 2; + } + + close(fd); + log_printf(INFO, "error on connect(): %s", strerror(errno)); + lua_pushnil(L); + lua_pushstring(L, "connect() failed"); + return 2; + } + + lua_pushboolean(L, 1); + return 2; } static int l_tcp_connect(lua_State *L) { - // TODO: implement this - return 0; + int fd = luaL_checkint(L, 1); + int error = 0; + int len = sizeof(error); + if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len)==-1) { + log_printf(ERROR, "error on getsockopt(): %s", strerror(errno)); + lua_pushnil(L); + lua_pushstring(L, "can't read SO_ERROR"); + return 2; + } + if(error) { + log_printf(ERROR, "error on connect(): %s", strerror(error)); + lua_pushnil(L); + lua_pushstring(L, "connect() failed"); + return 2; + } + + lua_pushboolean(L, 1); + return 1; } static int l_tcp_recv(lua_State *L) |