#include #include #include #include #include #include #include "connection.h" #include "event.h" #include "list.h" #include "logging.h" #include "network.h" struct connection { struct list_head list; struct sockaddr_in src_addr; struct sockaddr_in dst_addr; struct event_fd *src_event; struct event_fd *dst_event; struct event_timeout *timeout; time_t login_time; }; static LIST_HEAD(connection_list); static struct connection * create_connection() { struct connection *con = malloc(sizeof(struct connection)); if (con == NULL) { log_print(LOG_WARN, "listen_handler(): out of memory"); return 0; } memset(con, 0, sizeof(struct connection)); con->login_time = time(NULL); return con; } static void destroy_connection(struct connection *con) { if (con->timeout != NULL) event_remove_timeout(con->timeout); int src_fd = event_get_fd(con->src_event); if (src_fd != -1) { close(src_fd); event_remove_fd(con->src_event); } int dst_fd = event_get_fd(con->dst_event); if (dst_fd != -1) { close(dst_fd); event_remove_fd(con->dst_event); } free(con); } static int forward_handler(int fd, void *privdata) { struct connection *con = (struct connection *)privdata; char buf[256]; int len = read(fd, buf, sizeof(buf)); if (len <= 0) { list_del(&con->list); destroy_connection(con); return -1; } int src_fd = event_get_fd(con->src_event); int dst_fd = event_get_fd(con->dst_event); /* client -> device */ if (src_fd == fd && dst_fd != -1) write(dst_fd, buf, len); /* device -> client */ if (dst_fd == fd && src_fd != -1) write(src_fd, buf, len); return 0; } static int connect_handler(int fd, void *privdata) { struct connection *con = (struct connection *)privdata; if (tcp_connect_error(fd)) { list_del(&con->list); destroy_connection(con); return -1; } log_print(LOG_INFO, "forwarding to %s", get_sockaddr_buf(&con->dst_addr)); /* everything from dst will be forwarded */ event_add_readfd(con->dst_event, 0, forward_handler, con); /* remove connect_handler */ event_add_writefd(con->dst_event, 0, NULL, NULL); /* remove timeout */ event_remove_timeout(con->timeout); con->timeout = NULL; return 0; } static int connect_timeout(void *privdata) { struct connection *con = (struct connection *)privdata; log_print(LOG_INFO, "connect to %s timed out", get_sockaddr_buf(&con->dst_addr)); list_del(&con->list); destroy_connection(con); /* singleshot timer */ return -1; } static int client_handler(int fd, void *privdata) { struct connection *con = (struct connection *)privdata; char buf[256]; int len = read(fd, buf, sizeof(buf)); if (len <= 0) { list_del(&con->list); destroy_connection(con); return -1; } if (parse_saddr(buf, &con->dst_addr) != 0) { list_del(&con->list); destroy_connection(con); return -1; } // TODO: check destination int dst_fd = tcp_connect_socket(&con->dst_addr); if (dst_fd < 0) { list_del(&con->list); destroy_connection(con); return -1; } log_print(LOG_INFO, "connecting to %s", get_sockaddr_buf(&con->dst_addr)); struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0; con->timeout = event_add_timeout(&tv, connect_timeout, con); /* wait for connect() (non-blocking) */ con->dst_event = event_add_writefd(NULL, dst_fd, connect_handler, con); /* everything from src will be forwarded */ event_add_readfd(con->src_event, 0, forward_handler, con); return 0; } static int admin_handler(int fd, void *privdata) { struct connection *con = (struct connection *)privdata; char buf[256]; int len = read(fd, buf, sizeof(buf)); if (len <= 0) { list_del(&con->list); destroy_connection(con); return -1; } if (strncmp(buf, "list", 4) == 0) { struct connection *entry; list_for_each_entry(entry, &connection_list, list) { len = snprintf(buf, sizeof(buf), "%d,", event_get_fd(entry->src_event)); len += get_sockaddr(&entry->src_addr, buf + len, sizeof(buf) - len); len += snprintf(buf + len, sizeof(buf) - len, ",%d,", event_get_fd(entry->dst_event)); len += get_sockaddr(&entry->dst_addr, buf + len, sizeof(buf) - len); len += snprintf(buf + len, sizeof(buf) - len, ",%ld\n", (time(NULL) - entry->login_time)); write(fd, buf, len); } } else if (strncmp(buf, "kill", 4) == 0) { } else if (strncmp(buf, "quit", 4) == 0) { list_del(&con->list); destroy_connection(con); return -1; } return 0; } int listen_handler(int fd, void *privdata) { int mode = (int)privdata; struct connection *con = create_connection(); if (con == NULL) { log_print(LOG_WARN, "listen_handler(): out of memory"); return 0; } unsigned int i = sizeof(con->src_addr); int src_fd = accept(fd, (struct sockaddr *)&con->src_addr, &i); if (src_fd < 0) { free(con); return 0; } log_print(LOG_INFO, "accepted connection from %s", get_sockaddr_buf(&con->src_addr)); list_add_tail(&con->list, &connection_list); if (mode == CON_ADMIN) con->src_event = event_add_readfd(NULL, src_fd, admin_handler, con); else if (mode == CON_NORMAL) con->src_event = event_add_readfd(NULL, src_fd, client_handler, con); return 0; }