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 <string.h>
30#include <errno.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <unistd.h>
35#include <sys/mman.h>
36
37#include "datatypes.h"
38#include "log.h"
39#include "options.h"
40#include "tcp.h"
41#include "listener.h"
42
43struct listener {
44  char* la_;
45  resolv_type_t lrt_;
46  char* lp_;
47  char* ra_;
48  resolv_type_t rrt_;
49  char* rp_;
50  char* sa_;
51};
52
53static void init_listener_struct(struct listener* l)
54{
55  if(!l) return;
56
57  l->la_ = NULL;
58  l->lrt_ = ANY;
59  l->lp_ = NULL;
60  l->ra_ = NULL;
61  l->rrt_ = ANY;
62  l->rp_ = NULL;
63  l->sa_ = NULL;
64}
65
66static void clear_listener_struct(struct listener* l)
67{
68  if(!l) return;
69
70  if(l->la_)
71    free(l->la_);
72  if(l->lp_)
73    free(l->lp_);
74  if(l->ra_)
75    free(l->ra_);
76  if(l->rp_)
77    free(l->rp_);
78  if(l->sa_)
79    free(l->sa_);
80
81  init_listener_struct(l);
82}
83
84static int owrt_string(char** dest, char* start, char* end)
85{
86  if(!dest || start >= end)
87    return -1;
88
89  if(*dest) free(*dest);
90  int n = end - start;
91  *dest = malloc(n+1);
92  if(!(*dest))
93    return -2;
94
95  memcpy(*dest, start, n);
96  (*dest)[n] = 0;
97
98  return 0;
99}
100
101%%{
102  machine cfg_parser;
103
104  action set_cpy_start  { cpy_start = fpc; }
105  action set_local_addr { ret = owrt_string(&(lst.la_), cpy_start, fpc); cpy_start = NULL; }
106  action set_local_port { ret = owrt_string(&(lst.lp_), cpy_start, fpc); cpy_start = NULL; }
107  action set_local_resolv4 { lst.lrt_ = IPV4_ONLY; }
108  action set_local_resolv6 { lst.lrt_ = IPV6_ONLY; }
109  action set_remote_addr { ret = owrt_string(&(lst.ra_), cpy_start, fpc); cpy_start = NULL; }
110  action set_remote_port { ret = owrt_string(&(lst.rp_), cpy_start, fpc); cpy_start = NULL; }
111  action set_remote_resolv4 { lst.rrt_ = IPV4_ONLY; }
112  action set_remote_resolv6 { lst.rrt_ = IPV6_ONLY; }
113  action set_source_addr { ret = owrt_string(&(lst.sa_), cpy_start, fpc); cpy_start = NULL; }
114  action add_listener {
115    ret = listeners_add(listener, lst.la_, lst.lrt_, lst.lp_, lst.ra_, lst.rrt_, lst.rp_, lst.sa_);
116    clear_listener_struct(&lst);
117  }
118  action logerror {
119    if(fpc == eof)
120      log_printf(ERROR, "config file syntax error: unexpected end of file");
121    else
122      log_printf(ERROR, "config file syntax error at line %d", cur_line);
123
124    fgoto *cfg_parser_error;
125  }
126
127  newline = '\n' @{cur_line++;};
128  ws = [ \t];
129  comment = '#' [^\n]* newline;
130  ign = ( ws | comment | newline | [\v\f\r] );
131
132  number = [0-9]+;
133  ipv4_addr = [0-9.]+;
134  ipv6_addr = [0-9a-fA-F:]+;
135  name = [a-zA-Z0-9\-]+;
136  host_name = [a-zA-Z0-9\-.]+;
137  tok_ipv4 = "ipv4"i;
138  tok_ipv6 = "ipv6"i;
139
140  host_or_addr = ( host_name | ipv4_addr | ipv6_addr );
141  service = ( number | name );
142
143  local_addr = ( '*' | host_or_addr >set_cpy_start %set_local_addr );
144  local_port = service >set_cpy_start %set_local_port;
145  lresolv = ( tok_ipv4 @set_local_resolv4 | tok_ipv6 @set_local_resolv6 );
146
147  remote_addr = host_or_addr >set_cpy_start %set_remote_addr;
148  remote_port = service >set_cpy_start %set_remote_port;
149  rresolv = ( tok_ipv4 @set_remote_resolv4 | tok_ipv6 @set_remote_resolv6 );
150
151  source_addr = host_or_addr >set_cpy_start %set_source_addr;
152
153  resolv = "resolv" ws* ":" ws+ lresolv ws* ";";
154  remote = "remote" ws* ":" ws+ remote_addr ws+ remote_port ws* ";";
155  remote_resolv = "remote-resolv" ws* ":" ws+ rresolv ws* ";";
156  source = "source" ws* ":" ws+ source_addr ws* ";";
157
158  listen_head = 'listen' ws+ local_addr ws+ local_port;
159  listen_body = '{' ( ign+ | resolv | remote | remote_resolv | source )* '};' @add_listener;
160
161  main := ( listen_head ign* listen_body | ign+ )* $!logerror;
162}%%
163
164
165int parse_listener(char* p, char* pe, listeners_t* listener)
166{
167  int cs, ret = 0, cur_line = 1;
168
169  %% write data;
170  %% write init;
171
172  char* cpy_start = NULL;
173  struct listener lst;
174  init_listener_struct(&lst);
175
176  char* eof = pe;
177  %% write exec;
178
179  if(cs == cfg_parser_error) {
180    listeners_revert(listener);
181    ret = 1;
182  }
183  else
184    ret = listeners_update(listener);
185
186  clear_listener_struct(&lst);
187
188  return ret;
189}
190
191int read_configfile(const char* filename, listeners_t* listener)
192{
193  int fd = open(filename, 0);
194  if(fd < 0) {
195    log_printf(ERROR, "open('%s') failed: %s", filename, strerror(errno));
196    return -1;
197  }
198
199  struct stat sb;
200  if(fstat(fd, &sb) == -1) {
201    log_printf(ERROR, "fstat() error: %s", strerror(errno));
202    close(fd);
203    return -1;
204  }
205
206  if(!sb.st_size) {
207    log_printf(ERROR, "config file %s is empty", filename);
208    close(fd);
209    return -1;
210  }
211
212  if(!S_ISREG(sb.st_mode)) {
213    log_printf(ERROR, "config file %s is not a regular file", filename);
214    close(fd);
215    return -1;
216  }
217
218  char* p = (char*)mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
219  if(p == MAP_FAILED) {
220    log_printf(ERROR, "mmap() error: %s", strerror(errno));
221    close(fd);
222    return -1;
223  }
224  close(fd);
225
226  log_printf(DEBUG, "mapped %ld bytes from file %s at address 0x%08lX", sb.st_size, filename, p);
227  int ret = parse_listener(p, p + sb.st_size, listener);
228
229  if(munmap(p, sb.st_size) == -1) {
230    log_printf(ERROR, "munmap() error: %s", strerror(errno));
231    return -1;
232  }
233  log_printf(DEBUG, "unmapped file %s", filename);
234
235  return ret;
236}
237