]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/urn.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / urn.cc
index 59e61d4a528fa3f992f4111b1cc74abaa85a0f6c..cd794bdfca8ea5667293b4274f9c89835800f8aa 100644 (file)
@@ -1,36 +1,17 @@
 /*
- * DEBUG: section 52    URN Parsing
- * AUTHOR: Kostas Anagnostakis
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  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.
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
  *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
+/* DEBUG: section 52    URN Parsing */
+
 #include "squid.h"
+#include "AccessLogEntry.h"
+#include "acl/FilledChecklist.h"
+#include "base/TextException.h"
 #include "cbdata.h"
 #include "errorpage.h"
 #include "FwdState.h"
 #include "Store.h"
 #include "StoreClient.h"
 #include "tools.h"
-#include "URL.h"
 #include "urn.h"
 
-#define        URN_REQBUF_SZ   4096
+#define URN_REQBUF_SZ   4096
 
 class UrnState : public StoreClient
 {
+    CBDATA_CLASS(UrnState);
 
 public:
+    explicit UrnState(const AccessLogEntry::Pointer &anAle): ale(anAle) {}
+
     void created (StoreEntry *newEntry);
     void start (HttpRequest *, StoreEntry *);
-    char *getHost (String &urlpath);
     void setUriResFromRequest(HttpRequest *);
-    bool RequestNeedsMenu(HttpRequest *r);
-    void updateRequestURL(HttpRequest *r, char const *newPath, const size_t newPath_len);
-    void createUriResRequest (String &uri);
 
     virtual ~UrnState();
 
-    StoreEntry *entry;
-    store_client *sc;
-    StoreEntry *urlres_e;
+    StoreEntry *entry = nullptr;
+    store_client *sc = nullptr;
+    StoreEntry *urlres_e = nullptr;
     HttpRequest::Pointer request;
     HttpRequest::Pointer urlres_r;
+    AccessLogEntry::Pointer ale; ///< details of the requesting transaction
 
-    struct {
-        bool force_menu;
-    } flags;
-    char reqbuf[URN_REQBUF_SZ];
-    int reqofs;
+    char reqbuf[URN_REQBUF_SZ] = { '\0' };
+    int reqofs = 0;
 
 private:
-    char *urlres;
+    /* StoreClient API */
+    virtual LogTags *loggingTags() { return ale ? &ale->cache.code : nullptr; }
+    virtual void fillChecklist(ACLFilledChecklist &) const;
 
-    CBDATA_CLASS2(UrnState);
+    char *urlres = nullptr;
 };
 
 typedef struct {
@@ -95,17 +74,27 @@ typedef struct {
 static STCB urnHandleReply;
 static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
 static const char *const crlf = "\r\n";
-static QS url_entry_sort;
 
 CBDATA_CLASS_INIT(UrnState);
 
 UrnState::~UrnState()
 {
-    safe_free(urlres);
+    SWALLOW_EXCEPTIONS({
+        if (urlres_e) {
+            if (sc)
+                storeUnregister(sc, urlres_e, this);
+            urlres_e->unlock("~UrnState+res");
+        }
+
+        if (entry)
+            entry->unlock("~UrnState+prime");
+
+        safe_free(urlres);
+    });
 }
 
 static url_entry *
-urnFindMinRtt(url_entry * urls, const HttpRequestMethod& m, int *rtt_ret)
+urnFindMinRtt(url_entry * urls, const HttpRequestMethod &, int *rtt_ret)
 {
     int min_rtt = 0;
     url_entry *u = NULL;
@@ -150,72 +139,27 @@ urnFindMinRtt(url_entry * urls, const HttpRequestMethod& m, int *rtt_ret)
     return min_u;
 }
 
-char *
-UrnState::getHost (String &urlpath)
-{
-    char * result;
-    size_t p;
-
-    /** FIXME: this appears to be parsing the URL. *very* badly. */
-    /*   a proper encapsulated URI/URL type needs to clear this up. */
-    if ((p=urlpath.find(':')) != String::npos) {
-        result=xstrndup(urlpath.rawBuf(),p-1);
-    } else {
-        result = xstrndup(urlpath.rawBuf(),urlpath.size());
-    }
-    return result;
-}
-
-bool
-UrnState::RequestNeedsMenu(HttpRequest *r)
-{
-    if (r->urlpath.size() < 5)
-        return false;
-    //now we're sure it's long enough
-    return strncasecmp(r->urlpath.rawBuf(), "menu.", 5) == 0;
-}
-
-void
-UrnState::updateRequestURL(HttpRequest *r, char const *newPath, const size_t newPath_len)
-{
-    char *new_path = xstrndup (newPath, newPath_len);
-    r->urlpath = new_path;
-    xfree(new_path);
-}
-
-void
-UrnState::createUriResRequest (String &uri)
-{
-    LOCAL_ARRAY(char, local_urlres, 4096);
-    char *host = getHost (uri);
-    snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?urn:" SQUIDSTRINGPH,
-             host, SQUIDSTRINGPRINT(uri));
-    safe_free (host);
-    safe_free (urlres);
-    urlres = xstrdup (local_urlres);
-    urlres_r = HttpRequest::CreateFromUrl(urlres);
-}
-
 void
 UrnState::setUriResFromRequest(HttpRequest *r)
 {
-    if (RequestNeedsMenu(r)) {
-        updateRequestURL(r, r->urlpath.rawBuf() + 5, r->urlpath.size() - 5 );
-        flags.force_menu = true;
-    }
-
-    createUriResRequest (r->urlpath);
+    const auto &query = r->url.absolute();
+    const auto host = r->url.host();
+    // TODO: use class AnyP::Uri instead of generating a string and re-parsing
+    LOCAL_ARRAY(char, local_urlres, 4096);
+    snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?" SQUIDSBUFPH, host, SQUIDSBUFPRINT(query));
+    safe_free(urlres);
+    urlres_r = HttpRequest::FromUrlXXX(local_urlres, r->masterXaction);
 
-    if (urlres_r == NULL) {
-        debugs(52, 3, "urnStart: Bad uri-res URL " << urlres);
-        ErrorState *err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r);
-        err->url = urlres;
-        urlres = NULL;
+    if (!urlres_r) {
+        debugs(52, 3, "Bad uri-res URL " << local_urlres);
+        const auto err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r, ale);
+        err->url = xstrdup(local_urlres);
         errorAppendEntry(entry, err);
         return;
     }
 
-    urlres_r->header.putStr(HDR_ACCEPT, "text/plain");
+    urlres = xstrdup(local_urlres);
+    urlres_r->header.putStr(Http::HdrType::ACCEPT, "text/plain");
 }
 
 void
