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
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/select.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <fcntl.h>
41
42 #include "clients.h"
43 #include "tcp.h"
44 #include "log.h"
45
clients_delete_element(void * e)46 void clients_delete_element(void* e)
47 {
48 if(!e)
49 return;
50
51 client_t* element = (client_t*)e;
52 close(element->fd_[0]);
53 close(element->fd_[1]);
54 if(element->write_buf_[0].buf_)
55 free(element->write_buf_[0].buf_);
56 if(element->write_buf_[1].buf_)
57 free(element->write_buf_[1].buf_);
58
59 free(e);
60 }
61
clients_init(clients_t * list,int32_t buffer_size)62 int clients_init(clients_t* list, int32_t buffer_size)
63 {
64 list->buffer_size_ = buffer_size;
65 return slist_init(&(list->list_), &clients_delete_element);
66 }
67
clients_clear(clients_t * list)68 void clients_clear(clients_t* list)
69 {
70 slist_clear(&(list->list_));
71 }
72
handle_connect(client_t * c,int32_t buffer_size_)73 static int handle_connect(client_t* c, int32_t buffer_size_)
74 {
75 if(!c || c->state_ != CONNECTING)
76 return -1;
77
78 int error = 0;
79 socklen_t len = sizeof(error);
80 if(getsockopt(c->fd_[1], SOL_SOCKET, SO_ERROR, &error, &len)==-1) {
81 log_printf(ERROR, "Error on getsockopt(): %s", strerror(errno));
82 return -1;
83 }
84 if(error) {
85 log_printf(ERROR, "Error on connect(): %s, not adding client %d", strerror(error), c->fd_[0]);
86 return -1;
87 }
88
89 int i;
90 for(i = 0; i < 2; ++i) {
91 c->write_buf_[i].buf_ = malloc(buffer_size_);
92 if(!c->write_buf_[i].buf_) return -2;
93 c->write_buf_[i].length_ = buffer_size_;
94 c->write_buf_offset_[i] = 0;
95 c->transferred_[i] = 0;
96 }
97
98 log_printf(INFO, "successfully added client %d", c->fd_[0]);
99 c->state_ = CONNECTED;
100 return 0;
101 }
102
clients_add(clients_t * list,int fd,const tcp_endpoint_t remote_end,const tcp_endpoint_t source_end)103 int clients_add(clients_t* list, int fd, const tcp_endpoint_t remote_end, const tcp_endpoint_t source_end)
104 {
105 if(!list)
106 return -1;
107
108 client_t* element = malloc(sizeof(client_t));
109 if(!element) {
110 close(fd);
111 return -2;
112 }
113
114 int i;
115 for(i = 0; i < 2; ++i) {
116 element->write_buf_[i].buf_ = NULL;
117 element->write_buf_[i].length_ = 0;
118 element->write_buf_offset_[i] = 0;
119 }
120 element->state_ = CONNECTING;
121 element->fd_[0] = fd;
122 element->fd_[1] = socket(remote_end.addr_.ss_family, SOCK_STREAM, 0);
123 if(element->fd_[1] < 0) {
124 log_printf(INFO, "Error on socket(): %s, not adding client %d", strerror(errno), element->fd_[0]);
125 close(element->fd_[0]);
126 free(element);
127 return -1;
128 }
129
130 int on = 1;
131 if(setsockopt(element->fd_[0], IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) ||
132 setsockopt(element->fd_[1], IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on))) {
133 log_printf(ERROR, "Error on setsockopt(): %s", strerror(errno));
134 close(element->fd_[0]);
135 close(element->fd_[1]);
136 free(element);
137 return -1;
138 }
139
140 if(fcntl(element->fd_[0], F_SETFL, O_NONBLOCK) ||
141 fcntl(element->fd_[1], F_SETFL, O_NONBLOCK)) {
142 log_printf(ERROR, "Error on fcntl(): %s", strerror(errno));
143 close(element->fd_[0]);
144 close(element->fd_[1]);
145 free(element);
146 return -1;
147 }
148
149 if(source_end.addr_.ss_family != AF_UNSPEC) {
150 if(bind(element->fd_[1], (struct sockaddr *)&(source_end.addr_), source_end.len_)==-1) {
151 log_printf(INFO, "Error on bind(): %s, not adding client %d", strerror(errno), element->fd_[0]);
152 close(element->fd_[0]);
153 close(element->fd_[1]);
154 free(element);
155 return -1;
156 }
157 }
158
159 if(slist_add(&(list->list_), element) == NULL) {
160 close(element->fd_[0]);
161 close(element->fd_[1]);
162 free(element);
163 return -2;
164 }
165
166 if(connect(element->fd_[1], (struct sockaddr *)&(remote_end.addr_), remote_end.len_)==-1) {
167 if(errno == EINPROGRESS)
168 return 0;
169
170 log_printf(INFO, "Error on connect(): %s, not adding client %d", strerror(errno), element->fd_[0]);
171 slist_remove(&(list->list_), element);
172 return -1;
173 }
174
175 log_printf(DEBUG, "connect() for client %d returned immediatly", element->fd_[0]);
176
177 int ret = handle_connect(element, list->buffer_size_);
178 if(ret)
179 slist_remove(&(list->list_), element);
180
181 return ret;
182 }
183
clients_remove(clients_t * list,int fd)184 void clients_remove(clients_t* list, int fd)
185 {
186 slist_remove(&(list->list_), clients_find(list, fd));
187 }
188
clients_find(clients_t * list,int fd)189 client_t* clients_find(clients_t* list, int fd)
190 {
191 if(!list)
192 return NULL;
193
194 slist_element_t* tmp = list->list_.first_;
195 while(tmp) {
196 client_t* c = (client_t*)tmp->data_;
197 if(c && (c->fd_[0] == fd || c->fd_[1] == fd))
198 return c;
199 tmp = tmp->next_;
200 }
201
202 return NULL;
203 }
204
clients_print(clients_t * list)205 void clients_print(clients_t* list)
206 {
207 if(!list)
208 return;
209
210 slist_element_t* tmp = list->list_.first_;
211 while(tmp) {
212 client_t* c = (client_t*)tmp->data_;
213 if(c) {
214 char state = '?';
215 switch(c->state_) {
216 case CONNECTING: state = '>'; break;
217 case CONNECTED: state = 'c'; break;
218 }
219 log_printf(NOTICE, "[%c] client #%d/%d: %lld bytes received, %lld bytes sent", state, c->fd_[0], c->fd_[1], c->transferred_[0], c->transferred_[1]);
220 }
221 tmp = tmp->next_;
222 }
223 }
224
clients_read_fds(clients_t * list,fd_set * set,int * max_fd)225 void clients_read_fds(clients_t* list, fd_set* set, int* max_fd)
226 {
227 if(!list)
228 return;
229
230 slist_element_t* tmp = list->list_.first_;
231 while(tmp) {
232 client_t* c = (client_t*)tmp->data_;
233 if(c && c->state_ == CONNECTED) {
234 if(c->write_buf_offset_[1] < c->write_buf_[1].length_) {
235 FD_SET(c->fd_[0], set);
236 *max_fd = *max_fd > c->fd_[0] ? *max_fd : c->fd_[0];
237 }
238 if(c->write_buf_offset_[0] < c->write_buf_[0].length_) {
239 FD_SET(c->fd_[1], set);
240 *max_fd = *max_fd > c->fd_[1] ? *max_fd : c->fd_[1];
241 }
242 }
243 tmp = tmp->next_;
244 }
245 }
246
clients_write_fds(clients_t * list,fd_set * set,int * max_fd)247 void clients_write_fds(clients_t* list, fd_set* set, int* max_fd)
248 {
249 if(!list)
250 return;
251
252 slist_element_t* tmp = list->list_.first_;
253 while(tmp) {
254 client_t* c = (client_t*)tmp->data_;
255 if(c && c->state_ == CONNECTED) {
256 if(c->write_buf_offset_[0]) {
257 FD_SET(c->fd_[0], set);
258 *max_fd = *max_fd > c->fd_[0] ? *max_fd : c->fd_[0];
259 }
260 if(c->write_buf_offset_[1]) {
261 FD_SET(c->fd_[1], set);
262 *max_fd = *max_fd > c->fd_[1] ? *max_fd : c->fd_[1];
263 }
264 } else if(c && c->state_ == CONNECTING) {
265 FD_SET(c->fd_[1], set);
266 *max_fd = *max_fd > c->fd_[1] ? *max_fd : c->fd_[1];
267 }
268 tmp = tmp->next_;
269 }
270 }
271
clients_read(clients_t * list,fd_set * set)272 int clients_read(clients_t* list, fd_set* set)
273 {
274 if(!list)
275 return -1;
276
277 slist_element_t* tmp = list->list_.first_;
278 while(tmp) {
279 client_t* c = (client_t*)tmp->data_;
280 tmp = tmp->next_;
281 if(c && c->state_ == CONNECTED) {
282 int i;
283 for(i=0; i<2; ++i) {
284 int in, out;
285 if(FD_ISSET(c->fd_[i], set)) {
286 in = i;
287 out = i ^ 1;
288 }
289 else continue;
290
291 int len = recv(c->fd_[in], &(c->write_buf_[out].buf_[c->write_buf_offset_[out]]), c->write_buf_[out].length_ - c->write_buf_offset_[out], 0);
292 if(len < 0) {
293 log_printf(INFO, "Error on recv(): %s, removing client %d", strerror(errno), c->fd_[0]);
294 slist_remove(&(list->list_), c);
295 break;
296 }
297 else if(!len) {
298 log_printf(INFO, "client %d closed connection, removing it", c->fd_[0]);
299 slist_remove(&(list->list_), c);
300 break;
301 }
302 else
303 c->write_buf_offset_[out] += len;
304 }
305 }
306 }
307
308 return 0;
309 }
310
clients_write(clients_t * list,fd_set * set)311 int clients_write(clients_t* list, fd_set* set)
312 {
313 if(!list)
314 return -1;
315
316 slist_element_t* tmp = list->list_.first_;
317 while(tmp) {
318 client_t* c = (client_t*)tmp->data_;
319 tmp = tmp->next_;
320 if(c && c->state_ == CONNECTED) {
321 int i;
322 for(i=0; i<2; ++i) {
323 if(FD_ISSET(c->fd_[i], set)) {
324 int len = send(c->fd_[i], c->write_buf_[i].buf_, c->write_buf_offset_[i], 0);
325 if(len < 0) {
326 log_printf(INFO, "Error on send(): %s, removing client %d", strerror(errno), c->fd_[0]);
327 slist_remove(&(list->list_), c);
328 break;
329 }
330 else {
331 c->transferred_[i] += len;
332 if(c->write_buf_offset_[i] > len) {
333 memmove(c->write_buf_[i].buf_, &c->write_buf_[i].buf_[len], c->write_buf_offset_[i] - len);
334 c->write_buf_offset_[i] -= len;
335 }
336 else
337 c->write_buf_offset_[i] = 0;
338 }
339 }
340 }
341 } else if(c && c->state_ == CONNECTING && FD_ISSET(c->fd_[1], set)) {
342 int ret = handle_connect(c, list->buffer_size_);
343 if(ret)
344 slist_remove(&(list->list_), c);
345 }
346 }
347
348 return 0;
349 }
350