--- gvpe/src/vpn_tcp.C 2003/04/06 20:01:53 1.3 +++ gvpe/src/vpn_tcp.C 2004/01/17 01:18:36 1.11 @@ -1,5 +1,6 @@ /* vpn_tcp.C -- handle the tcp part of the protocol. + Copyright (C) 2003-2004 Marc Lehmann This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,22 +30,23 @@ #include #include -#include #include -#include #include -#include #include #include #include +#include #include -#include -#include -#include + +#include "netcompat.h" #include "vpn.h" +#if ENABLE_HTTP_PROXY +# include "conf.h" +#endif + struct tcp_connection; struct lt_sockinfo @@ -70,7 +72,7 @@ const sockinfo si; vpn &v; bool active; // this connection has been actively established - enum { ERROR, IDLE, CONNECTING, ESTABLISHED } state; + enum { ERROR, IDLE, CONNECTING, CONNECTING_PROXY, ESTABLISHED } state; vpn_packet *r_pkt; u32 r_len, r_ofs; @@ -78,6 +80,11 @@ vpn_packet *w_pkt; u32 w_len, w_ofs; +#if ENABLE_HTTP_PROXY + char *proxy_req; + int proxy_req_len; +#endif + void tcpv4_ev (io_watcher &w, short revents); bool send_packet (vpn_packet *pkt, int tos); @@ -96,7 +103,8 @@ void tcp_si_map::cleaner_cb (time_watcher &w) { - w.at = NOW + 600; + w.start (NOW + 600); + tstamp to = NOW - ::conf.keepalive - 30 - 60; for (iterator i = begin (); i != end(); ) @@ -112,7 +120,7 @@ void vpn::tcpv4_ev (io_watcher &w, short revents) { - if (revents & (POLLIN | POLLERR)) + if (revents & EVENT_READ) { struct sockaddr_in sa; socklen_t sa_len = sizeof (sa); @@ -152,21 +160,6 @@ return i->send_packet (pkt, tos); } -void tcp_connection::error () -{ - if (fd >= 0) - { - close (fd); - fd = -1; - } - - delete r_pkt; r_pkt = 0; - delete w_pkt; w_pkt = 0; - - stop (); - state = active ? IDLE : ERROR; -} - bool tcp_connection::write_packet () { @@ -177,9 +170,10 @@ u16 plen = htons (w_pkt->len); iovec vec[2]; - vec[0].iov_base = ((u8 *)&plen) + w_ofs; + //TODO: char* is the right type? hardly... + vec[0].iov_base = (char *)((u8 *)&plen) + w_ofs; vec[0].iov_len = 2 - w_ofs; - vec[1].iov_base = &((*w_pkt)[0]); + vec[1].iov_base = (char *)&((*w_pkt)[0]); vec[1].iov_len = w_len - 2; len = writev (fd, vec, 2); @@ -208,18 +202,26 @@ { last_activity = NOW; - if (revents & (POLLERR | POLLHUP)) - { - error (); - return; - } - - if (revents & POLLOUT) + if (revents & EVENT_WRITE) { if (state == CONNECTING) { state = ESTABLISHED; - set (POLLIN); + set (EVENT_READ); +#if ENABLE_HTTP_PROXY + if (::conf.proxy_host && ::conf.proxy_port) + { + state = CONNECTING_PROXY; + + if (write (fd, proxy_req, proxy_req_len) == 0) + { + error (); + return; + } + + free (proxy_req); proxy_req = 0; + } +#endif } else if (state == ESTABLISHED) { @@ -229,62 +231,114 @@ { delete w_pkt; w_pkt = 0; - set (POLLIN); + set (EVENT_READ); } } else - set (POLLIN); + set (EVENT_READ); } else - set (POLLIN); + set (EVENT_READ); } - if (revents & POLLIN) + if (revents & EVENT_READ) { - for (;;) - { - if (!r_pkt) - { - r_pkt = new vpn_packet; - r_ofs = 0; - r_len = 2; // header - } + if (state == ESTABLISHED) + for (;;) + { + if (!r_pkt) + { + r_pkt = new vpn_packet; + r_ofs = 0; + r_len = 2; // header + } + + ssize_t len = read (fd, &((*r_pkt)[r_ofs < 2 ? r_ofs : r_ofs - 2]), r_len); + + if (len > 0) + { + r_len -= len; + r_ofs += len; + + if (r_len == 0) + { + if (r_ofs == 2) + { + r_len = ntohs (*(u16 *)&((*r_pkt)[0])); + r_pkt->len = r_len; + + if (r_len > 0 && r_len < MAXSIZE) + continue; + } + else + { + v.recv_vpn_packet (r_pkt, si); + delete r_pkt; + r_pkt = 0; - ssize_t len = read (fd, &((*r_pkt)[r_ofs < 2 ? r_ofs : r_ofs - 2]), r_len); + continue; + } + } + else + break; + } + else if (len < 0 && (errno == EINTR || errno == EAGAIN)) + break; - if (len > 0) + // len == 0 <-> EOF + error (); + break; + } +#if ENABLE_HTTP_PROXY + else if (state == CONNECTING_PROXY) + { + fcntl (fd, F_SETFL, 0); + char r[1024]; + int i; + bool emptyline = false; + + // we do a blocking read of the response, to hell with it + for (i = 0; i < 1023; i++) { - r_len -= len; - r_ofs += len; + int l = read (fd, &r[i], 1); - if (r_len == 0) + if (l <= 0) { - if (r_ofs == 2) - { - r_len = ntohs (*(u16 *)&((*r_pkt)[0])); - r_pkt->len = r_len; + error (); + return; + } - if (r_len > 0 && r_len < MAXSIZE) - continue; - } + if (r[i] == '\012') + { + if (emptyline) + break; else - { - v.recv_vpn_packet (r_pkt, si); - delete r_pkt; - r_pkt = 0; - - continue; - } + emptyline = true; } - else - break; + else if (r[i] != '\015') + emptyline = false; } - else if (len < 0 && (errno == EINTR || errno == EAGAIN)) - break; - error (); - break; + fcntl (fd, F_SETFL, O_NONBLOCK); + + if (i < 12) + { + slog (L_ERR, _("(%s): unable to do proxy-forwarding, short response"), + (const char *)si); + error (); + } + else if (r[0] != 'H' || r[1] != 'T' || r[2] != 'T' || r[3] != 'P' || r[4] != '/' + || r[5] != '1' // http-major + || r[9] != '2') // response + { + slog (L_ERR, _("(%s): malformed or unexpected proxy response (%.12s)"), + (const char *)si, r); + error (); + } + else + state = ESTABLISHED; } +#endif } } @@ -300,13 +354,43 @@ if (fd >= 0) { - fcntl (fd, F_SETFL, O_NONBLOCK); + const sockinfo *csi = &si; + +#if ENABLE_HTTP_PROXY + sockinfo psi; + + if (::conf.proxy_host && ::conf.proxy_port) + { + psi.set (::conf.proxy_host, ::conf.proxy_port, PROT_TCPv4); + + if (psi.valid ()) + { + csi = ψ + + proxy_req_len = asprintf (&proxy_req, + "CONNECT %s:%d HTTP/1.0\015\012" + "%s%s%s" // optional proxy-auth + "\015\012", + si.ntoa (), + ntohs (si.port), + ::conf.proxy_auth ? "Proxy-Authorization: Basic " : "", + ::conf.proxy_auth ? ::conf.proxy_auth : "", + ::conf.proxy_auth ? "\015\012" : ""); + + } + else + slog (L_ERR, _("unable to resolve http proxy hostname '%s', trying direct"), + ::conf.proxy_host); + } +#endif - if (connect (fd, si.sav4 (), si.salenv4 ()) >= 0 + fcntl (fd, F_SETFL, O_NONBLOCK); + + if (connect (fd, csi->sav4 (), csi->salenv4 ()) >= 0 || errno == EINPROGRESS) { state = CONNECTING; - start (fd, POLLOUT); + start (fd, EVENT_WRITE); } else close (fd); @@ -320,7 +404,9 @@ { // how this maps to the underlying tcp packets we don't know // and we don't care. at least we tried ;) +#if defined(SOL_IP) && defined(IP_TOS) setsockopt (fd, SOL_IP, IP_TOS, &tos, sizeof tos); +#endif w_pkt = pkt; w_ofs = 0; @@ -333,7 +419,7 @@ w_pkt = new vpn_packet; w_pkt->set (*pkt); - set (POLLIN | POLLOUT); + set (EVENT_READ | EVENT_WRITE); } } } @@ -341,6 +427,24 @@ return state != ERROR; } +void tcp_connection::error () +{ + if (fd >= 0) + { + close (fd); + fd = -1; + } + + delete r_pkt; r_pkt = 0; + delete w_pkt; w_pkt = 0; +#if ENABLE_HTTP_PROXY + free (proxy_req); proxy_req = 0; +#endif + + stop (); + state = active ? IDLE : ERROR; +} + tcp_connection::tcp_connection (int fd_, const sockinfo &si_, vpn &v_) : v(v_), si(si_), io_watcher(this, &tcp_connection::tcpv4_ev) { @@ -348,6 +452,9 @@ r_pkt = 0; w_pkt = 0; fd = fd_; +#if ENABLE_HTTP_PROXY + proxy_req = 0; +#endif if (fd < 0) { @@ -358,7 +465,7 @@ { active = false; state = ESTABLISHED; - start (fd, POLLIN); + start (fd, EVENT_READ); } }