diff --git a/connection.c b/connection.c index eb88ca4..723578d 100644 --- a/connection.c +++ b/connection.c @@ -1,5 +1,149 @@ #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; + + int src_fd; + int dst_fd; + + unsigned long 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; + } + + con->src_fd = con->dst_fd = -1; + con->login_time = time(NULL); + + return con; +} + +static int destroy_connection(struct connection *con) +{ + if (con->src_fd != -1) { + close(con->src_fd); + event_remove_fd(con->src_fd); + } + + if (con->dst_fd != -1) { + close(con->dst_fd); + event_remove_fd(con->dst_fd); + } + + free(con); + return 0; +} + +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; + } + + /* client -> device */ + if (con->src_fd == fd) + write(con->dst_fd, buf, len); + + /* device -> client */ + else if (con->dst_fd == fd) + write(con->src_fd, buf, len); + + return 0; +} + +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; + } + + // check destination + con->dst_fd = tcp_connect_socket(&con->dst_addr); + if (con->dst_fd < 0) { + list_del(&con->list); + destroy_connection(con); + return -1; + } + + log_print(LOG_INFO, "forwarding to %s", get_sockaddr_buf(&con->dst_addr)); + event_add_readfd(con->dst_fd, forward_handler, con); + event_add_readfd(con->src_fd, forward_handler, con); + + /* dismiss client_handler */ + return -1; +} + +static int admin_handler(int fd, void *privdata) +{ + return -1; +} + +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); + con->src_fd = accept(fd, (struct sockaddr *)&con->src_addr, &i); + if (con->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) + event_add_readfd(con->src_fd, client_handler, con); + else + event_add_readfd(con->src_fd, admin_handler, con); + + return 0; +} + + diff --git a/connection.h b/connection.h index 724bce9..50433ce 100644 --- a/connection.h +++ b/connection.h @@ -1,23 +1,9 @@ #ifndef _CONNECTION_H_ #define _CONNECTION_H_ -#include +#define CON_NORMAL 0x01 +#define CON_ADMIN 0x02 -#include "list.h" - -struct connection { - struct list_head list; - - struct sockaddr_in src_addr; - struct sockaddr_in dst_addr; - - int src_fd; - int dst_fd; - - unsigned long login_time; - unsigned long idle_time; - - char user[32]; -}; +int listen_handler(int fd, void *privdata); #endif // _CONNECTION_H_ diff --git a/event.c b/event.c index 2d34826..2161e10 100644 --- a/event.c +++ b/event.c @@ -29,15 +29,17 @@ #include "event.h" -static LIST_HEAD(readfd_list); -static LIST_HEAD(writefd_list); -static LIST_HEAD(exceptfd_list); +#define FD_TYPE (FD_READ | FD_WRITE | FD_EXCEPT) +#define FD_NEW 0x10 +#define FD_DELETE 0x20 + +static LIST_HEAD(fd_list); static LIST_HEAD(timeout_list); struct fd_entry { struct list_head list; int fd; - int type; + int flags; int (*callback)(int fd, void *privdata); void *privdata; }; @@ -52,7 +54,7 @@ struct timeout_entry { int event_add_fd(int fd, int type, int (*callback)(int fd, void *privdata), void *privdata) { - if (fd < 0 || fd > FD_SETSIZE) + if (fd < 0 || fd > FD_SETSIZE || type == 0) return -1; struct fd_entry *entry; @@ -63,59 +65,23 @@ int event_add_fd(int fd, int type, int (*callback)(int fd, void *privdata), void } entry->fd = fd; - entry->type = type; + entry->flags = (type & FD_TYPE) | FD_NEW; entry->callback = callback; entry->privdata = privdata; - switch (type) { - case FD_READ: - list_add_tail(&entry->list, &readfd_list); - break; - - case FD_WRITE: - list_add_tail(&entry->list, &writefd_list); - break; - - case FD_EXCEPT: - list_add_tail(&entry->list, &exceptfd_list); - break; - - default: - log_print(LOG_ERROR, "add_fd(): unknown type"); - free(entry); - return -1; - } - + list_add_tail(&entry->list, &fd_list); return 0; } int event_remove_fd(int fd) { struct fd_entry *entry, *tmp; - list_for_each_entry_safe(entry, tmp, &readfd_list, list) { + list_for_each_entry_safe(entry, tmp, &fd_list, list) { if (entry->fd == fd) { - list_del(&entry->list); - free(entry); + entry->flags |= FD_DELETE; return 0; } } - - list_for_each_entry_safe(entry, tmp, &writefd_list, list) { - if (entry->fd == fd) { - list_del(&entry->list); - free(entry); - return 0; - } - } - - list_for_each_entry_safe(entry, tmp, &exceptfd_list, list) { - if (entry->fd == fd) { - list_del(&entry->list); - free(entry); - return 0; - } - } - return -1; } @@ -183,44 +149,52 @@ int event_add_timeout(struct timeval *timeout, int (*callback)(void *privdata), return 0; } -void event_loop(void) +int event_loop(void) { + fd_set *fdsets = malloc(sizeof(fd_set) * 3); + if (fdsets == NULL) { + log_print(LOG_ERROR, "event_loop(): out of memory"); + return -1; + } + while (1) { - fd_set readfds, *readfds_p = NULL; - fd_set writefds, *writefds_p = NULL; - fd_set exceptfds, *exceptfds_p =NULL; + fd_set *readfds = NULL, *writefds = NULL, *exceptfds = NULL; + struct fd_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &fd_list, list) { + entry->flags &= ~FD_NEW; + + if (entry->flags & FD_DELETE) { + log_print(LOG_DEBUG, "event_loop: delete FD: %d (%p/%p)", entry->fd, entry->callback, entry->privdata); + list_del(&entry->list); + free(entry); + + } else if ((entry->flags & FD_READ) != 0) { + if (readfds == NULL) { + readfds = &fdsets[0]; + FD_ZERO(readfds); + } + log_print(LOG_DEBUG, "event_loop: read FD: %d (%p/%p)", entry->fd, entry->callback, entry->privdata); + FD_SET(entry->fd, readfds); + + } else if ((entry->flags & FD_WRITE) != 0) { + if (writefds == NULL) { + writefds = &fdsets[1]; + FD_ZERO(writefds); + } + log_print(LOG_DEBUG, "event_loop: write FD: %d (%p/%p)", entry->fd, entry->callback, entry->privdata); + FD_SET(entry->fd, writefds); + + } else if ((entry->flags & FD_EXCEPT) != 0) { + if (exceptfds == NULL) { + exceptfds = &fdsets[2]; + FD_ZERO(exceptfds); + } + log_print(LOG_DEBUG, "event_loop: except FD: %d (%p/%p)", entry->fd, entry->callback, entry->privdata); + FD_SET(entry->fd, exceptfds); + } + } + struct timeval timeout, *timeout_p = NULL; - - if (!list_empty(&readfd_list)) { - struct fd_entry *entry; - - FD_ZERO(&readfds); - list_for_each_entry(entry, &readfd_list, list) - FD_SET(entry->fd, &readfds); - - readfds_p = &readfds; - } - - if (!list_empty(&writefd_list)) { - struct fd_entry *entry; - - FD_ZERO(&writefds); - list_for_each_entry(entry, &writefd_list, list) - FD_SET(entry->fd, &writefds); - - writefds_p = &writefds; - } - - if (!list_empty(&exceptfd_list)) { - struct fd_entry *entry; - - FD_ZERO(&exceptfds); - list_for_each_entry(entry, &exceptfd_list, list) - FD_SET(entry->fd, &exceptfds); - - exceptfds_p = &exceptfds; - } - if (!list_empty(&timeout_list)) { struct timeout_entry *entry, *tmp; list_for_each_entry_safe(entry, tmp, &timeout_list, list) { @@ -244,13 +218,15 @@ void event_loop(void) } } - int i = select(FD_SETSIZE, readfds_p, writefds_p, exceptfds_p, timeout_p); + log_print(LOG_DEBUG, ""); + + int i = select(FD_SETSIZE, readfds, writefds, exceptfds, timeout_p); if (i < 0) { /* On error, -1 is returned, and errno is set - * appropriately; the sets and timeout become - * undefined, so do not rely on their contents - * after an error. - */ + * appropriately; the sets and timeout become + * undefined, so do not rely on their contents + * after an error. + */ continue; } else if (i == 0 && !list_empty(&timeout_list)) { @@ -266,45 +242,29 @@ void event_loop(void) } else { free(entry); } - } - if (readfds_p) { - struct fd_entry *entry, *tmp; - list_for_each_entry_safe(entry, tmp, &readfd_list, list) { - if (!FD_ISSET(entry->fd, &readfds)) + } else { + list_for_each_entry(entry, &fd_list, list) { + if ((entry->flags & FD_NEW) != 0) { continue; - if (entry->callback(entry->fd, entry->privdata) != 0) { - list_del(&entry->list); - free(entry); + } else if ((entry->flags & FD_READ) != 0) { + if (!FD_ISSET(entry->fd, readfds)) + continue; + + } else if ((entry->flags & FD_WRITE) != 0) { + if (!FD_ISSET(entry->fd, writefds)) + continue; + + } else if ((entry->flags & FD_EXCEPT) != 0) { + if (!FD_ISSET(entry->fd, exceptfds)) + continue; } - } - } - if (writefds_p) { - struct fd_entry *entry, *tmp; - list_for_each_entry_safe(entry, tmp, &writefd_list, list) { - if (FD_ISSET(entry->fd, &writefds)) - continue; - - if (entry->callback(entry->fd, entry->privdata) != 0) { - list_del(&entry->list); - free(entry); - } - } - } - - if (exceptfds_p) { - struct fd_entry *entry, *tmp; - list_for_each_entry_safe(entry, tmp, &exceptfd_list, list) { - if (FD_ISSET(entry->fd, &exceptfds)) - continue; - - if (entry->callback(entry->fd, entry->privdata) != 0) { - list_del(&entry->list); - free(entry); - } + if (entry->callback(entry->fd, entry->privdata) != 0) + entry->flags |= FD_DELETE; } } } + free(fdsets); } diff --git a/event.h b/event.h index df7a396..a20d1f8 100644 --- a/event.h +++ b/event.h @@ -21,6 +21,6 @@ int event_add_timeout(struct timeval *timeout, int (*callback)(void *privdata), int event_remove_fd(int fd); -void event_loop(void); +int event_loop(void); #endif /* _EVENT_H_ */ diff --git a/network.c b/network.c index f82dcc1..9c3823f 100644 --- a/network.c +++ b/network.c @@ -48,7 +48,7 @@ char * get_sockaddr_buf(struct sockaddr_in *sa) return buf; } -int tcp_listen_socket(struct sockaddr_in *sa, int cnt) +int tcp_listen_socket(struct sockaddr_in *sa) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0 ) { @@ -69,10 +69,26 @@ int tcp_listen_socket(struct sockaddr_in *sa, int cnt) return -1; } - if (listen(sock, cnt)) { + if (listen(sock, 8)) { log_print(LOG_ERROR, "tcp_listen_socket(): listen()"); close(sock); return -1; } return sock; } + +int tcp_connect_socket(struct sockaddr_in *sa) +{ + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0 ) { + log_print(LOG_ERROR, "tcp_connect_socket(): socket()"); + return -1; + } + + if (connect(sock, (struct sockaddr *)sa, sizeof(*sa))) { + log_print(LOG_ERROR, "tcp_connect_socket(): bind(%s)", get_sockaddr_buf(sa)); + close(sock); + return -1; + } + return sock; +} diff --git a/network.h b/network.h index 25923ae..3c9b5ea 100644 --- a/network.h +++ b/network.h @@ -4,7 +4,8 @@ #include int parse_saddr(const char *addr, struct sockaddr_in *sa); -int tcp_listen_socket(struct sockaddr_in *sa, int cnt); +int tcp_listen_socket(struct sockaddr_in *sa); +int tcp_connect_socket(struct sockaddr_in *sa); int get_sockaddr(struct sockaddr_in *sa, char *buf, int size); char * get_sockaddr_buf(struct sockaddr_in *sa); diff --git a/telnetproxy.c b/telnetproxy.c index 97cbcef..46a6e1d 100644 --- a/telnetproxy.c +++ b/telnetproxy.c @@ -5,83 +5,9 @@ #include "configfile.h" #include "connection.h" #include "event.h" -#include "list.h" #include "logging.h" #include "network.h" -static LIST_HEAD(connection_list); - -static int server_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; - } - - write(con->src_fd, buf, len); - return 0; -} - -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 (con->dst_fd != -1) { - write(con->dst_fd, buf, len); - return 0; - } - - // check destination - int sock = tcp_connect_socket(&con->dst_addr); - if (sock < 0) { - list_del(&con->list); - destroy_connection(con); - return -1; - } - - event_add_readfd(sock, server_handler, con); - log_print(LOG_INFO, "forwarding to %s", get_sockaddr_buf(&con->dst_addr)); - return 0; -} - -static int listen_handler(int fd, void *privdata) -{ - struct connection *con = malloc(sizeof(struct connection)); - if (con == NULL) { - log_print(LOG_WARN, "listen_handler(): out of memory"); - return 0; - } - - int i = sizeof(con->src_addr); - con->src_fd = accept(fd, (struct sockaddr *)&con->src_addr, &i); - if (con->src_fd < 0) { - free(con); - return 0; - } - - con->dst_fd = -1; - con->login_time = con->idle_time = time(NULL); - - log_print(LOG_INFO, "accepted connection from %s", get_sockaddr_buf(&con->src_addr)); - - list_add_tail(&con->list, &connection_list); - event_add_readfd(con->src_fd, client_handler, con); - return 0; -} - static int listen_init(const char *value, void *privdata) { struct sockaddr_in addr; @@ -91,11 +17,11 @@ static int listen_init(const char *value, void *privdata) return -1; } - int sock = tcp_listen_socket(&addr, 10); + int sock = tcp_listen_socket(&addr); if (sock < 0) return -1; - event_add_readfd(sock, listen_handler, NULL); + event_add_readfd(sock, listen_handler, privdata); log_print(LOG_INFO, "listen on %s", get_sockaddr_buf(&addr)); return 0; } @@ -105,12 +31,18 @@ int main(int argc, char *argv[]) if (config_parse("telnetproxy.conf") == -1) return -1; - int cnt = config_get_strings("global", "listen", listen_init, NULL); + int cnt = config_get_strings("global", "listen", listen_init, (void*)CON_NORMAL); if (cnt <= 0) { log_print(LOG_ERROR, "no listen sockets defined!"); return -1; } + cnt = config_get_strings("global", "admin_listen", listen_init, (void*)CON_ADMIN); + if (cnt <= 0) { + log_print(LOG_ERROR, "no admin_listen sockets defined!"); + return -1; + } + event_loop(); return 0; diff --git a/telnetproxy.conf b/telnetproxy.conf index 647179e..eaf3c2c 100644 --- a/telnetproxy.conf +++ b/telnetproxy.conf @@ -1,2 +1,3 @@ [global] listen 0.0.0.0:8000 +admin_listen 0.0.0.0:8001