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