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