ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/vpn_tcp.C
Revision: 1.10
Committed: Thu Oct 16 02:41:21 2003 UTC (20 years, 7 months ago) by pcg
Content type: text/plain
Branch: MAIN
CVS Tags: poll-based-iom, VPE_1_2
Changes since 1.9: +1 -0 lines
Log Message:
*** empty log message ***

File Contents

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