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