=== modified file 'src/HttpRequest.cc' --- src/HttpRequest.cc 2014-04-27 07:59:17 +0000 +++ src/HttpRequest.cc 2014-04-27 13:20:32 +0000 @@ -75,41 +75,40 @@ HttpRequest::~HttpRequest() { clean(); debugs(93,7, HERE << "destructed, this=" << this); } void HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath) { method = aMethod; url.setScheme(aProtocol); urlpath = aUrlpath; } void HttpRequest::init() { method = Http::METHOD_NONE; url.clear(); urlpath = NULL; - login[0] = '\0'; host[0] = '\0'; host_is_numeric = -1; #if USE_AUTH auth_user_request = NULL; #endif port = 0; canonical = NULL; memset(&flags, '\0', sizeof(flags)); range = NULL; ims = -1; imslen = 0; lastmod = -1; client_addr.setEmpty(); my_addr.setEmpty(); body_pipe = NULL; // hier dnsWait = -1; errType = ERR_NONE; errDetail = ERR_DETAIL_NONE; peer_login = NULL; // not allocated/deallocated by this class @@ -190,41 +189,41 @@ void HttpRequest::reset() { clean(); init(); } HttpRequest * HttpRequest::clone() const { HttpRequest *copy = new HttpRequest(method, url.getScheme(), urlpath.termedBuf()); // TODO: move common cloning clone to Msg::copyTo() or copy ctor copy->header.append(&header); copy->hdrCacheInit(); copy->hdr_sz = hdr_sz; copy->http_ver = http_ver; copy->pstate = pstate; // TODO: should we assert a specific state here? copy->body_pipe = body_pipe; - strncpy(copy->login, login, sizeof(login)); // MAX_LOGIN_SZ + copy->url.userInfo(url.userInfo()); strncpy(copy->host, host, sizeof(host)); // SQUIDHOSTNAMELEN copy->host_addr = host_addr; copy->port = port; // urlPath handled in ctor copy->canonical = canonical ? xstrdup(canonical) : NULL; // range handled in hdrCacheInit() copy->ims = ims; copy->imslen = imslen; copy->hier = hier; // Is it safe to copy? Should we? copy->errType = errType; // XXX: what to do with copy->peer_login? copy->lastmod = lastmod; copy->etag = etag; copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL; // XXX: what to do with copy->peer_domain? === modified file 'src/HttpRequest.h' --- src/HttpRequest.h 2014-04-27 07:59:17 +0000 +++ src/HttpRequest.h 2014-04-27 08:49:32 +0000 @@ -121,43 +121,41 @@ /// Returns possibly nil history, creating it if icap logging is enabled Adaptation::Icap::History::Pointer icapHistory() const; #endif void recordLookup(const DnsLookupDetails &detail); /// sets error detail if no earlier detail was available void detailError(err_type aType, int aDetail); /// clear error details, useful for retries/repeats void clearError(); protected: void clean(); void init(); public: HttpRequestMethod method; // TODO expand to include all URI parts - URL url; ///< the request URI (scheme only) - - char login[MAX_LOGIN_SZ]; + URL url; ///< the request URI (scheme and userinfo only) private: char host[SQUIDHOSTNAMELEN]; int host_is_numeric; #if USE_ADAPTATION mutable Adaptation::History::Pointer adaptHistory_; ///< per-HTTP transaction info #endif #if ICAP_CLIENT mutable Adaptation::Icap::History::Pointer icapHistory_; ///< per-HTTP transaction info #endif public: Ip::Address host_addr; #if USE_AUTH Auth::UserRequest::Pointer auth_user_request; #endif unsigned short port; String urlpath; === modified file 'src/URL.h' --- src/URL.h 2014-04-27 07:59:17 +0000 +++ src/URL.h 2014-04-27 13:21:53 +0000 @@ -16,84 +16,88 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_SRC_URL_H #define SQUID_SRC_URL_H #include "anyp/UriScheme.h" #include "MemPool.h" +#include "SBuf.h" /** - \ingroup POD - * * The URL class represents a Uniform Resource Location */ class URL { public: MEMPROXY_CLASS(URL); URL() : scheme_() {} URL(AnyP::UriScheme const &aScheme) : scheme_(aScheme) {} void clear() { scheme_=AnyP::PROTO_NONE; } AnyP::UriScheme const & getScheme() const {return scheme_;} /// convert the URL scheme to that given void setScheme(const AnyP::ProtocolType &p) {scheme_=p;} + void userInfo(const SBuf &s) {userInfo_=s;} + const SBuf &userInfo(void) const {return userInfo_;} + private: /** \par * The scheme of this URL. This has the 'type code' smell about it. * In future we may want to make the methods that dispatch based on * the scheme virtual and have a class per protocol. \par * On the other hand, having Protocol as an explicit concept is useful, * see for instance the ACLProtocol acl type. One way to represent this * is to have one prototype URL with no host etc for each scheme, * another is to have an explicit scheme class, and then each URL class * could be a subclass of the scheme. Another way is one instance of * a AnyP::UriScheme class instance for each URL scheme we support, and one URL * class for each manner of treating the scheme : a Hierarchical URL, a * non-hierarchical URL etc. \par * Deferring the decision, its a type code for now. RBC 20060507. \par * In order to make taking any of these routes easy, scheme is private * and immutable, only settable at construction time, */ AnyP::UriScheme scheme_; + + SBuf userInfo_; // aka 'URL-login' }; MEMPROXY_CLASS_INLINE(URL); class HttpRequest; class HttpRequestMethod; AnyP::ProtocolType urlParseProtocol(const char *, const char *e = NULL); void urlInitialize(void); HttpRequest *urlParse(const HttpRequestMethod&, char *, HttpRequest *request = NULL); const char *urlCanonical(HttpRequest *); char *urlCanonicalClean(const HttpRequest *); const char *urlCanonicalFakeHttps(const HttpRequest * request); bool urlIsRelative(const char *); char *urlMakeAbsolute(const HttpRequest *, const char *); char *urlRInternal(const char *host, unsigned short port, const char *dir, const char *name); char *urlInternal(const char *dir, const char *name); int matchDomainName(const char *host, const char *domain); int urlCheckRequest(const HttpRequest *); int urlDefaultPort(AnyP::ProtocolType p); === modified file 'src/acl/UrlLogin.cc' --- src/acl/UrlLogin.cc 2013-10-25 00:13:46 +0000 +++ src/acl/UrlLogin.cc 2014-04-27 14:36:25 +0000 @@ -21,36 +21,36 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" #include "acl/Checklist.h" #include "acl/RegexData.h" #include "acl/UrlLogin.h" #include "HttpRequest.h" #include "rfc1738.h" int -ACLUrlLoginStrategy::match (ACLData * &data, ACLFilledChecklist *checklist, ACLFlags &) +ACLUrlLoginStrategy::match(ACLData * &data, ACLFilledChecklist *checklist, ACLFlags &) { - char *esc_buf = xstrdup(checklist->request->login); - rfc1738_unescape(esc_buf); - int result = data->match(esc_buf); - safe_free(esc_buf); + SBuf tmp = checklist->request->url.userInfo(); + // NOTE: unescape works in-place and may decrease the c-string length, never increase. + rfc1738_unescape(const_cast(tmp.c_str())); + int result = data->match(tmp.c_str()); return result; } ACLUrlLoginStrategy * ACLUrlLoginStrategy::Instance() { return &Instance_; } ACLUrlLoginStrategy ACLUrlLoginStrategy::Instance_; === modified file 'src/client_side.cc' --- src/client_side.cc 2014-04-27 07:59:17 +0000 +++ src/client_side.cc 2014-04-27 14:36:51 +0000 @@ -2718,43 +2718,40 @@ } else request->flags.spoofClientIp = false; } if (internalCheck(request->urlpath.termedBuf())) { if (internalHostnameIs(request->GetHost()) && request->port == getMyPort()) { debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->GetHost() << ':' << request->port); http->flags.internal = true; } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.termedBuf())) { debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->GetHost() << ':' << request->port << " (global_internal_static on)"); request->SetHost(internalHostname()); request->port = getMyPort(); http->flags.internal = true; } else debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->GetHost() << ':' << request->port << " (not this proxy)"); } - if (http->flags.internal) - request->login[0] = '\0'; - request->flags.internal = http->flags.internal; setLogUri (http, urlCanonicalClean(request.getRaw())); request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member. #if FOLLOW_X_FORWARDED_FOR // indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:) // not a details about teh TCP connection itself request->indirect_client_addr = conn->clientConnection->remote; #endif /* FOLLOW_X_FORWARDED_FOR */ request->my_addr = conn->clientConnection->local; request->myportname = conn->port->name; request->http_ver = http_ver; // Link this HttpRequest to ConnStateData relatively early so the following complex handling can use it // TODO: this effectively obsoletes a lot of conn->FOO copying. That needs cleaning up later. request->clientConnectionManager = conn; if (request->header.chunked()) { chunked = true; } else if (request->header.has(HDR_TRANSFER_ENCODING)) { const String te = request->header.getList(HDR_TRANSFER_ENCODING); === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2014-04-27 07:59:17 +0000 +++ src/client_side_request.cc 2014-04-27 10:11:28 +0000 @@ -1141,41 +1141,41 @@ http->range_iter.pos = request->range->begin(); http->range_iter.end = request->range->end(); http->range_iter.valid = true; } } /* Only HEAD and GET requests permit a Range or Request-Range header. * If these headers appear on any other type of request, delete them now. */ else { req_hdr->delById(HDR_RANGE); req_hdr->delById(HDR_REQUEST_RANGE); request->ignoreRange("neither HEAD nor GET"); } if (req_hdr->has(HDR_AUTHORIZATION)) request->flags.auth = true; clientCheckPinning(http); - if (request->login[0] != '\0') + if (!request->url.userInfo().isEmpty()) request->flags.auth = true; if (req_hdr->has(HDR_VIA)) { String s = req_hdr->getList(HDR_VIA); /* * ThisCache cannot be a member of Via header, "1.1 ThisCache" can. * Note ThisCache2 has a space prepended to the hostname so we don't * accidentally match super-domains. */ if (strListIsSubstr(&s, ThisCache2, ',')) { debugObj(33, 1, "WARNING: Forwarding loop detected for:\n", request, (ObjPackMethod) & httpRequestPack); request->flags.loopDetected = true; } #if USE_FORW_VIA_DB fvdbCountVia(s.termedBuf()); #endif === modified file 'src/ftp.cc' --- src/ftp.cc 2014-04-27 07:59:17 +0000 +++ src/ftp.cc 2014-04-27 12:50:27 +0000 @@ -43,41 +43,40 @@ #include "fde.h" #include "FwdState.h" #include "html_quote.h" #include "HttpHdrContRange.h" #include "HttpHeader.h" #include "HttpHeaderRange.h" #include "HttpReply.h" #include "HttpRequest.h" #include "ip/tools.h" #include "Mem.h" #include "MemBuf.h" #include "mime.h" #include "rfc1738.h" #include "Server.h" #include "SquidConfig.h" #include "SquidString.h" #include "SquidTime.h" #include "StatCounters.h" #include "Store.h" #include "tools.h" -#include "URL.h" #include "wordlist.h" #if USE_DELAY_POOLS #include "DelayPools.h" #include "MemObject.h" #endif #if HAVE_ERRNO_H #include #endif /** \defgroup ServerProtocolFTPInternal Server-Side FTP Internals \ingroup ServerProtocolFTPAPI */ /// \ingroup ServerProtocolFTPInternal static const char *const crlf = "\r\n"; #define CTRL_BUFLEN 1024 @@ -219,41 +218,40 @@ size_t offset; wordlist *message; char *last_command; char *last_reply; int replycode; } ctrl; /// FTP data channel info; the channel may be opened/closed a few times struct DataChannel: public FtpChannel { MemBuf *readBuf; char *host; unsigned short port; bool read_pending; } data; struct _ftp_flags flags; public: // these should all be private virtual void start(); - void loginParser(const char *, int escaped); int restartable(); void appendSuccessHeader(); void hackShortcut(FTPSM * nextState); void failed(err_type, int xerrno); void failedErrorMessage(err_type, int xerrno); void unhack(); void scheduleReadControlReply(int); void handleControlReply(); void readStor(); void parseListing(); MemBuf *htmlifyListEntry(const char *line); void completedListing(void); void dataComplete(); void dataRead(const CommIoCbParams &io); /// ignore timeout on CTRL channel. set read timeout on DATA channel. void switchTimeoutToDataChannel(); /// create a data channel acceptor and start listening. void listenForDataChannel(const Comm::ConnectionPointer &conn, const char *note); @@ -284,62 +282,57 @@ static HttpReply *ftpAuthRequired(HttpRequest * request, const char *realm); const char *ftpRealm(void); void loginFailed(void); static wordlist *ftpParseControlReply(char *, size_t, int *, size_t *); // sending of the request body to the server virtual void sentRequestBody(const CommIoCbParams&); virtual void doneSendingRequestBody(); virtual void haveParsedReplyHeaders(); virtual bool doneWithServer() const; virtual bool haveControlChannel(const char *caller_name) const; AsyncCall::Pointer dataCloser(); /// creates a Comm close callback AsyncCall::Pointer dataOpener(); /// creates a Comm connect callback private: // BodyConsumer for HTTP: consume request body. virtual void handleRequestBodyProducerAborted(); + void loginParser(const SBuf &login, bool escaped); CBDATA_CLASS2(FtpStateData); }; CBDATA_CLASS_INIT(FtpStateData); /// \ingroup ServerProtocolFTPInternal typedef struct { char type; int64_t size; char *date; char *name; char *showname; char *link; } ftpListParts; -/// \ingroup ServerProtocolFTPInternal -#define FTP_LOGIN_ESCAPED 1 - -/// \ingroup ServerProtocolFTPInternal -#define FTP_LOGIN_NOT_ESCAPED 0 - /* * State machine functions * send == state transition * read == wait for response, and select next state transition * other == Transition logic */ static FTPSM ftpReadWelcome; static FTPSM ftpSendUser; static FTPSM ftpReadUser; static FTPSM ftpSendPass; static FTPSM ftpReadPass; static FTPSM ftpSendType; static FTPSM ftpReadType; static FTPSM ftpSendMdtm; static FTPSM ftpReadMdtm; static FTPSM ftpSendSize; static FTPSM ftpReadSize; static FTPSM ftpSendEPRT; static FTPSM ftpReadEPRT; static FTPSM ftpSendPORT; @@ -539,92 +532,84 @@ safe_free(old_reply); safe_free(old_filepath); title_url.clean(); base_href.clean(); safe_free(filepath); safe_free(dirpath); safe_free(data.host); fwd = NULL; // refcounted } /** * Parse a possible login username:password pair. * Produces filled member variables user, password, password_url if anything found. + * + * \param login a decoded Basic authentication credential token or URI user-info token. + * \param escaped whether to URL-decode the token after extracting user and password */ void -FtpStateData::loginParser(const char *login, int escaped) +FtpStateData::loginParser(const SBuf &login, bool escaped) { - const char *u = NULL; // end of the username sub-string - int len; // length of the current sub-string to handle. + debugs(9, 4, "login=" << login << ", escaped=" << escaped); + debugs(9, 9, "IN : login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password); - int total_len = strlen(login); + if (login.isEmpty()) + return; - debugs(9, 4, HERE << ": login='" << login << "', escaped=" << escaped); - debugs(9, 9, HERE << ": IN : login='" << login << "', escaped=" << escaped << ", user=" << user << ", password=" << password); - - if ((u = strchr(login, ':'))) { - - /* if there was a username part */ - if (u > login) { - len = u - login; - ++u; // jump off the delimiter. - if (len > MAX_URL) - len = MAX_URL-1; - xstrncpy(user, login, len +1); - debugs(9, 9, HERE << ": found user='" << user << "'(" << len <<"), escaped=" << escaped); - if (escaped) - rfc1738_unescape(user); - debugs(9, 9, HERE << ": found user='" << user << "'(" << len <<") unescaped."); - } + const SBuf::size_type colonPos = login.find(':'); - /* if there was a password part */ - len = login + total_len - u; - if ( len > 0) { - if (len > MAX_URL) - len = MAX_URL -1; - xstrncpy(password, u, len +1); - debugs(9, 9, HERE << ": found password='" << password << "'(" << len <<"), escaped=" << escaped); - if (escaped) { - rfc1738_unescape(password); - password_url = 1; - } - debugs(9, 9, HERE << ": found password='" << password << "'(" << len <<") unescaped."); - } - } else if (login[0]) { - /* no password, just username */ - if (total_len > MAX_URL) - total_len = MAX_URL -1; - xstrncpy(user, login, total_len +1); - debugs(9, 9, HERE << ": found user='" << user << "'(" << total_len <<"), escaped=" << escaped); + /* if there was a username part */ + if (colonPos != 0) { + SBuf userName = login.substr(0, colonPos); + if (userName.length() >= sizeof(user)) + userName.chop(0, sizeof(user)-1); + memcpy(user, userName.rawContent(), userName.length()); + user[userName.length()] = '\0'; + debugs(9, 9, "found user=" << userName << " (" << userName.length() << "), escaped=" << escaped); if (escaped) rfc1738_unescape(user); - debugs(9, 9, HERE << ": found user='" << user << "'(" << total_len <<") unescaped."); + debugs(9, 9, "found user=" << user << " (" << strlen(user) << ") unescaped."); + } + + /* if there was a password part */ + if (colonPos != SBuf::npos) { + SBuf pass = login.substr(colonPos+1, SBuf::npos); + if (pass.length() >= sizeof(pass)) + pass.chop(0, sizeof(pass)-1); + memcpy(password, pass.rawContent(), pass.length()); + password[pass.length()] = '\0'; + debugs(9, 9, "found password=" << pass << " (" << pass.length() << "), escaped=" << escaped); + if (escaped) { + rfc1738_unescape(password); + password_url = 1; + } + debugs(9, 9, "found password=" << password << " (" << strlen(password) << ") unescaped."); } - debugs(9, 9, HERE << ": OUT: login='" << login << "', escaped=" << escaped << ", user=" << user << ", password=" << password); + debugs(9, 9, "OUT: login=" << login << ", escaped=" << escaped << ", user=" << user << ", password=" << password); } /** * Cancel the timeout on the Control socket and establish one * on the data socket */ void FtpStateData::switchTimeoutToDataChannel() { commUnsetConnTimeout(ctrl.conn); typedef CommCbMemFunT TimeoutDialer; AsyncCall::Pointer timeoutCall = JobCallback(9, 5, TimeoutDialer, this, FtpStateData::ftpTimeout); commSetConnTimeout(data.conn, Config.Timeout.read, timeoutCall); } void FtpStateData::listenForDataChannel(const Comm::ConnectionPointer &conn, const char *note) { assert(!Comm::IsConnOpen(data.conn)); @@ -1359,50 +1344,50 @@ * - Checks URL (ftp://user:pass@domain) * - Authorization: Basic header * - squid.conf anonymous-FTP settings (default: anonymous:Squid@). * * Special Case: A username-only may be provided in the URL and password in the HTTP headers. * * TODO: we might be able to do something about locating username from other sources: * ie, external ACL user=* tag or ident lookup * \retval 1 if we have everything needed to complete this request. \retval 0 if something is missing. */ int FtpStateData::checkAuth(const HttpHeader * req_hdr) { /* default username */ xstrncpy(user, "anonymous", MAX_URL); #if HAVE_AUTH_MODULE_BASIC /* Check HTTP Authorization: headers (better than defaults, but less than URL) */ - const char *auth; - if ( (auth = req_hdr->getAuth(HDR_AUTHORIZATION, "Basic")) ) { + const SBuf auth(req_hdr->getAuth(HDR_AUTHORIZATION, "Basic")); + if (!auth.isEmpty()) { flags.authenticated = 1; - loginParser(auth, FTP_LOGIN_NOT_ESCAPED); + loginParser(auth, false); } /* we fail with authorization-required error later IFF the FTP server requests it */ #endif /* Test URL login syntax. Overrides any headers received. */ - loginParser(request->login, FTP_LOGIN_ESCAPED); + loginParser(request->url.userInfo(), true); /* name is missing. thats fatal. */ if (!user[0]) fatal("FTP login parsing destroyed username info"); /* name + password == success */ if (password[0]) return 1; /* Setup default FTP password settings */ /* this has to be done last so that we can have a no-password case above. */ if (!password[0]) { if (strcmp(user, "anonymous") == 0 && !flags.tried_auth_anonymous) { xstrncpy(password, Config.Ftp.anon_user, MAX_URL); flags.tried_auth_anonymous=1; return 1; } else if (!flags.tried_auth_nopass) { xstrncpy(password, null_string, MAX_URL); flags.tried_auth_nopass=1; return 1; === modified file 'src/http.cc' --- src/http.cc 2014-04-27 07:59:17 +0000 +++ src/http.cc 2014-04-27 13:17:58 +0000 @@ -1802,43 +1802,44 @@ hdr_out->putStr(HDR_X_FORWARDED_FOR, strFwd.termedBuf()); } /** If set to DELETE - do not copy through. */ /* append Host if not there already */ if (!hdr_out->has(HDR_HOST)) { if (request->peer_domain) { hdr_out->putStr(HDR_HOST, request->peer_domain); } else if (request->port == urlDefaultPort(request->url.getScheme())) { /* use port# only if not default */ hdr_out->putStr(HDR_HOST, request->GetHost()); } else { httpHeaderPutStrf(hdr_out, HDR_HOST, "%s:%d", request->GetHost(), (int) request->port); } } /* append Authorization if known in URL, not in header and going direct */ if (!hdr_out->has(HDR_AUTHORIZATION)) { - if (!request->flags.proxying && request->login[0] != '\0') { - httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", - old_base64_encode(request->login)); + if (!request->flags.proxying && !request->url.userInfo().isEmpty()) { + static char result[MAX_URL*2]; // should be big enough for a single URI segment + if (base64_encode_str(result, sizeof(result)-1, request->url.userInfo().rawContent(), request->url.userInfo().length()) > 0) + httpHeaderPutStrf(hdr_out, HDR_AUTHORIZATION, "Basic %s", result); } } /* Fixup (Proxy-)Authorization special cases. Plain relaying dealt with above */ httpFixupAuthentication(request, hdr_in, hdr_out, flags); /* append Cache-Control, add max-age if not there already */ { HttpHdrCc *cc = hdr_in->getCc(); if (!cc) cc = new HttpHdrCc(); #if 0 /* see bug 2330 */ /* Set no-cache if determined needed but not found */ if (request->flags.nocache) EBIT_SET(cc->mask, CC_NO_CACHE); #endif /* Add max-age only without no-cache */ === modified file 'src/url.cc' --- src/url.cc 2014-04-27 07:59:17 +0000 +++ src/url.cc 2014-04-27 14:14:27 +0000 @@ -26,41 +26,41 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" #include "globals.h" #include "HttpRequest.h" #include "rfc1738.h" #include "SquidConfig.h" #include "SquidString.h" #include "URL.h" static HttpRequest *urlParseFinish(const HttpRequestMethod& method, const AnyP::ProtocolType protocol, const char *const urlpath, const char *const host, - const char *const login, + const SBuf &login, const int port, HttpRequest *request); static HttpRequest *urnParse(const HttpRequestMethod& method, char *urn, HttpRequest *request); static const char valid_hostname_chars_u[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789-._" "[:]" ; static const char valid_hostname_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789-." "[:]" ; void urlInitialize(void) { debugs(23, 5, "urlInitialize: Initializing..."); @@ -224,41 +224,41 @@ char *dst; proto[0] = host[0] = urlpath[0] = login[0] = '\0'; if ((l = strlen(url)) + Config.appendDomainLen > (MAX_URL - 1)) { /* terminate so it doesn't overflow other buffers */ *(url + (MAX_URL >> 1)) = '\0'; debugs(23, DBG_IMPORTANT, "urlParse: URL too large (" << l << " bytes)"); return NULL; } if (method == Http::METHOD_CONNECT) { port = CONNECT_PORT; if (sscanf(url, "[%[^]]]:%d", host, &port) < 1) if (sscanf(url, "%[^:]:%d", host, &port) < 1) return NULL; } else if ((method == Http::METHOD_OPTIONS || method == Http::METHOD_TRACE) && strcmp(url, "*") == 0) { protocol = AnyP::PROTO_HTTP; port = urlDefaultPort(protocol); - return urlParseFinish(method, protocol, url, host, login, port, request); + return urlParseFinish(method, protocol, url, host, SBuf(), port, request); } else if (!strncmp(url, "urn:", 4)) { return urnParse(method, url, request); } else { /* Parse the URL: */ src = url; i = 0; /* Find first : - everything before is protocol */ for (i = 0, dst = proto; i < l && *src != ':'; ++i, ++src, ++dst) { *dst = *src; } if (i >= l) return NULL; *dst = '\0'; /* Then its :// */ if ((i+3) > l || *src != ':' || *(src + 1) != '/' || *(src + 2) != '/') return NULL; i += 3; src += 3; @@ -429,66 +429,66 @@ break; case URI_WHITESPACE_CHOP: *(urlpath + strcspn(urlpath, w_space)) = '\0'; break; case URI_WHITESPACE_STRIP: default: t = q = urlpath; while (*t) { if (!xisspace(*t)) { *q = *t; ++q; } ++t; } *q = '\0'; } } - return urlParseFinish(method, protocol, urlpath, host, login, port, request); + return urlParseFinish(method, protocol, urlpath, host, SBuf(login), port, request); } /** * Update request with parsed URI data. If the request arg is * non-NULL, put parsed values there instead of allocating a new * HttpRequest. */ static HttpRequest * urlParseFinish(const HttpRequestMethod& method, const AnyP::ProtocolType protocol, const char *const urlpath, const char *const host, - const char *const login, + const SBuf &login, const int port, HttpRequest *request) { if (NULL == request) request = new HttpRequest(method, protocol, urlpath); else { request->initHTTP(method, protocol, urlpath); safe_free(request->canonical); } request->SetHost(host); - xstrncpy(request->login, login, MAX_LOGIN_SZ); + request->url.userInfo(login); request->port = (unsigned short) port; return request; } static HttpRequest * urnParse(const HttpRequestMethod& method, char *urn, HttpRequest *request) { debugs(50, 5, "urnParse: " << urn); if (request) { request->initHTTP(method, AnyP::PROTO_URN, urn + 4); safe_free(request->canonical); return request; } return new HttpRequest(method, AnyP::PROTO_URN, urn + 4); } const char * urlCanonical(HttpRequest * request) { @@ -498,93 +498,93 @@ if (request->canonical) return request->canonical; if (request->url.getScheme() == AnyP::PROTO_URN) { snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH, SQUIDSTRINGPRINT(request->urlpath)); } else { switch (request->method.id()) { case Http::METHOD_CONNECT: snprintf(urlbuf, MAX_URL, "%s:%d", request->GetHost(), request->port); break; default: { portbuf[0] = '\0'; if (request->port != urlDefaultPort(request->url.getScheme())) snprintf(portbuf, 32, ":%d", request->port); - snprintf(urlbuf, MAX_URL, "%s://%s%s%s%s" SQUIDSTRINGPH, + snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s%s%s" SQUIDSTRINGPH, request->url.getScheme().c_str(), - request->login, - *request->login ? "@" : null_string, + SQUIDSBUFPRINT(request->url.userInfo()), + !request->url.userInfo().isEmpty() ? "@" : "", request->GetHost(), portbuf, SQUIDSTRINGPRINT(request->urlpath)); } } } return (request->canonical = xstrdup(urlbuf)); } /** \todo AYJ: Performance: This is an *almost* duplicate of urlCanonical. But elides the query-string. * After copying it on in the first place! Would be less code to merge the two with a flag parameter. * and never copy the query-string part in the first place */ char * urlCanonicalClean(const HttpRequest * request) { LOCAL_ARRAY(char, buf, MAX_URL); LOCAL_ARRAY(char, portbuf, 32); LOCAL_ARRAY(char, loginbuf, MAX_LOGIN_SZ + 1); char *t; if (request->url.getScheme() == AnyP::PROTO_URN) { snprintf(buf, MAX_URL, "urn:" SQUIDSTRINGPH, SQUIDSTRINGPRINT(request->urlpath)); } else { switch (request->method.id()) { case Http::METHOD_CONNECT: snprintf(buf, MAX_URL, "%s:%d", request->GetHost(), request->port); break; default: { portbuf[0] = '\0'; if (request->port != urlDefaultPort(request->url.getScheme())) snprintf(portbuf, 32, ":%d", request->port); - loginbuf[0] = '\0'; - - if ((int) strlen(request->login) > 0) { - strcpy(loginbuf, request->login); - - if ((t = strchr(loginbuf, ':'))) - *t = '\0'; - - strcat(loginbuf, "@"); - } + if (!request->url.userInfo().isEmpty()) { + SBuf::size_type len = request->url.userInfo().find(':'); + if (len == SBuf::npos) + len = request->url.userInfo().length(); + + memcpy(loginbuf, request->url.userInfo().rawContent(), len); + loginbuf[len] = '@'; + loginbuf[len+1] = '\0'; + } else + loginbuf[0] = '\0'; snprintf(buf, MAX_URL, "%s://%s%s%s" SQUIDSTRINGPH, request->url.getScheme().c_str(), loginbuf, request->GetHost(), portbuf, SQUIDSTRINGPRINT(request->urlpath)); // strip arguments AFTER a question-mark if (Config.onoff.strip_query_terms) if ((t = strchr(buf, '?'))) *(++t) = '\0'; } } } if (stringHasCntl(buf)) xstrncpy(buf, rfc1738_escape_unescaped(buf), MAX_URL); return buf; @@ -652,52 +652,52 @@ */ char * urlMakeAbsolute(const HttpRequest * req, const char *relUrl) { if (req->method.id() == Http::METHOD_CONNECT) { return (NULL); } char *urlbuf = (char *)xmalloc(MAX_URL * sizeof(char)); if (req->url.getScheme() == AnyP::PROTO_URN) { snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH, SQUIDSTRINGPRINT(req->urlpath)); return (urlbuf); } size_t urllen; if (req->port != urlDefaultPort(req->url.getScheme())) { - urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s:%d", + urllen = snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s%s:%d", req->url.getScheme().c_str(), - req->login, - *req->login ? "@" : null_string, + SQUIDSBUFPRINT(req->url.userInfo()), + !req->url.userInfo().isEmpty() ? "@" : "", req->GetHost(), req->port ); } else { - urllen = snprintf(urlbuf, MAX_URL, "%s://%s%s%s", + urllen = snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s%s", req->url.getScheme().c_str(), - req->login, - *req->login ? "@" : null_string, + SQUIDSBUFPRINT(req->url.userInfo()), + !req->url.userInfo().isEmpty() ? "@" : "", req->GetHost() ); } if (relUrl[0] == '/') { strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1); } else { const char *path = req->urlpath.termedBuf(); const char *last_slash = strrchr(path, '/'); if (last_slash == NULL) { urlbuf[urllen] = '/'; ++urllen; strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1); } else { ++last_slash; size_t pathlen = last_slash - path; if (pathlen > MAX_URL - urllen - 1) { pathlen = MAX_URL - urllen - 1; }