ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/vpn_tcp.C
Revision: 1.23
Committed: Tue Dec 4 17:17:20 2007 UTC (16 years, 5 months ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.22: +4 -2 lines
Log Message:
switch to new callback system

File Contents

# Content
1 /*
2 vpn_tcp.C -- handle the tcp part of the protocol.
3 Copyright (C) 2003-2007 Marc Lehmann <gvpe@schmorp.de>
4
5 This file is part of GVPE.
6
7 GVPE is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with gvpe; if not, write to the Free Software
19 Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "config.h"
23
24 #if ENABLE_TCP
25
26 // tcp processing is extremely ugly, since the gvpe protocol is simply
27 // designed for unreliable datagram networks. tcp is implemented by
28 // multiplexing packets over tcp. errors are completely ignored, as we
29 // rely on the higher level layers to time out and reconnect.
30
31 #include <cstring>
32
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/wait.h>
36 #include <sys/uio.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41
42 #include <map>
43
44 #include "netcompat.h"
45
46 #include "vpn.h"
47
48 #if ENABLE_HTTP_PROXY
49 # include "conf.h"
50 #endif
51
52 struct tcp_connection;
53
54 struct lt_sockinfo
55 {
56 bool operator()(const sockinfo *a, const sockinfo *b) const
57 {
58 return *a < *b;
59 }
60 };
61
62 struct tcp_si_map : public map<const sockinfo *, tcp_connection *, lt_sockinfo>
63 {
64 void cleaner_cb (ev::timer &w, int revents); ev::timer cleaner;
65
66 tcp_si_map ()
67 {
68 cleaner.set<tcp_si_map, &tcp_si_map::cleaner_cb> (this);
69 cleaner.start (::conf.keepalive / 2, ::conf.keepalive / 2);
70 }
71
72 } tcp_si;
73
74 struct tcp_connection : ev::io
75 {
76 int tos;
77 tstamp last_activity;
78 const sockinfo si;
79 vpn &v;
80 bool active; // this connection has been actively established
81 enum { ERROR, IDLE, CONNECTING, CONNECTING_PROXY, ESTABLISHED } state;
82
83 vpn_packet *r_pkt;
84 u32 r_len, r_ofs;
85
86 vpn_packet *w_pkt;
87 u32 w_len, w_ofs;
88
89 #if ENABLE_HTTP_PROXY
90 char *proxy_req;
91 int proxy_req_len;
92 #endif
93
94 void tcpv4_ev (ev::io &w, int revents);
95
96 bool send_packet (vpn_packet *pkt, int tos);
97 bool write_packet ();
98
99 void error (); // abort conenction && cleanup
100
101 operator tcp_si_map::value_type()
102 {
103 return tcp_si_map::value_type (&si, this);
104 }
105
106 tcp_connection (int fd_, const sockinfo &si_, vpn &v_);
107 ~tcp_connection ();
108 };
109
110 void tcp_si_map::cleaner_cb (ev::timer &w, int revents)
111 {
112 tstamp to = ev_now () - ::conf.keepalive - 30 - 60;
113
114 for (iterator i = begin (); i != end(); )
115 if (i->second->last_activity >= to)
116 ++i;
117 else
118 {
119 delete i->second;
120 erase (i);
121 i = begin ();
122 }
123 }
124
125 void
126 vpn::tcpv4_ev (ev::io &w, int revents)
127 {
128 if (revents & EV_READ)
129 {
130 struct sockaddr_in sa;
131 socklen_t sa_len = sizeof (sa);
132 int len;
133
134 int fd = accept (w.fd, (sockaddr *)&sa, &sa_len);
135
136 if (fd >= 0)
137 {
138 fcntl (fd, F_SETFL, O_NONBLOCK);
139 fcntl (fd, F_SETFD, FD_CLOEXEC);
140
141 sockinfo si(sa, PROT_TCPv4);
142
143 slog (L_DEBUG, _("%s: accepted tcp connection"), (const char *)si);//D
144
145 tcp_connection *i = new tcp_connection (fd, si, *this);
146 tcp_si.insert (*i);
147 }
148 }
149 }
150
151 bool
152 vpn::send_tcpv4_packet (vpn_packet *pkt, const sockinfo &si, int tos)
153 {
154 tcp_si_map::iterator info = tcp_si.find (&si);
155
156 tcp_connection *i;
157
158 if (info == tcp_si.end ())
159 {
160 i = new tcp_connection (-1, si, *this);
161 tcp_si.insert (*i);
162 }
163 else
164 i = info->second;
165
166 return i->send_packet (pkt, tos);
167 }
168
169 bool
170 tcp_connection::write_packet ()
171 {
172 ssize_t len;
173
174 if (w_ofs < 2)
175 {
176 u16 plen = htons (w_pkt->len);
177
178 iovec vec[2];
179 //TODO: char* is the right type? hardly...
180 vec[0].iov_base = (char *)((u8 *)&plen) + w_ofs;
181 vec[0].iov_len = 2 - w_ofs;
182 vec[1].iov_base = (char *)&((*w_pkt)[0]);
183 vec[1].iov_len = w_len - 2;
184
185 len = writev (fd, vec, 2);
186 }
187 else
188 len = write (fd, &((*w_pkt)[w_ofs - 2]), w_len);
189
190 if (len > 0)
191 {
192 w_ofs += len;
193 w_len -= len;
194
195 return w_len == 0;
196 }
197 else if (len < 0 && (errno == EAGAIN || errno == EINTR))
198 return false;
199 else
200 {
201 error ();
202 return false;
203 }
204 }
205
206 void
207 tcp_connection::tcpv4_ev (ev::io &w, int revents)
208 {
209 last_activity = ev_now ();
210
211 if (revents & EV_WRITE)
212 {
213 if (state == CONNECTING)
214 {
215 state = ESTABLISHED;
216 set (EV_READ);
217 #if ENABLE_HTTP_PROXY
218 if (::conf.proxy_host && ::conf.proxy_port)
219 {
220 state = CONNECTING_PROXY;
221
222 if (write (fd, proxy_req, proxy_req_len) == 0)
223 {
224 error ();
225 return;
226 }
227
228 free (proxy_req); proxy_req = 0;
229 }
230 #endif
231 }
232 else if (state == ESTABLISHED)
233 {
234 if (w_pkt)
235 {
236 if (write_packet ())
237 {
238 delete w_pkt; w_pkt = 0;
239
240 set (EV_READ);
241 }
242 }
243 else
244 set (EV_READ);
245 }
246 else
247 set (EV_READ);
248 }
249
250 if (revents & EV_READ)
251 {
252 if (state == ESTABLISHED)
253 for (;;)
254 {
255 if (!r_pkt)
256 {
257 r_pkt = new vpn_packet;
258 r_ofs = 0;
259 r_len = 2; // header
260 }
261
262 ssize_t len = read (fd, &((*r_pkt)[r_ofs < 2 ? r_ofs : r_ofs - 2]), r_len);
263
264 if (len > 0)
265 {
266 r_len -= len;
267 r_ofs += len;
268
269 if (r_len == 0)
270 {
271 if (r_ofs == 2)
272 {
273 r_len = ntohs (*(u16 *)&((*r_pkt)[0]));
274 r_pkt->len = r_len;
275
276 if (r_len > 0 && r_len < MAXSIZE)
277 continue;
278 }
279 else
280 {
281 v.recv_vpn_packet (r_pkt, si);
282 delete r_pkt;
283 r_pkt = 0;
284
285 continue;
286 }
287 }
288 else
289 break;
290 }
291 else if (len < 0 && (errno == EINTR || errno == EAGAIN))
292 break;
293
294 // len == 0 <-> EOF
295 error ();
296 break;
297 }
298 #if ENABLE_HTTP_PROXY
299 else if (state == CONNECTING_PROXY)
300 {
301 fcntl (fd, F_SETFL, 0);
302 char r[1024];
303 int i;
304 bool emptyline = false;
305
306 // we do a blocking read of the response, to hell with it
307 for (i = 0; i < 1023; i++)
308 {
309 int l = read (fd, &r[i], 1);
310
311 if (l <= 0)
312 {
313 error ();
314 return;
315 }
316
317 if (r[i] == '\012')
318 {
319 if (emptyline)
320 break;
321 else
322 emptyline = true;
323 }
324 else if (r[i] != '\015')
325 emptyline = false;
326 }
327
328 fcntl (fd, F_SETFL, O_NONBLOCK);
329
330 if (i < 12)
331 {
332 slog (L_ERR, _("(%s): unable to do proxy-forwarding, short response"),
333 (const char *)si);
334 error ();
335 }
336 else if (r[0] != 'H' || r[1] != 'T' || r[2] != 'T' || r[3] != 'P' || r[4] != '/'
337 || r[5] != '1' // http-major
338 || r[9] != '2') // response
339 {
340 slog (L_ERR, _("(%s): malformed or unexpected proxy response (%.12s)"),
341 (const char *)si, r);
342 error ();
343 }
344 else
345 state = ESTABLISHED;
346 }
347 #endif
348 }
349 }
350
351 bool
352 tcp_connection::send_packet (vpn_packet *pkt, int tos)
353 {
354 last_activity = ev_now ();
355
356 if (state == IDLE)
357 {
358 // woaw, the first lost packet ;)
359 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
360
361 if (fd >= 0)
362 {
363 const sockinfo *csi = &si;
364
365 #if ENABLE_HTTP_PROXY
366 sockinfo psi;
367
368 if (::conf.proxy_host && ::conf.proxy_port)
369 {
370 psi.set (::conf.proxy_host, ::conf.proxy_port, PROT_TCPv4);
371
372 if (psi.valid ())
373 {
374 csi = &psi;
375
376 proxy_req_len = asprintf (&proxy_req,
377 "CONNECT %s:%d HTTP/1.0\015\012"
378 "%s%s%s" // optional proxy-auth
379 "\015\012",
380 si.ntoa (),
381 ntohs (si.port),
382 ::conf.proxy_auth ? "Proxy-Authorization: Basic " : "",
383 ::conf.proxy_auth ? ::conf.proxy_auth : "",
384 ::conf.proxy_auth ? "\015\012" : "");
385
386 }
387 else
388 slog (L_ERR, _("unable to resolve http proxy hostname '%s', trying direct"),
389 ::conf.proxy_host);
390 }
391 #endif
392
393 fcntl (fd, F_SETFL, O_NONBLOCK);
394
395 if (connect (fd, csi->sav4 (), csi->salenv4 ()) >= 0
396 || errno == EINPROGRESS)
397 {
398 fcntl (fd, F_SETFL, O_NONBLOCK);
399 fcntl (fd, F_SETFD, FD_CLOEXEC);
400
401 state = CONNECTING;
402 start (fd, EV_WRITE);
403 }
404 else
405 close (fd);
406 }
407 }
408 else if (state == ESTABLISHED)
409 {
410 // drop packet if the tcp write buffer is full. this *is* the
411 // right thing to do, not using tcp *is* the right thing to do.
412 if (!w_pkt)
413 {
414 // how this maps to the underlying tcp packets we don't know
415 // and we don't care. at least we tried ;)
416 #if defined(SOL_IP) && defined(IP_TOS)
417 if (tos != this->tos)
418 {
419 this->tos = tos;
420 setsockopt (fd, SOL_IP, IP_TOS, &tos, sizeof tos);
421 }
422 #endif
423
424 w_pkt = pkt;
425 w_ofs = 0;
426 w_len = pkt->len + 2; // length + size header
427
428 if (write_packet ())
429 w_pkt = 0;
430 else
431 {
432 w_pkt = new vpn_packet;
433 w_pkt->set (*pkt);
434
435 set (EV_READ | EV_WRITE);
436 }
437 }
438 }
439
440 return state != ERROR;
441 }
442
443 void tcp_connection::error ()
444 {
445 stop ();
446
447 if (fd >= 0)
448 {
449 close (fd);
450 tos = -1;
451 fd = -1;
452 }
453
454 delete r_pkt; r_pkt = 0;
455 delete w_pkt; w_pkt = 0;
456 #if ENABLE_HTTP_PROXY
457 free (proxy_req); proxy_req = 0;
458 #endif
459
460 state = active ? IDLE : ERROR;
461 }
462
463 tcp_connection::tcp_connection (int fd_, const sockinfo &si_, vpn &v_)
464 : v(v_), si(si_)
465 {
466 set<tcp_connection, &tcp_connection::tcpv4_ev> (this);
467
468 last_activity = ev_now ();
469 r_pkt = 0;
470 w_pkt = 0;
471 tos = -1;
472 fd = fd_;
473 #if ENABLE_HTTP_PROXY
474 proxy_req = 0;
475 #endif
476
477 if (fd < 0)
478 {
479 active = true;
480 state = IDLE;
481 }
482 else
483 {
484 active = false;
485 state = ESTABLISHED;
486 start (fd, EV_READ);
487 }
488 }
489
490 tcp_connection::~tcp_connection ()
491 {
492 error ();
493 }
494
495 #endif
496