diff --git a/connection.c b/connection.c index 3f656b7..8399ac9 100644 --- a/connection.c +++ b/connection.c @@ -18,8 +18,9 @@ struct connection { struct sockaddr_in src_addr; struct sockaddr_in dst_addr; - int src_fd; - int dst_fd; + struct event_entry *src_event; + struct event_entry *dst_event; + struct event_entry *timeout; time_t login_time; }; @@ -34,26 +35,26 @@ static struct connection * create_connection() return 0; } - con->src_fd = con->dst_fd = -1; - con->src_addr.sin_addr.s_addr = con->src_addr.sin_addr.s_addr = 0; - con->src_addr.sin_port = con->src_addr.sin_port = 0; + memset(con, 0, sizeof(struct connection)); con->login_time = time(NULL); - return con; } static int destroy_connection(struct connection *con) { - // TODO: detroy connect-timeout? + if (con->timeout != NULL) + event_remove(con->timeout); - if (con->src_fd != -1) { - close(con->src_fd); - event_remove_fd(con->src_fd); + int src_fd = event_get_fd(con->src_event); + if (src_fd != -1) { + close(src_fd); + event_remove(con->src_event); } - if (con->dst_fd != -1) { - close(con->dst_fd); - event_remove_fd(con->dst_fd); + int dst_fd = event_get_fd(con->dst_event); + if (dst_fd != -1) { + close(dst_fd); + event_remove(con->dst_event); } free(con); @@ -72,13 +73,16 @@ static int forward_handler(int fd, void *privdata) return -1; } + int src_fd = event_get_fd(con->src_event); + int dst_fd = event_get_fd(con->dst_event); + /* client -> device */ - if (con->src_fd == fd) - write(con->dst_fd, buf, len); + if (src_fd == fd && dst_fd != -1) + write(dst_fd, buf, len); /* device -> client */ - else if (con->dst_fd == fd) - write(con->src_fd, buf, len); + if (dst_fd == fd && src_fd != -1) + write(src_fd, buf, len); return 0; } @@ -94,10 +98,32 @@ static int connect_handler(int fd, void *privdata) } 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 connect_handler */ + /* 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(con->timeout); + con->timeout = NULL; + + log_print(LOG_DEBUG, "connect_handler(): exit"); + + 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; } @@ -119,21 +145,29 @@ static int client_handler(int fd, void *privdata) return -1; } - // check destination - con->dst_fd = tcp_connect_socket(&con->dst_addr); - if (con->dst_fd < 0) { + // 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; } - // TODO: start connect timeout, callback destroys connection - log_print(LOG_INFO, "connecting to %s", get_sockaddr_buf(&con->dst_addr)); - event_add_writefd(con->dst_fd, connect_handler, con); - /* dismiss client_handler */ - return -1; + struct timeval tv; + tv.tv_sec = 5; + 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) @@ -151,9 +185,9 @@ static int admin_handler(int fd, void *privdata) if (strncmp(buf, "list", 4) == 0) { struct connection *entry; list_for_each_entry(entry, &connection_list, list) { - len = snprintf(buf, sizeof(buf), "%d,", entry->src_fd); + 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,", entry->dst_fd); + 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); @@ -182,8 +216,8 @@ int listen_handler(int fd, void *privdata) } unsigned int i = sizeof(con->src_addr); - con->src_fd = accept(fd, (struct sockaddr *)&con->src_addr, &i); - if (con->src_fd < 0) { + int src_fd = accept(fd, (struct sockaddr *)&con->src_addr, &i); + if (src_fd < 0) { free(con); return 0; } @@ -193,9 +227,10 @@ int listen_handler(int fd, void *privdata) list_add_tail(&con->list, &connection_list); if (mode == CON_ADMIN) - event_add_readfd(con->src_fd, admin_handler, con); - else - event_add_readfd(con->src_fd, client_handler, con); + 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; } diff --git a/event.c b/event.c index 8b00990..fa2c48e 100644 --- a/event.c +++ b/event.c @@ -29,58 +29,103 @@ #include "event.h" -#define FD_TYPE (FD_READ | FD_WRITE | FD_EXCEPT) -#define FD_NEW 0x10 -#define FD_DELETE 0x20 +static LIST_HEAD(event_fd_list); +static LIST_HEAD(event_timeout_list); -static LIST_HEAD(fd_list); -static LIST_HEAD(timeout_list); - -struct fd_entry { +struct event_entry { struct list_head list; - int fd; - int flags; - int (*callback)(int fd, void *privdata); - void *privdata; + unsigned int flags; + + union { + struct { + int fd; + int (*read_cb)(int fd, void *privdata); + int (*write_cb)(int fd, void *privdata); + void *read_priv; + void *write_priv; + }; + struct { + struct timeval intervall; + struct timeval nextrun; + int (*callback)(void *privdata); + void *privdata; + }; + }; }; -struct timeout_entry { - struct list_head list; - struct timeval intervall; - struct timeval nextrun; - int (*callback)(void *privdata); - void *privdata; -}; - -int event_add_fd(int fd, int type, int (*callback)(int fd, void *privdata), void *privdata) +struct event_entry * event_add_fd( + struct event_entry *entry, + int fd, + int type, + int (*callback)(int fd, void *privdata), + void *privdata) { - if (fd < 0 || fd > FD_SETSIZE || type == 0) - return -1; - - struct fd_entry *entry; - entry = malloc(sizeof(struct fd_entry)); - if (entry == NULL) { - log_print(LOG_ERROR, "event_add_fd(): out of memory"); - return -1; + /* check valid filediskriptor */ + if (fd < 0 || fd > FD_SETSIZE) { + log_print(LOG_ERROR, "event_add_fd(): invalid fd"); + return NULL; } - entry->fd = fd; - entry->flags = (type & FD_TYPE) | FD_NEW; - entry->callback = callback; - entry->privdata = privdata; + /* check valid type (read/write) */ + if (!(type & FD_TYPES)) { + log_print(LOG_ERROR, "event_add_fd(): invalid type"); + return NULL; + } - list_add_tail(&entry->list, &fd_list); - return 0; + /* create new entry */ + if (entry == NULL) { + entry = malloc(sizeof(struct event_entry)); + if (entry == NULL) { + log_print(LOG_ERROR, "event_add_fd(): out of memory"); + return NULL; + } + + entry->flags = EVENT_FD_CB; + entry->fd = fd; + + /* put it on the list */ + list_add_tail(&entry->list, &event_fd_list); + } + + if (type & FD_READ) { + entry->flags = (callback != NULL) ? (entry->flags | FD_READ) : (entry->flags & ~FD_READ); + entry->read_cb = callback; + entry->read_priv = privdata; + + } else if (type & FD_WRITE) { + entry->flags = (callback != NULL) ? (entry->flags | FD_WRITE) : (entry->flags & ~FD_WRITE); + entry->write_cb = callback; + entry->write_priv = privdata; + } + + entry->flags |= EVENT_NEW; + return entry; } -int event_remove_fd(int fd) +int event_get_fd(struct event_entry *entry) { - struct fd_entry *entry, *tmp; - list_for_each_entry_safe(entry, tmp, &fd_list, list) { - if (entry->fd == fd) - entry->flags |= FD_DELETE; + if (entry == NULL || !(entry->flags & EVENT_FD_CB)) + return -1; + + return entry->fd; +} + +void event_remove_fd(int fd) +{ + struct event_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &event_fd_list, list) { + if (entry->fd == fd) { + entry->flags |= EVENT_DELETE; + return; + } } - return 0; + return; +} + +void event_remove(struct event_entry *entry) +{ + /* mark the event as deleted -> remove in select() loop */ + entry->flags |= EVENT_DELETE; } static void add_timeval(struct timeval *ret, struct timeval *a, struct timeval *b) @@ -105,12 +150,29 @@ static void sub_timeval(struct timeval *ret, struct timeval *a, struct timeval * } } -static void schedule_nextrun(struct timeout_entry *entry, struct timeval *now) +static int cmp_timeval(struct timeval *a, struct timeval *b) +{ + if (a->tv_sec > b->tv_sec) + return -1; + + if (a->tv_sec < b->tv_sec) + return 1; + + if (a->tv_usec > b->tv_usec) + return -1; + + if (a->tv_usec < b->tv_usec) + return 1; + + return 0; +} + +static void schedule_nextrun(struct event_entry *entry, struct timeval *now) { add_timeval(&entry->nextrun, now, &entry->intervall); - struct timeout_entry *search; - list_for_each_entry(search, &timeout_list, list) { + struct event_entry *search; + list_for_each_entry(search, &event_timeout_list, list) { if (search->nextrun.tv_sec > entry->nextrun.tv_sec) { list_add_tail(&entry->list, &search->list); return; @@ -121,106 +183,108 @@ static void schedule_nextrun(struct timeout_entry *entry, struct timeval *now) return; } } - list_add_tail(&entry->list, &timeout_list); + list_add_tail(&entry->list, &event_timeout_list); } -int event_add_timeout(struct timeval *timeout, int (*callback)(void *privdata), void *privdata) +struct event_entry * event_add_timeout( + struct timeval *timeout, + int (*callback)(void *privdata), + void *privdata) { - struct timeout_entry *entry; - entry = malloc(sizeof(struct timeout_entry)); + struct event_entry *entry; + entry = malloc(sizeof(struct event_entry)); if (entry == NULL) { log_print(LOG_ERROR, "event_add_timeout(): out of memory"); - return -1; + return NULL; } + entry->flags = EVENT_TIMEOUT; memcpy(&entry->intervall, timeout, sizeof(entry->intervall)); entry->callback = callback; entry->privdata = privdata; struct timeval now; gettimeofday(&now, NULL); + schedule_nextrun(entry, &now); - return 0; + return entry; } -int event_remove_timeout(int id) -{ - -} int event_loop(void) { - fd_set *fdsets = malloc(sizeof(fd_set) * 3); + fd_set *fdsets = malloc(sizeof(fd_set) * 2); if (fdsets == NULL) { log_print(LOG_ERROR, "event_loop(): out of memory"); return -1; } while (1) { - 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; + fd_set *readfds = NULL, *writefds = NULL; + struct event_entry *entry, *tmp; - if (entry->flags & FD_DELETE) { + list_for_each_entry_safe(entry, tmp, &event_fd_list, list) { + entry->flags &= ~EVENT_NEW; + + if (entry->flags & EVENT_DELETE) { list_del(&entry->list); free(entry); - } else if ((entry->flags & FD_READ) != 0) { + } else if (entry->flags & FD_READ) { if (readfds == NULL) { readfds = &fdsets[0]; FD_ZERO(readfds); } FD_SET(entry->fd, readfds); - } else if ((entry->flags & FD_WRITE) != 0) { + } else if (entry->flags & FD_WRITE) { if (writefds == NULL) { writefds = &fdsets[1]; FD_ZERO(writefds); } FD_SET(entry->fd, writefds); - - } else if ((entry->flags & FD_EXCEPT) != 0) { - if (exceptfds == NULL) { - exceptfds = &fdsets[2]; - FD_ZERO(exceptfds); - } - FD_SET(entry->fd, exceptfds); } } struct timeval timeout, *timeout_p = NULL; - if (!list_empty(&timeout_list)) { + if (!list_empty(&event_timeout_list)) { struct timeval now; gettimeofday(&now, NULL); - struct timeout_entry *entry, *tmp; - list_for_each_entry_safe(entry, tmp, &timeout_list, list) { - /* timeout elapsed */ - if (entry->nextrun.tv_sec < now.tv_sec || - (entry->nextrun.tv_sec == now.tv_sec && entry->nextrun.tv_usec < now.tv_usec)) { - + struct event_entry *entry, *tmp; + list_for_each_entry_safe(entry, tmp, &event_timeout_list, list) { + if (entry->flags & EVENT_DELETE) { list_del(&entry->list); - if (entry->callback(entry->privdata)) { - free(entry); + free(entry); + continue; + } - } else { - schedule_nextrun(entry, &now); - } + /* timeout not elapsed, exit search (since list is sorted) */ + if (cmp_timeval(&entry->nextrun, &now) == -1) + break; + + /* remove event from list */ + list_del(&entry->list); + + /* execute callback, when callback returns 0 -> schedule event again */ + if (entry->callback(entry->privdata)) { + free(entry); } else { - break; + schedule_nextrun(entry, &now); } } - entry = list_entry(timeout_list.next, typeof(*entry), list); + if (!list_empty(&event_timeout_list)) { + entry = list_entry(event_timeout_list.next, typeof(*entry), list); - /* calc select() timeout */ - sub_timeval(&timeout, &entry->nextrun, &now); - timeout_p = &timeout; + /* calc select() timeout */ + sub_timeval(&timeout, &entry->nextrun, &now); + timeout_p = &timeout; + } } - int i = select(FD_SETSIZE, readfds, writefds, exceptfds, timeout_p); + int i = select(FD_SETSIZE, readfds, writefds, NULL, timeout_p); if (i <= 0) { /* On error, -1 is returned, and errno is set * appropriately; the sets and timeout become @@ -230,26 +294,19 @@ int event_loop(void) continue; } else { - list_for_each_entry(entry, &fd_list, list) { - if ((entry->flags & FD_NEW) != 0) { + list_for_each_entry(entry, &event_fd_list, list) { + if ((entry->flags & EVENT_NEW) != 0) { /* entry has just been added, execute it next round */ continue; - - } 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 (entry->callback(entry->fd, entry->privdata) != 0) - entry->flags |= FD_DELETE; + if ((entry->flags & FD_READ) && FD_ISSET(entry->fd, readfds)) + if (entry->read_cb(entry->fd, entry->read_priv) != 0) + entry->flags |= EVENT_DELETE; + + if ((entry->flags & FD_WRITE) && FD_ISSET(entry->fd, writefds)) + if (entry->write_cb(entry->fd, entry->write_priv) != 0) + entry->flags |= EVENT_DELETE; } } } diff --git a/event.h b/event.h index a20d1f8..0bba388 100644 --- a/event.h +++ b/event.h @@ -3,23 +3,38 @@ #include +#define EVENT_FD_CB 0x10000000 +#define EVENT_TIMEOUT 0x20000000 + +#define EVENT_NEW 0x01000000 +#define EVENT_DELETE 0x02000000 + #define FD_READ 0x01 #define FD_WRITE 0x02 -#define FD_EXCEPT 0x04 +#define FD_TYPES (FD_READ | FD_WRITE) -#define event_add_readfd(fd, callback, privdata) \ - event_add_fd(fd, FD_READ, callback, privdata) +#define event_add_readfd(entry, fd, callback, privdata) \ + event_add_fd(entry, fd, FD_READ, callback, privdata) -#define event_add_writefd(fd, callback, privdata) \ - event_add_fd(fd, FD_WRITE, callback, privdata) +#define event_add_writefd(entry, fd, callback, privdata) \ + event_add_fd(entry, fd, FD_WRITE, callback, privdata) -#define event_add_exceptfd(fd, callback, privdata) \ - event_add_fd(fd, FD_EXCEPT, callback, privdata) +struct event_entry * event_add_fd( + struct event_entry *entry, + int fd, + int type, + int (*callback)(int fd, void *privdata), + void *privdata); -int event_add_fd(int fd, int type, int (*callback)(int fd, void *privdata), void *privdata); -int event_add_timeout(struct timeval *timeout, int (*callback)(void *privdata), void *privdata); +struct event_entry * event_add_timeout( + struct timeval *timeout, + int (*callback)(void *privdata), + void *privdata); -int event_remove_fd(int fd); +int event_get_fd(struct event_entry *entry); + +void event_remove_fd(int fd); +void event_remove(struct event_entry *entry); int event_loop(void); diff --git a/network.c b/network.c index 96b569e..b5d3165 100644 --- a/network.c +++ b/network.c @@ -94,7 +94,7 @@ int tcp_connect_socket(struct sockaddr_in *sa) return -1; } - /* non-blocking */ + /* non-blocking connect() */ if (fcntl(sock, F_SETFL, flags | O_NONBLOCK)) { log_print(LOG_ERROR, "tcp_connect_socket(): fcntl(F_SETFL)"); close(sock); @@ -111,7 +111,7 @@ int tcp_connect_socket(struct sockaddr_in *sa) /* reset EINPROGRESS */ errno = 0; - /* blocking */ + /* all further actions are blocking */ if (fcntl(sock, F_SETFL, flags)) { log_print(LOG_ERROR, "tcp_connect_socket(): fcntl(F_SETFL)"); close(sock); diff --git a/telnetproxy.c b/telnetproxy.c index b61cf53..2a1d546 100644 --- a/telnetproxy.c +++ b/telnetproxy.c @@ -24,20 +24,13 @@ static int listen_init(const char *value, void *privdata) if (sock < 0) return -1; - event_add_readfd(sock, listen_handler, privdata); + event_add_readfd(NULL, sock, listen_handler, privdata); log_print(LOG_INFO, "listen on %s", get_sockaddr_buf(&addr)); return 0; } -static int trigger(void *privdata) -{ - log_print(LOG_DEBUG, "%s", (char *)privdata); - return 0; -} - int main(int argc, char *argv[]) { -/* if (config_parse("telnetproxy.conf") == -1) return -1; @@ -52,16 +45,6 @@ int main(int argc, char *argv[]) log_print(LOG_ERROR, "no admin_listen sockets defined!"); return -1; } -*/ - struct timeval tv; - tv.tv_usec = 0; - - tv.tv_sec = 1; - event_add_timeout(&tv, trigger, "trigger 1"); - tv.tv_sec = 2; - event_add_timeout(&tv, trigger, "trigger 2"); - tv.tv_sec = 3; - event_add_timeout(&tv, trigger, "trigger 3"); event_loop();