@@ -225,7 +169,7 @@ UrnState::start(HttpRequest * r, StoreEntry * e)
     entry = e;
     request = r;
 
-    entry->lock();
+    entry->lock("UrnState::start");
     setUriResFromRequest(r);
 
     if (urlres_r == NULL)
@@ -235,16 +179,25 @@ UrnState::start(HttpRequest * r, StoreEntry * e)
 }
 
 void
-UrnState::created(StoreEntry *newEntry)
+UrnState::fillChecklist(ACLFilledChecklist &checklist) const
 {
-    urlres_e = newEntry;
+    checklist.setRequest(request.getRaw());
+    checklist.al = ale;
+}
 
-    if (urlres_e->isNull()) {
+void
+UrnState::created(StoreEntry *e)
+{
+    if (!e || (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false))) {
         urlres_e = storeCreateEntry(urlres, urlres, RequestFlags(), Http::METHOD_GET);
         sc = storeClientListAdd(urlres_e, this);
-        FwdState::fwdStart(Comm::ConnectionPointer(), urlres_e, urlres_r.getRaw());
+        FwdState::Start(Comm::ConnectionPointer(), urlres_e, urlres_r.getRaw(), ale);
+        // TODO: StoreClients must either store/lock or abandon found entries.
+        //if (e)
+        //    e->abandon();
     } else {
-        urlres_e->lock();
+        urlres_e = e;
+        urlres_e->lock("UrnState::created");
         sc = storeClientListAdd(urlres_e, this);
     }
 
@@ -260,9 +213,9 @@ UrnState::created(StoreEntry *newEntry)
 }
 
 void
