diff --git a/AddressBook.cpp b/AddressBook.cpp index 4dad5614..20a118fb 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -703,13 +703,13 @@ namespace client int dest_port = url.port ? url.port : 80; /* create http request & send it */ i2p::http::HTTPReq req; - req.add_header("Host", dest_host); - req.add_header("User-Agent", "Wget/1.11.4"); - req.add_header("Connection", "close"); + req.AddHeader("Host", dest_host); + req.AddHeader("User-Agent", "Wget/1.11.4"); + req.AddHeader("Connection", "close"); if (!m_Etag.empty()) - req.add_header("If-None-Match", m_Etag); + req.AddHeader("If-None-Match", m_Etag); if (!m_LastModified.empty()) - req.add_header("If-Modified-Since", m_LastModified); + req.AddHeader("If-Modified-Since", m_LastModified); /* convert url to relative */ url.schema = ""; url.host = ""; diff --git a/HTTP.cpp b/HTTP.cpp index 201a4e3d..6ab6ecf4 100644 --- a/HTTP.cpp +++ b/HTTP.cpp @@ -1,14 +1,15 @@ /* -* Copyright (c) 2013-2016, The PurpleI2P Project +* Copyright (c) 2013-2017, The PurpleI2P Project * * This file is part of Purple i2pd project and licensed under BSD3 * * See full license text in LICENSE file at top of project tree */ +#include +#include #include "util.h" #include "HTTP.h" -#include #include namespace i2p { @@ -43,18 +44,16 @@ namespace http { } } - bool parse_header_line(const std::string & line, std::map & headers) { + static std::pair parse_header_line(const std::string& line) + { std::size_t pos = 0; std::size_t len = 2; /* strlen(": ") */ std::size_t max = line.length(); if ((pos = line.find(": ", pos)) == std::string::npos) - return false; + return std::make_pair("", ""); while ((pos + len) < max && isspace(line.at(pos + len))) len++; - std::string name = line.substr(0, pos); - std::string value = line.substr(pos + len); - headers[name] = value; - return true; + return std::make_pair(line.substr(0, pos), line.substr(pos + len)); } void gen_rfc1123_date(std::string & out) { @@ -247,10 +246,15 @@ namespace http { uri = tokens[1]; version = tokens[2]; expect = HEADER_LINE; - } else { + } + else + { std::string line = str.substr(pos, eol - pos); - if (!parse_header_line(line, headers)) - return -1; + auto p = parse_header_line(line); + if (p.first.length () > 0) + headers.push_back (p); + else + return -1; } pos = eol + strlen(CRLF); if (pos >= eoh) @@ -259,12 +263,12 @@ namespace http { return eoh + strlen(HTTP_EOH); } - void HTTPReq::write(std::ostream & o) { - o << method << " " << uri << " " << version << CRLF; - for (auto & h : headers) { - o << h.first << ": " << h.second << CRLF; - } - o << CRLF; + void HTTPReq::write(std::ostream & o) + { + o << method << " " << uri << " " << version << CRLF; + for (auto & h : headers) + o << h.first << ": " << h.second << CRLF; + o << CRLF; } std::string HTTPReq::to_string() @@ -274,6 +278,40 @@ namespace http { return ss.str(); } + void HTTPReq::AddHeader (const std::string& name, const std::string& value) + { + headers.push_back (std::make_pair(name, value)); + } + + void HTTPReq::UpdateHeader (const std::string& name, const std::string& value) + { + for (auto& it : headers) + if (it.first == name) + { + it.second = value; + break; + } + } + + void HTTPReq::RemoveHeader (const std::string& name) + { + for (auto it = headers.begin (); it != headers.end ();) + { + if (!it->first.compare(0, name.length (), name)) + it = headers.erase (it); + else + it++; + } + } + + std::string HTTPReq::GetHeader (const std::string& name) const + { + for (auto& it : headers) + if (it.first == name) + return it.second; + return ""; + } + bool HTTPRes::is_chunked() { auto it = headers.find("Transfer-Encoding"); if (it == headers.end()) @@ -335,7 +373,10 @@ namespace http { expect = HEADER_LINE; } else { std::string line = str.substr(pos, eol - pos); - if (!parse_header_line(line, headers)) + auto p = parse_header_line(line); + if (p.first.length () > 0) + headers.insert (p); + else return -1; } pos = eol + strlen(CRLF); diff --git a/HTTP.h b/HTTP.h index 581e4a34..3175cb79 100644 --- a/HTTP.h +++ b/HTTP.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -54,7 +55,8 @@ namespace http { std::string to_string (); }; - struct HTTPMsg { + struct HTTPMsg + { std::map headers; void add_header(const char *name, std::string & value, bool replace = false); @@ -65,7 +67,9 @@ namespace http { long int content_length(); }; - struct HTTPReq : HTTPMsg { + struct HTTPReq + { + std::list > headers; std::string version; std::string method; std::string uri; @@ -82,9 +86,12 @@ namespace http { /** @brief Serialize HTTP request to string */ std::string to_string(); - - void write(std::ostream & o); + void write(std::ostream & o); + void AddHeader (const std::string& name, const std::string& value); + void UpdateHeader (const std::string& name, const std::string& value); + void RemoveHeader (const std::string& name); + std::string GetHeader (const std::string& name) const; }; struct HTTPRes : HTTPMsg { diff --git a/HTTPProxy.cpp b/HTTPProxy.cpp index 33a2a85c..e4d765ac 100644 --- a/HTTPProxy.cpp +++ b/HTTPProxy.cpp @@ -200,26 +200,16 @@ namespace proxy { void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req) { /* drop common headers */ - req.del_header("Referer"); - req.del_header("Via"); - req.del_header("Forwarded"); + req.RemoveHeader ("Referer"); + req.RemoveHeader("Via"); + req.RemoveHeader("Forwarded"); /* drop proxy-disclosing headers */ - std::vector toErase; - for (const auto& it : req.headers) { - if (it.first.compare(0, 12, "X-Forwarded-") == 0) { - toErase.push_back(it.first); - } else if (it.first.compare(0, 6, "Proxy-") == 0) { - toErase.push_back(it.first); - } else { - /* allow */ - } - } - for (const auto& header : toErase) { - req.headers.erase(header); - } + req.RemoveHeader("X-Forwarded"); + req.RemoveHeader("Proxy-"); /* replace headers */ - req.add_header("Connection", "close", true); /* keep-alive conns not supported yet */ - req.add_header("User-Agent", "MYOB/6.66 (AN/ON)", true); /* privacy */ + req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)"); + /* add headers */ + req.AddHeader("Connection", "close"); /* keep-alive conns not supported yet */ } /** @@ -263,29 +253,36 @@ namespace proxy { std::string dest_host = m_RequestURL.host; uint16_t dest_port = m_RequestURL.port; /* always set port, even if missing in request */ - if (!dest_port) { + if (!dest_port) dest_port = (m_RequestURL.schema == "https") ? 443 : 80; - } /* detect dest_host, set proper 'Host' header in upstream request */ - auto h = m_ClientRequest.headers.find("Host"); - if (dest_host != "") { + if (dest_host != "") + { /* absolute url, replace 'Host' header */ std::string h = dest_host; if (dest_port != 0 && dest_port != 80) h += ":" + std::to_string(dest_port); - m_ClientRequest.add_header("Host", h, true); - } else if (h != m_ClientRequest.headers.end()) { - /* relative url and 'Host' header provided. transparent proxy mode? */ - i2p::http::URL u; - std::string t = "http://" + h->second; - u.parse(t); - dest_host = u.host; - dest_port = u.port; - } else { - /* relative url and missing 'Host' header */ - GenericProxyError("Invalid request", "Can't detect destination host from request"); - return true; - } + m_ClientRequest.UpdateHeader("Host", h); + } + else + { + auto h = m_ClientRequest.GetHeader ("Host"); + if (h.length () > 0) + { + /* relative url and 'Host' header provided. transparent proxy mode? */ + i2p::http::URL u; + std::string t = "http://" + h; + u.parse(t); + dest_host = u.host; + dest_port = u.port; + } + else + { + /* relative url and missing 'Host' header */ + GenericProxyError("Invalid request", "Can't detect destination host from request"); + return true; + } + } /* check dest_host really exists and inside I2P network */ i2p::data::IdentHash identHash; diff --git a/HTTPServer.cpp b/HTTPServer.cpp index 1b4512eb..154dce5e 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -714,9 +714,11 @@ namespace http { return true; } /* method #2: 'Authorization' header sent */ - if (req.headers.count("Authorization") > 0) { + auto provided = req.GetHeader ("Authorization"); + if (provided.length () > 0) + { bool result = false; - std::string provided = req.headers.find("Authorization")->second; + std::string expected = user + ":" + pass; size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()) + 1; char * b64_creds = new char[b64_sz]; diff --git a/Reseed.cpp b/Reseed.cpp index 24df13f7..1766e1f6 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -517,8 +517,8 @@ namespace data LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port); i2p::http::HTTPReq req; req.uri = url.to_string(); - req.add_header("User-Agent", "Wget/1.11.4"); - req.add_header("Connection", "close"); + req.AddHeader("User-Agent", "Wget/1.11.4"); + req.AddHeader("Connection", "close"); s.write_some (boost::asio::buffer (req.to_string())); // read response std::stringstream rs;