1 /*
2 * tcpproxy
3 *
4 * tcpproxy is a simple tcp connection proxy which combines the
5 * features of rinetd and 6tunnel. tcpproxy supports IPv4 and
6 * IPv6 and also supports connections from IPv6 to IPv4
7 * endpoints and vice versa.
8 *
9 *
10 * Copyright (C) 2010-2015 Christian Pointner <equinox@spreadspace.org>
11 *
12 * This file is part of tcpproxy.
13 *
14 * tcpproxy is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * any later version.
18 *
19 * tcpproxy is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with tcpproxy. If not, see <http://www.gnu.org/licenses/>.
26 */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/select.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <signal.h>
36
37 #include "datatypes.h"
38 #include "options.h"
39 #include "string_list.h"
40 #include "sig_handler.h"
41 #include "log.h"
42 #include "daemon.h"
43
44 #include "listener.h"
45 #include "clients.h"
46 #include "cfg_parser.h"
47
main_loop(options_t * opt,listeners_t * listeners)48 int main_loop(options_t* opt, listeners_t* listeners)
49 {
50 log_printf(INFO, "entering main loop");
51
52 int sig_fd = signal_init();
53 if(sig_fd < 0)
54 return -1;
55
56 clients_t clients;
57 int return_value = clients_init(&clients, opt->buffer_size_);
58
59 while(!return_value) {
60 fd_set readfds, writefds;
61 FD_ZERO(&readfds);
62 FD_ZERO(&writefds);
63 FD_SET(sig_fd, &readfds);
64 int nfds = sig_fd;
65 listeners_read_fds(listeners, &readfds, &nfds);
66 clients_read_fds(&clients, &readfds, &nfds);
67 clients_write_fds(&clients, &writefds, &nfds);
68 int ret = select(nfds + 1, &readfds, &writefds, NULL, NULL);
69 if(ret == -1 && errno != EINTR) {
70 log_printf(ERROR, "select returned with error: %s", strerror(errno));
71 return_value = -1;
72 break;
73 }
74 if(!ret || ret == -1)
75 continue;
76
77 if(FD_ISSET(sig_fd, &readfds)) {
78 return_value = signal_handle();
79 if(return_value == SIGINT || return_value == SIGQUIT || return_value == SIGTERM) break;
80 if(return_value == SIGHUP) {
81 if(opt->config_file_) {
82 log_printf(NOTICE, "re-reading config file: %s", opt->config_file_);
83 read_configfile(opt->config_file_, listeners);
84 } else
85 log_printf(NOTICE, "ignoring SIGHUP: no config file specified");
86
87 return_value = 0;
88 } else if(return_value == SIGUSR1) {
89 listeners_print(listeners);
90 } else if(return_value == SIGUSR2) {
91 clients_print(&clients);
92 }
93 }
94
95 return_value = listeners_handle_accept(listeners, &clients, &readfds);
96 if(return_value) break;
97
98 return_value = clients_write(&clients, &writefds);
99 if(return_value) break;
100
101 return_value = clients_read(&clients, &readfds);
102 }
103
104 clients_clear(&clients);
105 signal_stop();
106 return return_value;
107 }
108
main(int argc,char * argv[])109 int main(int argc, char* argv[])
110 {
111 log_init();
112
113 options_t opt;
114 int ret = options_parse(&opt, argc, argv);
115 if(ret) {
116 if(ret > 0) {
117 fprintf(stderr, "syntax error near: %s\n\n", argv[ret]);
118 }
119 if(ret == -2) {
120 fprintf(stderr, "memory error on options_parse, exitting\n");
121 }
122 if(ret == -3) {
123 options_print_version();
124 }
125
126 if(ret != -2 && ret != -3)
127 options_print_usage();
128
129 if(ret == -1 || ret == -3)
130 ret = 0;
131
132 options_clear(&opt);
133 log_close();
134 exit(ret);
135 }
136 slist_element_t* tmp = opt.log_targets_.first_;
137 while(tmp) {
138 ret = log_add_target(tmp->data_);
139 if(ret) {
140 switch(ret) {
141 case -2: fprintf(stderr, "memory error on log_add_target, exitting\n"); break;
142 case -3: fprintf(stderr, "unknown log target: '%s', exitting\n", (char*)(tmp->data_)); break;
143 case -4: fprintf(stderr, "this log target is only allowed once: '%s', exitting\n", (char*)(tmp->data_)); break;
144 default: fprintf(stderr, "syntax error near: '%s', exitting\n", (char*)(tmp->data_)); break;
145 }
146
147 options_clear(&opt);
148 log_close();
149 exit(ret);
150 }
151 tmp = tmp->next_;
152 }
153
154 log_printf(NOTICE, "just started...");
155 options_parse_post(&opt);
156
157 listeners_t listeners;
158 ret = listeners_init(&listeners);
159 if(ret) {
160 options_clear(&opt);
161 log_close();
162 exit(-1);
163 }
164
165 if(opt.local_port_) {
166 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_);
167 if(!ret) ret = listeners_update(&listeners);
168 if(ret) {
169 listeners_clear(&listeners);
170 options_clear(&opt);
171 log_close();
172 exit(-1);
173 }
174 } else {
175 ret = read_configfile(opt.config_file_, &listeners);
176 if(ret || !slist_length(&listeners)) {
177 if(!ret)
178 log_printf(ERROR, "no listeners defined in config file %s", opt.config_file_);
179 listeners_clear(&listeners);
180 options_clear(&opt);
181 log_close();
182 exit(-1);
183 }
184 }
185
186 priv_info_t priv;
187 if(opt.username_)
188 if(priv_init(&priv, opt.username_, opt.groupname_)) {
189 listeners_clear(&listeners);
190 options_clear(&opt);
191 log_close();
192 exit(-1);
193 }
194
195 FILE* pid_file = NULL;
196 if(opt.pid_file_) {
197 pid_file = fopen(opt.pid_file_, "w");
198 if(!pid_file) {
199 log_printf(WARNING, "unable to open pid file: %s", strerror(errno));
200 }
201 }
202
203 if(opt.chroot_dir_)
204 if(do_chroot(opt.chroot_dir_)) {
205 listeners_clear(&listeners);
206 options_clear(&opt);
207 log_close();
208 exit(-1);
209 }
210 if(opt.username_)
211 if(priv_drop(&priv)) {
212 listeners_clear(&listeners);
213 options_clear(&opt);
214 log_close();
215 exit(-1);
216 }
217
218 if(opt.daemonize_) {
219 pid_t oldpid = getpid();
220 daemonize();
221 log_printf(INFO, "running in background now (old pid: %d)", oldpid);
222 }
223
224 if(pid_file) {
225 pid_t pid = getpid();
226 fprintf(pid_file, "%d", pid);
227 fclose(pid_file);
228 }
229
230 ret = main_loop(&opt, &listeners);
231
232 listeners_clear(&listeners);
233 options_clear(&opt);
234
235 if(!ret)
236 log_printf(NOTICE, "normal shutdown");
237 else if(ret < 0)
238 log_printf(NOTICE, "shutdown after error");
239 else {
240 log_printf(NOTICE, "shutdown after signal");
241 log_close();
242 kill(getpid(), ret);
243 }
244
245 log_close();
246
247 return ret;
248 }
249