-urnStart(HttpRequest * r, StoreEntry * e)
+urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
 {
-    UrnState *anUrn = new UrnState();
+    const auto anUrn = new UrnState(ale);
     anUrn->start (r, e);
 }
 
@@ -282,14 +235,6 @@ url_entry_sort(const void *A, const void *B)
         return u1->rtt - u2->rtt;
 }
 
-static void
-urnHandleReplyError(UrnState *urnState, StoreEntry *urlres_e)
-{
-    urlres_e->unlock();
-    urnState->entry->unlock();
-    delete urnState;
-}
-
 /* TODO: use the clientStream support for this */
 static void
 urnHandleReply(void *data, StoreIOBuffer result)
@@ -303,7 +248,6 @@ urnHandleReply(void *data, StoreIOBuffer result)
     url_entry *urls;
     url_entry *u;
     url_entry *min_u;
-    MemBuf *mb = NULL;
     ErrorState *err;
     int i;
     int urlcnt = 0;
@@ -312,8 +256,8 @@ urnHandleReply(void *data, StoreIOBuffer result)
 
     debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
 
-    if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.length == 0 || result.flags.error) {
-        urnHandleReplyError(urnState, urlres_e);
+    if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
+        delete urnState;
         return;
     }
 
@@ -322,15 +266,15 @@ urnHandleReply(void *data, StoreIOBuffer result)
 
     /* Handle reqofs being bigger than normal */
     if (urnState->reqofs >= URN_REQBUF_SZ) {
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
     /* If we haven't received the entire object (urn), copy more */
-    if (urlres_e->store_status == STORE_PENDING &&
-            urnState->reqofs < URN_REQBUF_SZ) {
+    if (urlres_e->store_status == STORE_PENDING) {
+        Must(result.length > 0); // zero length ought to imply STORE_OK
         tempBuffer.offset = urnState->reqofs;
-        tempBuffer.length = URN_REQBUF_SZ;
+        tempBuffer.length = URN_REQBUF_SZ - urnState->reqofs;
         tempBuffer.data = urnState->reqbuf + urnState->reqofs;
         storeClientCopy(urnState->sc, urlres_e,
                         tempBuffer,
@@ -344,23 +288,23 @@ urnHandleReply(void *data, StoreIOBuffer result)
 
     if (0 == k) {
         debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url()  );
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
     s = buf + k;
-    assert(urlres_e->getReply());
+    // TODO: Check whether we should parse urlres_e reply, as before 528b2c61.
     rep = new HttpReply;
     rep->parseCharBuf(buf, k);
     debugs(52, 3, "reply exists, code=" << rep->sline.status() << ".");
 
     if (rep->sline.status() != Http::scOkay) {
         debugs(52, 3, "urnHandleReply: failed.");
-        err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
+        err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
         err->url = xstrdup(e->url());
         errorAppendEntry(e, err);
         delete rep;
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
@@ -371,47 +315,46 @@ urnHandleReply(void *data, StoreIOBuffer result)
 
     urls = urnParseReply(s, urnState->request->method);
 
-    for (i = 0; NULL != urls[i].url; ++i)
-        ++urlcnt;
-
-    debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
-
-    if (urls == NULL) {                /* unkown URN error */
+    if (!urls) {     /* unknown URN error */
         debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
-        err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw());
+        err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
         err->url = xstrdup(e->url());
         errorAppendEntry(e, err);
-        urnHandleReplyError(urnState, urlres_e);
+        delete urnState;
         return;
     }
 
+    for (i = 0; urls[i].url; ++i)
+        ++urlcnt;
+
+    debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
+
     min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
     qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
     e->buffer();
-    mb = new MemBuf;
-    mb->init();
-    mb->Printf( "<TITLE>Select URL for %s</TITLE>\n"
-                "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
-                "<H2>Select URL for %s</H2>\n"
-                "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
+    SBuf body;
+    SBuf *mb = &body; // diff reduction hack; TODO: Remove
+    mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
+                 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
+                 "<H2>Select URL for %s</H2>\n"
+                 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
 
     for (i = 0; i < urlcnt; ++i) {
         u = &urls[i];
         debugs(52, 3, "URL {" << u->url << "}");
-        mb->Printf(
+        mb->appendf(
             "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
 
         if (urls[i].rtt > 0)
-            mb->Printf(
+            mb->appendf(
                 "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
         else
-            mb->Printf("<TD align=\"right\">Unknown</TD>");
+            mb->appendf("<TD align=\"right\">Unknown</TD>");
 
-        mb->Printf(
-            "<TD>%s</TD></TR>\n", u->flags.cached ? "    [cached]" : " ");
+        mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? "    [cached]" : " ");
     }
 
-    mb->Printf(
+    mb->appendf(
         "</TABLE>"
         "<HR noshade size=\"1px\">\n"
         "<ADDRESS>\n"
@@ -419,16 +362,13 @@ urnHandleReply(void *data, StoreIOBuffer result)
         "</ADDRESS>\n",
         APP_FULLNAME, getMyHostname());
     rep = new HttpReply;
-    rep->setHeaders(Http::scMovedTemporarily, NULL, "text/html", mb->contentSize(), 0, squid_curtime);
+    rep->setHeaders(Http::scFound, NULL, "text/html", mb->length(), 0, squid_curtime);
 
-    if (urnState->flags.force_menu) {
-        debugs(51, 3, "urnHandleReply: forcing menu");
-    } else if (min_u) {
-        rep->header.putStr(HDR_LOCATION, min_u->url);
+    if (min_u) {
+        rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
     }
 
-    rep->body.setMb(mb);
-    /* don't clean or delete mb; rep->body owns it now */
+    rep->body.set(body);
     e->replaceHttpReply(rep);
     e->complete();
 
@@ -438,10 +378,8 @@ urnHandleReply(void *data, StoreIOBuffer result)
     }
 
     safe_free(urls);
-    /* mb was absorbed in httpBodySet call, so we must not clean it */
-    storeUnregister(urnState->sc, urlres_e, urnState);
 
-    urnHandleReplyError(urnState, urlres_e);
+    delete urnState;
 }
 
 static url_entry *
@@ -449,7 +387,6 @@ urnParseReply(const char *inbuf, const HttpRequestMethod& m)
 {
     char *buf = xstrdup(inbuf);
     char *token;
-    char *url;
     char *host;
     url_entry *list;
     url_entry *old;
@@ -469,8 +406,7 @@ urnParseReply(const char *inbuf, const HttpRequestMethod& m)
             safe_free(old);
         }
 
-        url = xstrdup(token);
-        host = urlHostname(url);
+        host = urlHostname(token);
 
         if (NULL == host)
             continue;
@@ -486,14 +422,15 @@ urnParseReply(const char *inbuf, const HttpRequestMethod& m)
         list[i].rtt = 0;
 #endif
 
-        list[i].url = url;
+        list[i].url = xstrdup(token);
         list[i].host = xstrdup(host);
         // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
         // ones.
-        list[i].flags.cached = storeGetPublic(url, m) ? 1 : 0;
+        list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
         ++i;
     }
 
     debugs(52, 3, "urnParseReply: Found " << i << " URLs");
     return list;
 }
+