diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cfg_parser.rl | 15 | ||||
-rw-r--r-- | src/listener.c | 117 | ||||
-rw-r--r-- | src/listener.h | 4 | ||||
-rw-r--r-- | src/options.c | 2 | ||||
-rw-r--r-- | src/sig_handler.c | 2 | ||||
-rw-r--r-- | src/tcpproxy.c | 22 |
6 files changed, 116 insertions, 46 deletions
diff --git a/src/cfg_parser.rl b/src/cfg_parser.rl index c215a83..47d237c 100644 --- a/src/cfg_parser.rl +++ b/src/cfg_parser.rl @@ -163,17 +163,18 @@ int parse_listener(char* p, char* pe, listeners_t* listener) char* eof = pe; %% write exec; - if(cs == cfg_parser_error) { + if(cs == cfg_parser_error) { log_printf(ERROR, "config file syntax error at line %d", cur_line); + listeners_revert(listener); ret = 1; - } - - // we only have one file so if we aren't there something is wrong - if(cs != cfg_parser_first_final) { + } else if(cs != cfg_parser_first_final) { + // we only have one file so if we aren't there something is wrong log_printf(ERROR, "config file syntax error: unexpected end of file"); + listeners_revert(listener); ret = 1; - } - + } else + ret = listeners_update(listener); + clear_listener_struct(&lst); return ret; diff --git a/src/listener.c b/src/listener.c index 80876c5..69833b7 100644 --- a/src/listener.c +++ b/src/listener.c @@ -1,14 +1,14 @@ /* * tcpproxy * - * tcpproxy is a simple tcp connection proxy which combines the - * features of rinetd and 6tunnel. tcpproxy supports IPv4 and - * IPv6 and also supports connections from IPv6 to IPv4 + * tcpproxy is a simple tcp connection proxy which combines the + * features of rinetd and 6tunnel. tcpproxy supports IPv4 and + * IPv6 and also supports connections from IPv6 to IPv4 * endpoints and vice versa. - * + * * * Copyright (C) 2010-2011 Christian Pointner <equinox@spreadspace.org> - * + * * This file is part of tcpproxy. * * tcpproxy is free software: you can redistribute it and/or modify @@ -49,7 +49,7 @@ void listeners_delete_element(void* e) { if(!e) return; - + listener_t* element = (listener_t*)e; if(element->fd_ >= 0) close(element->fd_); @@ -76,7 +76,7 @@ int listeners_add(listeners_t* list, const char* laddr, resolv_type_t lrt, const if(!raddr) { log_printf(ERROR, "no remote address specified"); return -1; } if(!rport) { log_printf(ERROR, "no remote port specified"); return -1; } -// TODO: what if more than one address is returned here? +// TODO: what if more than one address is returned here? struct addrinfo* re = tcp_resolve_endpoint(raddr, rport, rrt, 0); if(!re) return -1; @@ -86,7 +86,7 @@ int listeners_add(listeners_t* list, const char* laddr, resolv_type_t lrt, const se = tcp_resolve_endpoint(saddr, NULL, rrt, 0); if(!se) { freeaddrinfo(re); - return -1; + return -1; } } @@ -140,13 +140,16 @@ int listeners_add(listeners_t* list, const char* laddr, resolv_type_t lrt, const static int activate_listener(listener_t* l) { + if(!l || l->state_ != NEW) + return -1; + l->fd_ = socket(l->local_end_.addr_.ss_family, SOCK_STREAM, 0); if(l->fd_ < 0) { log_printf(ERROR, "Error on opening tcp socket: %s", strerror(errno)); l->state_ = ZOMBIE; return -1; } - + int on = 1; int ret = setsockopt(l->fd_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if(ret) { @@ -158,23 +161,23 @@ static int activate_listener(listener_t* l) if(setsockopt(l->fd_, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) log_printf(WARNING, "failed to set IPV6_V6ONLY socket option: %s", strerror(errno)); } - + ret = bind(l->fd_, (struct sockaddr *)&(l->local_end_.addr_), l->local_end_.len_); if(ret) { log_printf(ERROR, "Error on bind(): %s", strerror(errno)); l->state_ = ZOMBIE; return -1; } - + ret = listen(l->fd_, 0); if(ret) { log_printf(ERROR, "Error on listen(): %s", strerror(errno)); l->state_ = ZOMBIE; return -1; } - + l->state_ = ACTIVE; - + char* ls = tcp_endpoint_to_string(l->local_end_); char* rs = tcp_endpoint_to_string(l->remote_end_); char* ss = tcp_endpoint_to_string(l->source_end_); @@ -186,38 +189,102 @@ static int activate_listener(listener_t* l) return 0; } -int listeners_activate(listeners_t* list) +static void update_listener(listener_t* dest, listener_t* src) +{ + if(!dest || !src || src->state_ != ZOMBIE || dest->state_ != NEW) + return; + + dest->fd_ = src->fd_; + src->fd_ = -1; + dest->state_ = ACTIVE; + + char* ls = tcp_endpoint_to_string(dest->local_end_); + char* rs = tcp_endpoint_to_string(dest->remote_end_); + char* ss = tcp_endpoint_to_string(dest->source_end_); + log_printf(NOTICE, "reusing %s with remote: %s%s%s", ls ? ls:"(null)", rs ? rs:"(null)", ss ? " and source " : "", ss ? ss : ""); + if(ls) free(ls); + if(rs) free(rs); + if(ss) free(ss); +} + +static listener_t* find_zombie_listener(listeners_t* list, tcp_endpoint_t* local_end) +{ + if(!list) + return NULL; + + slist_element_t* tmp = list->first_; + while(tmp) { + listener_t* l = (listener_t*)tmp->data_; + if(l && l->state_ == ZOMBIE && l->local_end_.len_ == local_end->len_ && + !memcmp(&(l->local_end_.addr_), &(local_end->addr_), local_end->len_)) + return l; + tmp = tmp->next_; + } + + return NULL; +} + +int listeners_update(listeners_t* list) { if(!list) return; - - int ret = 0; + slist_element_t* tmp = list->first_; while(tmp) { listener_t* l = (listener_t*)tmp->data_; - if(l && l->state_ == NEW) - ret = activate_listener(l); + if(l && l->state_ == ACTIVE) + l->state_ = ZOMBIE; + tmp = tmp->next_; + } - if(ret) - break; + int retval = 0; + tmp = list->first_; + while(tmp) { + listener_t* l = (listener_t*)tmp->data_; + int ret = 0; + if(l && l->state_ == NEW) { + listener_t* tmp = find_zombie_listener(list, &(l->local_end_)); + if(tmp) + update_listener(l, tmp); + else + ret = activate_listener(l); + } + if(!retval) retval = ret; tmp = tmp->next_; } - return ret; + int cnt = 0; + tmp = list->first_; + while(tmp) { + listener_t* l = (listener_t*)tmp->data_; + tmp = tmp->next_; + if(l && l->state_ == ZOMBIE) { + cnt++; + slist_remove(list, l); + } + } + log_printf(DEBUG, "%d listener zombies removed", cnt); + + return retval; } -void listeners_cleanup(listeners_t* list) +void listeners_revert(listeners_t* list) { if(!list) return; + int cnt = 0; slist_element_t* tmp = list->first_; while(tmp) { listener_t* l = (listener_t*)tmp->data_; tmp = tmp->next_; - if(l && l->state_ == ZOMBIE) + if(l && l->state_ == NEW) { + cnt++; slist_remove(list, l); + } } + + log_printf(DEBUG, "%d new listeners reverted", cnt); } void listeners_remove(listeners_t* list, int fd) @@ -245,7 +312,7 @@ void listeners_print(listeners_t* list) { if(!list) return; - + slist_element_t* tmp = list->first_; while(tmp) { listener_t* l = (listener_t*)tmp->data_; @@ -309,6 +376,6 @@ int listeners_handle_accept(listeners_t* list, clients_t* clients, fd_set* set) } tmp = tmp->next_; } - + return 0; } diff --git a/src/listener.h b/src/listener.h index 7638254..6b95498 100644 --- a/src/listener.h +++ b/src/listener.h @@ -52,8 +52,8 @@ typedef slist_t listeners_t; int listeners_init(listeners_t* list); void listeners_clear(listeners_t* list); int listeners_add(listeners_t* list, const char* laddr, resolv_type_t lrt, const char* lport, const char* raddr, resolv_type_t rrt, const char* rport, const char* saddr); -int listeners_activate(listeners_t* list); -void listeners_cleanup(listeners_t* list); +int listeners_update(listeners_t* list); +void listeners_revert(listeners_t* list); void listeners_remove(listeners_t* list, int fd); listener_t* listeners_find(listeners_t* list, int fd); void listeners_print(listeners_t* list); diff --git a/src/options.c b/src/options.c index b905426..116cb52 100644 --- a/src/options.c +++ b/src/options.c @@ -234,6 +234,8 @@ void options_parse_post(options_t* opt) if(opt->config_file_ && opt->local_port_) { log_printf(WARNING, "local port and config file specified, will ignore config file"); + free(opt->config_file_); + opt->config_file_ = NULL; } if(opt->buffer_size_ <= 0) { diff --git a/src/sig_handler.c b/src/sig_handler.c index 6e6a04a..7c3d45b 100644 --- a/src/sig_handler.c +++ b/src/sig_handler.c @@ -119,7 +119,7 @@ int signal_handle() case SIGINT: log_printf(NOTICE, "SIG-Int caught, exitting"); return_value = 1; break; case SIGQUIT: log_printf(NOTICE, "SIG-Quit caught, exitting"); return_value = 1; break; case SIGTERM: log_printf(NOTICE, "SIG-Term caught, exitting"); return_value = 1; break; - case SIGHUP: log_printf(NOTICE, "SIG-Hup caught"); break; + case SIGHUP: log_printf(NOTICE, "SIG-Hup caught"); return_value = 2; break; case SIGUSR1: log_printf(NOTICE, "SIG-Usr1 caught"); break; case SIGUSR2: log_printf(NOTICE, "SIG-Usr2 caught"); break; default: log_printf(WARNING, "unknown signal %d caught, ignoring", sig); break; diff --git a/src/tcpproxy.c b/src/tcpproxy.c index fcc871f..1007b80 100644 --- a/src/tcpproxy.c +++ b/src/tcpproxy.c @@ -71,9 +71,16 @@ int main_loop(options_t* opt, listeners_t* listeners) continue; if(FD_ISSET(sig_fd, &readfds)) { - if(signal_handle()) { - return_value = 1; - break; + return_value = signal_handle(); + if(return_value == 1) break; + if(return_value == 2) { + if(opt->config_file_) { + log_printf(NOTICE, "re-reading config file: %s", opt->config_file_); + read_configfile(opt->config_file_, listeners); + } else + log_printf(NOTICE, "ignoring SIGHUP: no config file specified"); + + return_value = 0; } } @@ -149,6 +156,7 @@ int main(int argc, char* argv[]) if(opt.local_port_) { ret = listeners_add(&listeners, opt.local_addr_, opt.lresolv_type_, opt.local_port_, opt.remote_addr_, opt.rresolv_type_, opt.remote_port_, opt.source_addr_); + if(!ret) ret = listeners_update(&listeners); if(ret) { listeners_clear(&listeners); options_clear(&opt); @@ -167,14 +175,6 @@ int main(int argc, char* argv[]) } } - ret = listeners_activate(&listeners); - if(ret) { - listeners_clear(&listeners); - options_clear(&opt); - log_close(); - exit(-1); - } - priv_info_t priv; if(opt.username_) if(priv_init(&priv, opt.username_, opt.groupname_)) { |