--- gvpe/src/vpn_tcp.C 2003/04/05 02:32:40 1.1 +++ gvpe/src/vpn_tcp.C 2003/10/14 03:22:09 1.7 @@ -45,6 +45,10 @@ #include "vpn.h" +#if ENABLE_HTTP_PROXY +# include "conf.h" +#endif + struct tcp_connection; struct lt_sockinfo @@ -70,14 +74,23 @@ 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; + 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); - void send_packet (vpn_packet *pkt, int tos); + bool send_packet (vpn_packet *pkt, int tos); + bool write_packet (); void error (); // abort conenction && cleanup @@ -130,7 +143,7 @@ } } -void +bool vpn::send_tcpv4_packet (vpn_packet *pkt, const sockinfo &si, int tos) { tcp_si_map::iterator info = tcp_si.find (&si); @@ -145,22 +158,44 @@ else i = info->second; - i->send_packet (pkt, tos); + return i->send_packet (pkt, tos); } -void tcp_connection::error () +bool +tcp_connection::write_packet () { - if (fd >= 0) + ssize_t len; + + if (w_ofs < 2) { - close (fd); - fd = -1; + u16 plen = htons (w_pkt->len); + + iovec vec[2]; + //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 = (char *)&((*w_pkt)[0]); + vec[1].iov_len = w_len - 2; + + len = writev (fd, vec, 2); } + else + len = write (fd, &((*w_pkt)[w_ofs - 2]), w_len); - delete r_pkt; - r_pkt = 0; + if (len > 0) + { + w_ofs += len; + w_len -= len; - stop (); - state = active ? IDLE : ERROR; + return w_len == 0; + } + else if (len < 0 && (errno == EAGAIN || errno == EINTR)) + return false; + else + { + error (); + return false; + } } void @@ -169,62 +204,145 @@ last_activity = NOW; if (revents & (POLLERR | POLLHUP)) - error (); - else if (revents & POLLOUT && state == CONNECTING) { - state = ESTABLISHED; - set (POLLIN); + error (); + return; } - else if (revents & POLLIN) + if (revents & POLLOUT) { - for (;;) + if (state == CONNECTING) + { + 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) { - if (!r_pkt) + if (w_pkt) { - r_pkt = new vpn_packet; - r_ofs = 0; - r_len = 2; // header + if (write_packet ()) + { + delete w_pkt; w_pkt = 0; + + set (POLLIN); + } } + else + set (POLLIN); + } + else + set (POLLIN); + } + + if (revents & POLLIN) + { + 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; + + error (); + break; + } +#if ENABLE_HTTP_PROXY + else if (state == CONNECTING_PROXY) + { + fcntl (fd, F_SETFL, 0); + char r[1024]; + int i; + bool emptyline = false; - if (len > 0) + // 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 if (r[i] != '\015') + emptyline = false; } - else if (len < 0 && (errno == EINTR || errno == EAGAIN)) - return; - error (); - return; + 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 } } -void +bool tcp_connection::send_packet (vpn_packet *pkt, int tos) { last_activity = NOW; @@ -236,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; @@ -250,25 +398,51 @@ } else if (state == ESTABLISHED) { - // how this maps to the underlying tcp packet we don't know - // and we don't care. at least we tried ;) - setsockopt (fd, SOL_IP, IP_TOS, &tos, sizeof tos); - - // we use none of the advantages of tcp; if an error occurs, just drop - // (this happens when a tcp connection gets stuck, too, which might not be - // the wisest thing to do.. either drop packet (too late) or make sure - // it gets delivered) - u16 len = htons (pkt->len); + // drop packet if the tcp write buffer is full. this *is* the + // right thing to do, not using tcp *is* the right thing to do. + if (!w_pkt) + { + // 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; + w_len = pkt->len + 2; // length + size header - iovec vec[2]; - vec[0].iov_base = &len; - vec[0].iov_len = sizeof len; - vec[1].iov_base = &((*pkt)[0]); - vec[1].iov_len = pkt->len; - - if (sizeof (u16) + pkt->len != writev (fd, vec, 2)) - error (); + if (write_packet ()) + w_pkt = 0; + else + { + w_pkt = new vpn_packet; + w_pkt->set (*pkt); + + set (POLLIN | POLLOUT); + } + } + } + + 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_) @@ -276,7 +450,11 @@ { last_activity = NOW; r_pkt = 0; + w_pkt = 0; fd = fd_; +#if ENABLE_HTTP_PROXY + proxy_req = 0; +#endif if (fd < 0) {