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 "datatypes.h"
29 #include "config.h"
30
31 #include "options.h"
32 #include "log.h"
33 #include "tcp.h"
34
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #define PARSE_BOOL_PARAM(SHORT, LONG, VALUE) \
41 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
42 VALUE = 1;
43
44 #define PARSE_INVERSE_BOOL_PARAM(SHORT, LONG, VALUE) \
45 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
46 VALUE = 0;
47
48 #define PARSE_INT_PARAM(SHORT, LONG, VALUE) \
49 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
50 { \
51 if(argc < 1) \
52 return i; \
53 VALUE = atoi(argv[i+1]); \
54 argc--; \
55 i++; \
56 }
57
58 #define PARSE_STRING_PARAM(SHORT, LONG, VALUE) \
59 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
60 { \
61 if(argc < 1 || argv[i+1][0] == '-') \
62 return i; \
63 if(VALUE) free(VALUE); \
64 VALUE = strdup(argv[i+1]); \
65 if(!VALUE) \
66 return -2; \
67 argc--; \
68 i++; \
69 }
70
71 #define PARSE_STRING_PARAM_SEC(SHORT, LONG, VALUE) \
72 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
73 { \
74 if(argc < 1 || argv[i+1][0] == '-') \
75 return i; \
76 if(VALUE) free(VALUE); \
77 VALUE = strdup(argv[i+1]); \
78 if(!VALUE) \
79 return -2; \
80 size_t j; \
81 for(j=0; j < strlen(argv[i+1]); ++j) \
82 argv[i+1][j] = '#'; \
83 argc--; \
84 i++; \
85 }
86
87 #define PARSE_HEXSTRING_PARAM_SEC(SHORT, LONG, VALUE) \
88 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
89 { \
90 if(argc < 1 || argv[i+1][0] == '-') \
91 return i; \
92 int ret; \
93 ret = options_parse_hex_string(argv[i+1], &VALUE); \
94 if(ret > 0) \
95 return i+1; \
96 else if(ret < 0) \
97 return ret; \
98 size_t j; \
99 for(j=0; j < strlen(argv[i+1]); ++j) \
100 argv[i+1][j] = '#'; \
101 argc--; \
102 i++; \
103 }
104
105 #define PARSE_STRING_LIST(SHORT, LONG, LIST) \
106 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
107 { \
108 if(argc < 1 || argv[i+1][0] == '-') \
109 return i; \
110 int ret = string_list_add(&LIST, argv[i+1]); \
111 if(ret == -2) \
112 return ret; \
113 else if(ret) \
114 return i+1; \
115 argc--; \
116 i++; \
117 }
118
119 #define PARSE_RESOLV_TYPE(SHORT, LONG, VALUE) \
120 else if(!strcmp(str,SHORT) || !strcmp(str,LONG)) \
121 { \
122 if(argc < 1 || argv[i+1][0] == '-') \
123 return i; \
124 if(!strcmp(argv[i+1], "4") || \
125 !strcmp(argv[i+1], "ipv4")) \
126 VALUE = IPV4_ONLY; \
127 else if(!strcmp(argv[i+1], "6") || \
128 !strcmp(argv[i+1], "ipv6")) \
129 VALUE = IPV6_ONLY; \
130 else \
131 return i+1; \
132 argc--; \
133 i++; \
134 }
135
options_parse_hex_string(const char * hex,buffer_t * buffer)136 int options_parse_hex_string(const char* hex, buffer_t* buffer)
137 {
138 if(!hex || !buffer)
139 return -1;
140
141 uint32_t hex_len = strlen(hex);
142 if(hex_len%2)
143 return 1;
144
145 if(buffer->buf_)
146 free(buffer->buf_);
147
148 buffer->length_ = hex_len/2;
149 buffer->buf_ = malloc(buffer->length_);
150 if(!buffer->buf_) {
151 buffer->length_ = 0;
152 return -2;
153 }
154
155 const char* ptr = hex;
156 int i;
157 for(i=0;i<buffer->length_;++i) {
158 uint32_t tmp;
159 sscanf(ptr, "%2X", &tmp);
160 buffer->buf_[i] = (uint8_t)tmp;
161 ptr += 2;
162 }
163
164 return 0;
165 }
166
167
options_parse(options_t * opt,int argc,char * argv[])168 int options_parse(options_t* opt, int argc, char* argv[])
169 {
170 if(!opt)
171 return -1;
172
173 options_default(opt);
174
175 if(opt->progname_)
176 free(opt->progname_);
177 opt->progname_ = strdup(argv[0]);
178 if(!opt->progname_)
179 return -2;
180
181 argc--;
182
183 int i;
184 for(i=1; argc > 0; ++i)
185 {
186 char* str = argv[i];
187 argc--;
188
189 if(!strcmp(str,"-h") || !strcmp(str,"--help"))
190 return -1;
191 else if(!strcmp(str,"-v") || !strcmp(str,"--version"))
192 return -3;
193 PARSE_INVERSE_BOOL_PARAM("-D","--nodaemonize", opt->daemonize_)
194 PARSE_STRING_PARAM("-u","--username", opt->username_)
195 PARSE_STRING_PARAM("-g","--groupname", opt->groupname_)
196 PARSE_STRING_PARAM("-C","--chroot", opt->chroot_dir_)
197 PARSE_STRING_PARAM("-P","--write-pid", opt->pid_file_)
198 PARSE_STRING_LIST("-L","--log", opt->log_targets_)
199 PARSE_BOOL_PARAM("-U", "--debug", opt->debug_)
200 PARSE_STRING_PARAM("-l","--local-addr", opt->local_addr_)
201 PARSE_RESOLV_TYPE("-t","--local-resolv", opt->lresolv_type_)
202 PARSE_STRING_PARAM("-p","--local-port", opt->local_port_)
203 PARSE_STRING_PARAM("-r","--remote-addr", opt->remote_addr_)
204 PARSE_RESOLV_TYPE("-R","--remote-resolv", opt->rresolv_type_)
205 PARSE_STRING_PARAM("-o","--remote-port", opt->remote_port_)
206 PARSE_STRING_PARAM("-s","--source-addr", opt->source_addr_)
207 PARSE_STRING_PARAM("-c","--config", opt->config_file_)
208 PARSE_INT_PARAM("-b","--buffer-size", opt->buffer_size_)
209 else
210 return i;
211 }
212
213 if(opt->debug_) {
214 string_list_add(&opt->log_targets_, "stdout:5");
215 opt->daemonize_ = 0;
216 }
217
218 if(!opt->log_targets_.first_) {
219 string_list_add(&opt->log_targets_, "syslog:3,tcpproxy,daemon");
220 }
221
222 if(!opt->local_port_ && !opt->config_file_) {
223 opt->config_file_ = strdup(CONFFILE);
224 if(!opt->config_file_) return -2;
225 }
226
227 return 0;
228 }
229
options_parse_post(options_t * opt)230 void options_parse_post(options_t* opt)
231 {
232 if(!opt)
233 return;
234
235 if(opt->config_file_ && opt->local_port_) {
236 log_printf(WARNING, "local port and config file specified, will ignore config file");
237 free(opt->config_file_);
238 opt->config_file_ = NULL;
239 }
240
241 if(opt->buffer_size_ <= 0) {
242 log_printf(WARNING, "illegal buffer size %d using default buffer size", opt->buffer_size_);
243 opt->buffer_size_ = 10 * 1024;
244 }
245 }
246
options_default(options_t * opt)247 void options_default(options_t* opt)
248 {
249 if(!opt)
250 return;
251
252 opt->progname_ = strdup("tcpproxy");
253 opt->daemonize_ = 1;
254 opt->username_ = NULL;
255 opt->groupname_ = NULL;
256 opt->chroot_dir_ = NULL;
257 opt->pid_file_ = NULL;
258 opt->local_addr_ = NULL;
259 opt->lresolv_type_ = ANY;
260 opt->local_port_ = NULL;
261 opt->remote_addr_ = NULL;
262 opt->rresolv_type_ = ANY;
263 opt->remote_port_ = NULL;
264 opt->source_addr_ = NULL;
265 opt->config_file_ = NULL;
266 string_list_init(&opt->log_targets_);
267 opt->buffer_size_ = 10 * 1024;
268 opt->debug_ = 0;
269 }
270
options_clear(options_t * opt)271 void options_clear(options_t* opt)
272 {
273 if(!opt)
274 return;
275
276 if(opt->progname_)
277 free(opt->progname_);
278 if(opt->username_)
279 free(opt->username_);
280 if(opt->groupname_)
281 free(opt->groupname_);
282 if(opt->chroot_dir_)
283 free(opt->chroot_dir_);
284 if(opt->pid_file_)
285 free(opt->pid_file_);
286 string_list_clear(&opt->log_targets_);
287 if(opt->local_addr_)
288 free(opt->local_addr_);
289 if(opt->local_port_)
290 free(opt->local_port_);
291 if(opt->remote_addr_)
292 free(opt->remote_addr_);
293 if(opt->remote_port_)
294 free(opt->remote_port_);
295 if(opt->source_addr_)
296 free(opt->source_addr_);
297 if(opt->config_file_)
298 free(opt->config_file_);
299 }
300
options_print_usage()301 void options_print_usage()
302 {
303 printf("USAGE:\n");
304 printf("tcpproxy [-h|--help] prints this...\n");
305 printf(" [-v|--version] print version info and exit\n");
306 printf(" [-D|--nodaemonize] don't run in background\n");
307 printf(" [-u|--username] <username> change to this user\n");
308 printf(" [-g|--groupname] <groupname> change to this group\n");
309 printf(" [-C|--chroot] <path> chroot to this directory\n");
310 printf(" [-P|--write-pid] <path> write pid to this file\n");
311 printf(" [-L|--log] <target>:<level>[,<param1>[,<param2>..]]\n");
312 printf(" add a log target, can be invoked several times\n");
313 printf(" [-U|--debug] don't daemonize and log to stdout with maximum log level\n");
314 printf(" [-l|--local-addr] <host> local address to listen on\n");
315 printf(" [-t|--local-resolv] (ipv4|4|ipv6|6) set IPv4 or IPv6 only resolving for the local address\n");
316 printf(" [-p|--local-port] <service> local port to listen on\n");
317 printf(" [-r|--remote-addr] <host> remote address to connect to\n");
318 printf(" [-R|--remote-resolv] (ipv4|4|ipv6|6) set IPv4 or IPv6 only resolving for remote and source address\n");
319 printf(" [-o|--remote-port] <service> remote port to connect to\n");
320 printf(" [-s|--source-addr] <host> source address to connect from\n");
321 printf(" [-b|--buffer-size] <size> size of transmit buffers\n");
322 printf(" [-c|--config] <file> configuration file\n");
323 }
324
options_print_version()325 void options_print_version()
326 {
327 printf("%s\n", VERSION_STRING_0);
328 #if defined(__clang__)
329 printf("%s, using CLANG %s\n", VERSION_STRING_1, __clang_version__);
330 #elif defined(__GNUC__)
331 printf("%s, using GCC %d.%d.%d\n", VERSION_STRING_1, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
332 #else
333 printf("%s\n", VERSION_STRING_1);
334 #endif
335 }
336
options_print(options_t * opt)337 void options_print(options_t* opt)
338 {
339 if(!opt)
340 return;
341
342 printf("progname: '%s'\n", opt->progname_);
343 printf("daemonize: %d\n", opt->daemonize_);
344 printf("username: '%s'\n", opt->username_);
345 printf("groupname: '%s'\n", opt->groupname_);
346 printf("chroot_dir: '%s'\n", opt->chroot_dir_);
347 printf("pid_file: '%s'\n", opt->pid_file_);
348 printf("log_targets: \n");
349 string_list_print(&opt->log_targets_, " '", "'\n");
350 printf("local_addr: '%s'\n", opt->local_addr_);
351 if(opt->lresolv_type_ == IPV4_ONLY) printf("lresolv_type: IPv4\n");
352 else if(opt->lresolv_type_ == IPV6_ONLY) printf("lresolv_type: IPv6\n");
353 else printf("lresolv_type: Both\n");
354 printf("local_port: '%s'\n", opt->local_port_);
355 printf("remote_addr: '%s'\n", opt->remote_addr_);
356 if(opt->rresolv_type_ == IPV4_ONLY) printf("rresolv_type: IPv4\n");
357 else if(opt->rresolv_type_ == IPV6_ONLY) printf("rresolv_type: IPv6\n");
358 else printf("rresolv_type: Both\n");
359 printf("remote_port: '%s'\n", opt->remote_port_);
360 printf("source_addr: '%s'\n", opt->source_addr_);
361 printf("buffer-size: %d\n", opt->buffer_size_);
362 printf("config_file: '%s'\n", opt->config_file_);
363 printf("debug: %s\n", !opt->debug_ ? "false" : "true");
364 }
365