--- gvpe/src/vpn_tcp.C 2003/04/06 20:01:53 1.3 +++ gvpe/src/vpn_tcp.C 2003/10/14 15:48:15 1.8 @@ -31,9 +31,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -43,8 +41,14 @@ #include #include +#include "netcompat.h" + #include "vpn.h" +#if ENABLE_HTTP_PROXY +# include "conf.h" +#endif + struct tcp_connection; struct lt_sockinfo @@ -70,7 +74,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 +82,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); @@ -152,21 +161,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 +171,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); @@ -220,6 +215,14 @@ { state = ESTABLISHED; set (POLLIN); +#if ENABLE_HTTP_PROXY + if (::conf.proxy_host && ::conf.proxy_port) + { + state = CONNECTING_PROXY; + write (fd, proxy_req, proxy_req_len); + free (proxy_req); proxy_req = 0; + } +#endif } else if (state == ESTABLISHED) { @@ -241,50 +244,101 @@ if (revents & POLLIN) { - 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) + 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,9 +354,39 @@ 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; @@ -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; @@ -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) {