From 14076327364f5502f876043b7bd13ead747f50a9 Mon Sep 17 00:00:00 2001 From: visor Date: Tue, 11 Feb 2014 11:44:06 +0900 Subject: [PATCH] experimental. add https proxy. --- lib/http.cc | 16 +++---- lib/http.h | 3 +- lib/util_tcp.cc | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++--- lib/util_tcp.h | 24 ++++++++-- modules/ml-http.cc | 69 ++++++++++++++++++--------- 5 files changed, 206 insertions(+), 41 deletions(-) diff --git a/lib/http.cc b/lib/http.cc index 0f89fe3..6b84a5a 100644 --- a/lib/http.cc +++ b/lib/http.cc @@ -364,6 +364,7 @@ bool HTTPSend::submit (TcpClient& client, TcpBuf& buf, MlEnv* mlenv) { if (status != HTTP_INIT) return false; +#if 0 if (host.port == 0) { if (proto == uHttp) { host.port = 80; @@ -372,8 +373,9 @@ bool HTTPSend::submit (TcpClient& client, TcpBuf& buf, MlEnv* mlenv) { } } rc = client.connect (&host); +#endif - if (rc) { +// if (rc) { status = HTTP_OPEN; q = query (mlenv); client.write (&*q.begin (), q.length ()); @@ -405,9 +407,9 @@ bool HTTPSend::submit (TcpClient& client, TcpBuf& buf, MlEnv* mlenv) { } client.flush_write (); status = HTTP_QUERY; - } else { - return false; - } +// } else { +// return false; +// } return true; } @@ -519,11 +521,7 @@ ustring HTTPSend::query (MlEnv* mlenv) { } } q.append (CharConst (" HTTP/1.0" kCRLF)); - if (reqhost.length () > 0) { - makeHeader (q, ustring (CharConst ("Host")), reqhost); - } else { - makeHeader (q, ustring (CharConst ("Host")), host.host); - } + makeHeader (q, ustring (CharConst ("Host")), host.host); q.append (CharConst ("Accept: */*" kCRLF)); makeCookieHeader (q); if (id.length () > 0) { diff --git a/lib/http.h b/lib/http.h index a4cfa11..4695910 100644 --- a/lib/http.h +++ b/lib/http.h @@ -100,7 +100,8 @@ class HTTPSend { } method; ustring proto; HostSpec host; - ustring reqhost; +// ustring reqhost; + HostSpec conhost; ustring path; MNodePtr params; MNodePtr getparams; diff --git a/lib/util_tcp.cc b/lib/util_tcp.cc index d530729..5104834 100644 --- a/lib/util_tcp.cc +++ b/lib/util_tcp.cc @@ -1,6 +1,8 @@ #include "util_tcp.h" #include "config.h" +#include "http.h" #include "util_const.h" +#include "util_base64.h" #include "util_check.h" #include "util_string.h" #include "ustring.h" @@ -87,6 +89,18 @@ bool TcpBuf::getln (TcpClient& tc, ustring& ans) { return false; } +bool TcpBuf::getln2 (TcpClient& tc, ustring& ans) { + boost::match_results m; + static uregex re_crlf ("\\r\\n"); + + if (regex_search (start, tail, m, re_crlf, boost::regex_constants::match_single_line)) { + ans.assign (start, m[0].first); + start = m[0].second; + return true; + } + return false; +} + size_t TcpBuf::size () { return tail - start; } @@ -339,11 +353,11 @@ ssize_t TcpClient::read2 (void* buf, size_t nbytes) { } //============================================================ -bool SslClient::connect (const HostSpec* _host) { +bool SslClient::connect (const HostSpec* conhost, const HostSpec* _ephost) { bool rc; - host = _host; - rc = TcpClient::connect (host); + ephost = _ephost; + rc = TcpClient::connect (conhost); return rc; } @@ -378,6 +392,7 @@ bool SslClient::sslOpen () { return false; } if (fnoverify || verifyCA ()) { + sslmode = true; return true; } else { close (); @@ -410,8 +425,8 @@ bool SslClient::verifyCA () { if (!cert.get ()) return false; - lhost.resize (host->host.length ()); - std::transform (host->host.begin (), host->host.end (), lhost.begin (), ::tolower); + lhost.resize (ephost->host.length ()); + std::transform (ephost->host.begin (), ephost->host.end (), lhost.begin (), ::tolower); ans = verifyCN (lhost, cert.get ()) || verifyAltName (lhost, cert.get ()); if (! ans) { std::cerr << lhost << ": hostname not match\n"; @@ -488,10 +503,116 @@ bool SslClient::globMatch (const ustring& lhost, const ustring& name) { } ssize_t SslClient::write2 (struct iovec* iov, int iovcnt) { - return SSL_write(ssl.get (), iov->iov_base, iov->iov_len); + if (sslmode) { + return SSL_write(ssl.get (), iov->iov_base, iov->iov_len); + } else { + TcpClient::write2 (iov, iovcnt); + } } ssize_t SslClient::read2 (void* buf, size_t nbytes) { - return SSL_read (ssl.get (), buf, nbytes); + if (sslmode) { + return SSL_read (ssl.get (), buf, nbytes); + } else { + TcpClient::read2 (buf, nbytes); + } +} + +//============================================================ +bool ProxySslClient::connect2 () { + ustring msg; + + if (checkHostname (ephost->host)) { + msg.assign (CharConst ("CONNECT ")); + msg.append (ephost->host).append (uColon).append (to_ustring (ephost->port)).append (CharConst (" HTTP/1.0" kCRLF)); + msg.append (CharConst ("Host: ")).append (ephost->host).append (uCRLF); + // no User-Agent: + if (proxyid.length () > 0) { + ustring idpw; + idpw.assign (proxyid).append (uColon).append (proxypw); + msg.append (CharConst ("Proxy-Authorization: Basic ")).append (base64Encode (idpw.begin (), idpw.end ())).append (uCRLF); + } + msg.append (uCRLF); + write (&*msg.begin (), msg.length ()); // CONNECT HOST... + + int rc = readReplyHead (); + if (rc == 200) { + return SslClient::connect2 (); + } else { + } + } + return false; +} + +int ProxySslClient::readReplyHead () { + TcpBuf buf; + ustring line; + int responseCode = 0; + int rc; + umatch m; + static uregex re_crlf ("\\r\\n"); + + buf.tail += read3 (&*buf.start, buf.buf.length ()); + if (buf.getln2 (*this, line)) { + } else { + return 0; + } + + if (! match (line.substr (0, 5), CharConst ("HTTP/"))) { + return 0; // bad protocol + } else { + ustring::size_type p1 = line.find (' ', 0); + ustring::size_type p2 = line.find (' ', p1 + 1); + responseCode = strtoul (line.substr (p1 + 1, p2)); + while (rc = buf.getln2 (*this, line)) { + // XXX + if (line.empty ()) + break; + } + } + return responseCode; +} + +ssize_t ProxySslClient::read3 (void* buf, size_t nbytes) { + struct timeval delta; + fd_set rfds; + ssize_t rlen, total; + int r; + + FD_ZERO (&rfds); + total = 0; + + FD_SET (sd, &rfds); + delta.tv_sec = timeLimit; + delta.tv_usec = 0; + errno = 0; + r = select (sd + 1, &rfds, NULL, NULL, &delta); + if (r <= 0) + return total; + FD_CLR (sd, &rfds); + rlen = read2 (buf, nbytes); + if (rlen <= 0) + return total; + nbytes -= rlen; + buf = (char*)buf + rlen; + total += rlen; + + while (nbytes > 0) { + FD_SET (sd, &rfds); + delta.tv_sec = 0; + delta.tv_usec = 0; + errno = 0; + r = select (sd + 1, &rfds, NULL, NULL, &delta); + if (r <= 0) + return total; + FD_CLR (sd, &rfds); + rlen = read2 (buf, nbytes); + if (rlen <= 0) + return total; + nbytes -= rlen; + buf = (char*)buf + rlen; + total += rlen; + } + return total; } diff --git a/lib/util_tcp.h b/lib/util_tcp.h index c6a0c9f..d89bf2c 100644 --- a/lib/util_tcp.h +++ b/lib/util_tcp.h @@ -37,6 +37,7 @@ class TcpBuf { virtual bool empty (); virtual bool fill (TcpClient& tc); virtual bool getln (TcpClient& tc, ustring& ans); + virtual bool getln2 (TcpClient& tc, ustring& ans); virtual size_t size (); virtual void consume (); }; @@ -188,20 +189,22 @@ class X509_autoptr { class SslClient: public TcpClient { public: - const HostSpec* host; + const HostSpec* ephost; SSL_autoptr ssl; SSL_CTX_autoptr ssl_ctx; SSL_METHOD* ssl_meth; bool fnoverify; + bool sslmode; SslClient (bool _fnoverify = false) { - host = NULL; + ephost = NULL; ssl_meth = NULL; fnoverify = _fnoverify; + sslmode = false; }; virtual ~SslClient () {}; - virtual bool connect (const HostSpec* _host); + virtual bool connect (const HostSpec* conhost, const HostSpec* _ephost); protected: virtual bool connect2 (); virtual bool sslOpen (); @@ -214,4 +217,19 @@ class SslClient: public TcpClient { virtual ssize_t read2 (void* buf, size_t nbytes); }; +class HTTPSend; +class ProxySslClient: public SslClient { + public: + ustring proxyid; + ustring proxypw; + + ProxySslClient (bool _fnoverify = false): SslClient (_fnoverify) {}; + virtual ~ProxySslClient () {}; + + protected: + virtual bool connect2 (); + virtual int readReplyHead (); + virtual ssize_t read3 (void* buf, size_t nbytes); +}; + #endif /* UTIL_TCP_H */ diff --git a/modules/ml-http.cc b/modules/ml-http.cc index 392dc42..733a642 100644 --- a/modules/ml-http.cc +++ b/modules/ml-http.cc @@ -58,29 +58,33 @@ static void url_sub (const ustring& url, HTTPSend* http, bool noencode) { b = str.begin (); e = str.end (); if (usearch (b, e, m, re)) { + http->proto = ustring (m[1].first, m[1].second); + http->host.host = ustring (m[2].first, m[2].second); + if (m[4].matched) + http->host.port = to_uint32 (ustring (m[5].first, m[5].second)); + else + if (http->proto == uHttps) + http->host.port = 443; + else + http->host.port = 80; if (http->useproxy) { - if (match (m[1].first, m[1].second, uHttps)) { - throw (ustring (CharConst ("proxy connection of SSL protocol not implemented."))); + if (http->proto == uHttps) { +// throw (ustring (CharConst ("proxy connection of SSL protocol not implemented."))); + // XXX experimental https proxy connection. + if (noencode) + http->path = ustring (m[0].second - 1, e); + else + http->path = percentEncode_path (m[0].second - 1, e); } else { - http->proto = uHttp; - http->reqhost = ustring (m[2].first, m[2].second); if (noencode) { http->path = str; } else { http->path = ustring (b, m[0].second - 1); http->path.append (percentEncode_path (m[0].second - 1, e)); } - if (http->host.port == 0) - http->host.port = 80; } } else { - http->proto = ustring (m[1].first, m[1].second); - http->host.host = ustring (m[2].first, m[2].second); - http->reqhost = http->host.host; - if (m[4].matched) - http->host.port = to_uint32 (ustring (m[5].first, m[5].second)); - else - http->host.port = 0; + http->conhost = http->host; if (noencode) http->path = ustring (m[0].second - 1, e); else @@ -542,6 +546,7 @@ MNode* ml_http_get (MNode* cell, MlEnv* mlenv) { MNode* rest; MNodePtr ans; MNodePtr t; + int rc; static paramList kwlist[] = { {CharConst ("get"), true}, // 0 {CharConst ("post"), true}, // 1 @@ -629,10 +634,10 @@ MNode* ml_http_get (MNode* cell, MlEnv* mlenv) { if (evkw (14, t)) // header headerquery = t (); if (evkw (15, t)) { // proxy-host - obj.http->host.host = omitNonAsciiWord (to_string (t ())); + obj.http->conhost.host = omitNonAsciiWord (to_string (t ())); obj.http->useproxy = true; if (evkw (16, t)) // proxy-port - obj.http->host.port = to_uint32 (to_string (t ())); + obj.http->conhost.port = to_uint32 (to_string (t ())); if (evkw (17, t)) // proxy-id obj.http->proxyid = omitCtrl (to_string (t ())); if (evkw (18, t)) // proxy-password @@ -667,14 +672,36 @@ MNode* ml_http_get (MNode* cell, MlEnv* mlenv) { request_cookie (cookie (), obj.http); request_headerquery (headerquery (), obj.http); + rc = 0; if (obj.http->proto == uHttp) { - if (! obj.client) - obj.client = new TcpClient; - obj.http->submit (*obj.client, obj.buf, mlenv); + if (! obj.client) { + TcpClient* c = new TcpClient; + obj.client = c; + rc = c->connect (&obj.http->conhost); + } + if (rc) + obj.http->submit (*obj.client, obj.buf, mlenv); } else if (obj.http->proto == uHttps) { - if (! obj.client) - obj.client = new SslClient (fnoverify); - obj.http->submit (*obj.client, obj.buf, mlenv); + if (obj.http->useproxy) { + if (! obj.client) { + ProxySslClient* c = new ProxySslClient (fnoverify); + c->proxyid = obj.http->proxyid; + c->proxypw = obj.http->proxypw; + obj.http->proxyid = uEmpty; + obj.http->proxypw = uEmpty; + obj.client = c; + rc = c->connect (&obj.http->conhost, &obj.http->host); + } + obj.http->useproxy = false; + } else { + if (! obj.client) { + SslClient* c = new SslClient (fnoverify); + obj.client = c; + rc = c->connect (&obj.http->conhost, &obj.http->host); + } + } + if (rc) + obj.http->submit (*obj.client, obj.buf, mlenv); } else { throw (obj.http->proto + ": protocol not supported."); } -- 2.11.0