summaryrefslogtreecommitdiff
path: root/src/l_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/l_tcp.c')
-rw-r--r--src/l_tcp.c168
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)