ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/vpn_tcp.C
(Generate patch)

Comparing gvpe/src/vpn_tcp.C (file contents):
Revision 1.2 by pcg, Sun Apr 6 18:12:18 2003 UTC vs.
Revision 1.6 by pcg, Mon Apr 7 01:40:54 2003 UTC

43#include <fcntl.h> 43#include <fcntl.h>
44#include <sys/poll.h> 44#include <sys/poll.h>
45 45
46#include "vpn.h" 46#include "vpn.h"
47 47
48#if ENABLE_HTTP_PROXY
49# include "conf.h"
50#endif
51
48struct tcp_connection; 52struct tcp_connection;
49 53
50struct lt_sockinfo 54struct lt_sockinfo
51{ 55{
52 bool operator()(const sockinfo *a, const sockinfo *b) const 56 bool operator()(const sockinfo *a, const sockinfo *b) const
68struct tcp_connection : io_watcher { 72struct tcp_connection : io_watcher {
69 tstamp last_activity; 73 tstamp last_activity;
70 const sockinfo si; 74 const sockinfo si;
71 vpn &v; 75 vpn &v;
72 bool active; // this connection has been actively established 76 bool active; // this connection has been actively established
73 enum { ERROR, IDLE, CONNECTING, ESTABLISHED } state; 77 enum { ERROR, IDLE, CONNECTING, CONNECTING_PROXY, ESTABLISHED } state;
74 78
75 vpn_packet *r_pkt; 79 vpn_packet *r_pkt;
76 u32 r_len, r_ofs; 80 u32 r_len, r_ofs;
77 81
82 vpn_packet *w_pkt;
83 u32 w_len, w_ofs;
84
85#if ENABLE_HTTP_PROXY
86 char *proxy_req;
87 int proxy_req_len;
88#endif
89
78 void tcpv4_ev (io_watcher &w, short revents); 90 void tcpv4_ev (io_watcher &w, short revents);
79 91
80 bool send_packet (vpn_packet *pkt, int tos); 92 bool send_packet (vpn_packet *pkt, int tos);
93 bool write_packet ();
81 94
82 void error (); // abort conenction && cleanup 95 void error (); // abort conenction && cleanup
83 96
84 operator tcp_si_map::value_type() 97 operator tcp_si_map::value_type()
85 { 98 {
146 i = info->second; 159 i = info->second;
147 160
148 return i->send_packet (pkt, tos); 161 return i->send_packet (pkt, tos);
149} 162}
150 163
164bool
165tcp_connection::write_packet ()
166{
167 ssize_t len;
168
169 if (w_ofs < 2)
170 {
171 u16 plen = htons (w_pkt->len);
172
173 iovec vec[2];
174 vec[0].iov_base = ((u8 *)&plen) + w_ofs;
175 vec[0].iov_len = 2 - w_ofs;
176 vec[1].iov_base = &((*w_pkt)[0]);
177 vec[1].iov_len = w_len - 2;
178
179 len = writev (fd, vec, 2);
180 }
181 else
182 len = write (fd, &((*w_pkt)[w_ofs - 2]), w_len);
183
184 if (len > 0)
185 {
186 w_ofs += len;
187 w_len -= len;
188
189 return w_len == 0;
190 }
191 else if (len < 0 && (errno == EAGAIN || errno == EINTR))
192 return false;
193 else
194 {
195 error ();
196 return false;
197 }
198}
199
200void
201tcp_connection::tcpv4_ev (io_watcher &w, short revents)
202{
203 last_activity = NOW;
204
205 if (revents & (POLLERR | POLLHUP))
206 {
207 error ();
208 return;
209 }
210
211 if (revents & POLLOUT)
212 {
213 if (state == CONNECTING)
214 {
215 state = ESTABLISHED;
216 set (POLLIN);
217#if ENABLE_HTTP_PROXY
218 if (::conf.proxy_host && ::conf.proxy_port)
219 {
220 state = CONNECTING_PROXY;
221 write (fd, proxy_req, proxy_req_len);
222 free (proxy_req); proxy_req = 0;
223 }
224#endif
225 }
226 else if (state == ESTABLISHED)
227 {
228 if (w_pkt)
229 {
230 if (write_packet ())
231 {
232 delete w_pkt; w_pkt = 0;
233
234 set (POLLIN);
235 }
236 }
237 else
238 set (POLLIN);
239 }
240 else
241 set (POLLIN);
242 }
243
244 if (revents & POLLIN)
245 {
246 if (state == ESTABLISHED)
247 for (;;)
248 {
249 if (!r_pkt)
250 {
251 r_pkt = new vpn_packet;
252 r_ofs = 0;
253 r_len = 2; // header
254 }
255
256 ssize_t len = read (fd, &((*r_pkt)[r_ofs < 2 ? r_ofs : r_ofs - 2]), r_len);
257
258 if (len > 0)
259 {
260 r_len -= len;
261 r_ofs += len;
262
263 if (r_len == 0)
264 {
265 if (r_ofs == 2)
266 {
267 r_len = ntohs (*(u16 *)&((*r_pkt)[0]));
268 r_pkt->len = r_len;
269
270 if (r_len > 0 && r_len < MAXSIZE)
271 continue;
272 }
273 else
274 {
275 v.recv_vpn_packet (r_pkt, si);
276 delete r_pkt;
277 r_pkt = 0;
278
279 continue;
280 }
281 }
282 else
283 break;
284 }
285 else if (len < 0 && (errno == EINTR || errno == EAGAIN))
286 break;
287
288 error ();
289 break;
290 }
291#if ENABLE_HTTP_PROXY
292 else if (state == CONNECTING_PROXY)
293 {
294 fcntl (fd, F_SETFL, 0);
295 char r[1024];
296 int i;
297 bool emptyline = false;
298
299 // we do a blocking read of the response, to hell with it
300 for (i = 0; i < 1023; i++)
301 {
302 int l = read (fd, &r[i], 1);
303
304 if (l <= 0)
305 {
306 error ();
307 return;
308 }
309
310 if (r[i] == '\012')
311 {
312 if (emptyline)
313 break;
314 else
315 emptyline = true;
316 }
317 else if (r[i] != '\015')
318 emptyline = false;
319 }
320
321 fcntl (fd, F_SETFL, O_NONBLOCK);
322
323 if (i < 12)
324 {
325 slog (L_ERR, _("(%s): unable to do proxy-forwarding, short response"),
326 (const char *)si);
327 error ();
328 }
329 else if (r[0] != 'H' || r[1] != 'T' || r[2] != 'T' || r[3] != 'P' || r[4] != '/'
330 || r[5] != '1' // http-major
331 || r[9] != '2') // response
332 {
333 slog (L_ERR, _("(%s): malformed or unexpected proxy response (%.12s)"),
334 (const char *)si, r);
335 error ();
336 }
337 else
338 state = ESTABLISHED;
339 }
340#endif
341 }
342}
343
344bool
345tcp_connection::send_packet (vpn_packet *pkt, int tos)
346{
347 last_activity = NOW;
348
349 if (state == IDLE)
350 {
351 // woaw, the first lost packet ;)
352 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
353
354 if (fd >= 0)
355 {
356 const sockinfo *csi = &si;
357
358#if ENABLE_HTTP_PROXY
359 sockinfo psi;
360
361 if (::conf.proxy_host && ::conf.proxy_port)
362 {
363 psi.set (::conf.proxy_host, ::conf.proxy_port, PROT_TCPv4);
364
365 if (psi.valid ())
366 {
367 csi = &psi;
368
369 proxy_req_len = asprintf (&proxy_req,
370 "CONNECT %s:%d HTTP/1.0\015\012"
371 "%s%s%s" // optional proxy-auth
372 "\015\012",
373 si.ntoa (),
374 ntohs (si.port),
375 ::conf.proxy_auth ? "Proxy-Authorization: Basic " : "",
376 ::conf.proxy_auth ? ::conf.proxy_auth : "",
377 ::conf.proxy_auth ? "\015\012" : "");
378
379 }
380 else
381 slog (L_ERR, _("unable to resolve http proxy hostname '%s', trying direct"),
382 ::conf.proxy_host);
383 }
384#endif
385
386 fcntl (fd, F_SETFL, O_NONBLOCK);
387
388 if (connect (fd, csi->sav4 (), csi->salenv4 ()) >= 0
389 || errno == EINPROGRESS)
390 {
391 state = CONNECTING;
392 start (fd, POLLOUT);
393 }
394 else
395 close (fd);
396 }
397 }
398 else if (state == ESTABLISHED)
399 {
400 // drop packet if the tcp write buffer is full. this *is* the
401 // right thing to do, not using tcp *is* the right thing to do.
402 if (!w_pkt)
403 {
404 // how this maps to the underlying tcp packets we don't know
405 // and we don't care. at least we tried ;)
406 setsockopt (fd, SOL_IP, IP_TOS, &tos, sizeof tos);
407
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
419 set (POLLIN | POLLOUT);
420 }
421 }
422 }
423
424 return state != ERROR;
425}
426
151void tcp_connection::error () 427void tcp_connection::error ()
152{ 428{
153 if (fd >= 0) 429 if (fd >= 0)
154 { 430 {
155 close (fd); 431 close (fd);
156 fd = -1; 432 fd = -1;
157 } 433 }
158 434
159 delete r_pkt; 435 delete r_pkt; r_pkt = 0;
160 r_pkt = 0; 436 delete w_pkt; w_pkt = 0;
437#if ENABLE_HTTP_PROXY
438 free (proxy_req); proxy_req = 0;
439#endif
161 440
162 stop (); 441 stop ();
163 state = active ? IDLE : ERROR; 442 state = active ? IDLE : ERROR;
164} 443}
165 444
166void
167tcp_connection::tcpv4_ev (io_watcher &w, short revents)
168{
169 last_activity = NOW;
170
171 if (revents & (POLLERR | POLLHUP))
172 error ();
173 else if (revents & POLLOUT && state == CONNECTING)
174 {
175 state = ESTABLISHED;
176 set (POLLIN);
177 }
178
179 else if (revents & POLLIN)
180 {
181 for (;;)
182 {
183 if (!r_pkt)
184 {
185 r_pkt = new vpn_packet;
186 r_ofs = 0;
187 r_len = 2; // header
188 }
189
190 ssize_t len = read (fd, &((*r_pkt)[r_ofs < 2 ? r_ofs : r_ofs - 2]), r_len);
191
192 if (len > 0)
193 {
194 r_len -= len;
195 r_ofs += len;
196
197 if (r_len == 0)
198 {
199 if (r_ofs == 2)
200 {
201 r_len = ntohs (*(u16 *)&((*r_pkt)[0]));
202 r_pkt->len = r_len;
203
204 if (r_len > 0 && r_len < MAXSIZE)
205 continue;
206 }
207 else
208 {
209 v.recv_vpn_packet (r_pkt, si);
210 delete r_pkt;
211 r_pkt = 0;
212
213 continue;
214 }
215 }
216
217 }
218 else if (len < 0 && (errno == EINTR || errno == EAGAIN))
219 return;
220
221 error ();
222 return;
223 }
224 }
225}
226
227bool
228tcp_connection::send_packet (vpn_packet *pkt, int tos)
229{
230 last_activity = NOW;
231
232 if (state == IDLE)
233 {
234 // woaw, the first lost packet ;)
235 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
236
237 if (fd >= 0)
238 {
239 fcntl (fd, F_SETFL, O_NONBLOCK);
240
241 if (connect (fd, si.sav4 (), si.salenv4 ()) >= 0
242 || errno == EINPROGRESS)
243 {
244 state = CONNECTING;
245 start (fd, POLLOUT);
246 }
247 else
248 close (fd);
249 }
250 }
251 else if (state == ESTABLISHED)
252 {
253 // how this maps to the underlying tcp packet we don't know
254 // and we don't care. at least we tried ;)
255 setsockopt (fd, SOL_IP, IP_TOS, &tos, sizeof tos);
256
257 // we use none of the advantages of tcp; if an error occurs, just drop
258 // (this happens when a tcp connection gets stuck, too, which might not be
259 // the wisest thing to do.. either drop packet (too late) or make sure
260 // it gets delivered)
261 u16 len = htons (pkt->len);
262
263 iovec vec[2];
264 vec[0].iov_base = &len;
265 vec[0].iov_len = sizeof len;
266 vec[1].iov_base = &((*pkt)[0]);
267 vec[1].iov_len = pkt->len;
268
269 if (sizeof (u16) + pkt->len != writev (fd, vec, 2))
270 error ();
271 }
272
273 return state != ERROR;
274}
275
276tcp_connection::tcp_connection (int fd_, const sockinfo &si_, vpn &v_) 445tcp_connection::tcp_connection (int fd_, const sockinfo &si_, vpn &v_)
277: v(v_), si(si_), io_watcher(this, &tcp_connection::tcpv4_ev) 446: v(v_), si(si_), io_watcher(this, &tcp_connection::tcpv4_ev)
278{ 447{
279 last_activity = NOW; 448 last_activity = NOW;
280 r_pkt = 0; 449 r_pkt = 0;
450 w_pkt = 0;
281 fd = fd_; 451 fd = fd_;
452#if ENABLE_HTTP_PROXY
453 proxy_req = 0;
454#endif
282 455
283 if (fd < 0) 456 if (fd < 0)
284 { 457 {
285 active = true; 458 active = true;
286 state = IDLE; 459 state = IDLE;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines