]> git.ipfire.org Git - thirdparty/squid.git/blame - src/urn.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / urn.cc
CommitLineData
85491f8d 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
85491f8d 7 */
8
bbc27441
AJ
9/* DEBUG: section 52 URN Parsing */
10
582c2af2 11#include "squid.h"
d2a6dcba 12#include "AccessLogEntry.h"
819be284 13#include "acl/FilledChecklist.h"
bd10977c 14#include "base/TextException.h"
bda078fe 15#include "cbdata.h"
aa839030 16#include "errorpage.h"
eb13c21e 17#include "FwdState.h"
582c2af2 18#include "globals.h"
e87137f1
FC
19#include "HttpReply.h"
20#include "HttpRequest.h"
9b5c4a9a 21#include "icmp/net_db.h"
e87137f1 22#include "MemBuf.h"
b6149797 23#include "mime_header.h"
f206b652 24#include "RequestFlags.h"
e87137f1
FC
25#include "SquidTime.h"
26#include "Store.h"
27#include "StoreClient.h"
8d03bdb4 28#include "tools.h"
5eb529cb 29#include "urn.h"
85491f8d 30
f53969cc 31#define URN_REQBUF_SZ 4096
add2192d 32
62e76326 33class UrnState : public StoreClient
34{
5c2f68b7 35 CBDATA_CLASS(UrnState);
62e76326 36
e6ccf245 37public:
d2a6dcba
EB
38 explicit UrnState(const AccessLogEntry::Pointer &anAle): ale(anAle) {}
39
3b13a8fd 40 void created (StoreEntry *newEntry);
190154cf 41 void start (HttpRequest *, StoreEntry *);
190154cf 42 void setUriResFromRequest(HttpRequest *);
e6ccf245 43
44 virtual ~UrnState();
62e76326 45
d2a6dcba
EB
46 StoreEntry *entry = nullptr;
47 store_client *sc = nullptr;
48 StoreEntry *urlres_e = nullptr;
8a70cdbb
AJ
49 HttpRequest::Pointer request;
50 HttpRequest::Pointer urlres_r;
7e6eabbc 51 AccessLogEntry::Pointer ale; ///< details of the requesting transaction
62e76326 52
d2a6dcba
EB
53 char reqbuf[URN_REQBUF_SZ] = { '\0' };
54 int reqofs = 0;
62e76326 55
e6ccf245 56private:
819be284 57 /* StoreClient API */
d2a6dcba 58 virtual LogTags *loggingTags() { return ale ? &ale->cache.code : nullptr; }
819be284
EB
59 virtual void fillChecklist(ACLFilledChecklist &) const;
60
d2a6dcba 61 char *urlres = nullptr;
e6ccf245 62};
85491f8d 63
26ac0430 64typedef struct {
9ce5e3e6 65 char *url;
66 char *host;
67 int rtt;
62e76326 68
26ac0430 69 struct {
62e76326 70 int cached;
2fadd50d
HN
71 } flags;
72} url_entry;
9ce5e3e6 73
74static STCB urnHandleReply;
60745f24 75static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
9ce5e3e6 76static const char *const crlf = "\r\n";
9ce5e3e6 77
bda078fe 78CBDATA_CLASS_INIT(UrnState);
62e76326 79
bda078fe 80UrnState::~UrnState()
e6ccf245 81{
bd10977c
EB
82 SWALLOW_EXCEPTIONS({
83 if (urlres_e) {
84 if (sc)
85 storeUnregister(sc, urlres_e, this);
86 urlres_e->unlock("~UrnState+res");
87 }
88
89 if (entry)
90 entry->unlock("~UrnState+prime");
91
92 safe_free(urlres);
93 });
e6ccf245 94}
95
48ebcb22 96static url_entry *
ced8def3 97urnFindMinRtt(url_entry * urls, const HttpRequestMethod &, int *rtt_ret)
85491f8d 98{
23d92c64 99 int min_rtt = 0;
9ce5e3e6 100 url_entry *u = NULL;
101 url_entry *min_u = NULL;
102 int i;
1caf595b 103 int urlcnt = 0;
bf8fe701 104 debugs(52, 3, "urnFindMinRtt");
23d92c64 105 assert(urls != NULL);
62e76326 106
14942edd
FC
107 for (i = 0; NULL != urls[i].url; ++i)
108 ++urlcnt;
62e76326 109
bf8fe701 110 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
62e76326 111
9ce5e3e6 112 if (1 == urlcnt) {
bf8fe701 113 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
62e76326 114 return urls;
1caf595b 115 }
62e76326 116
14942edd 117 for (i = 0; i < urlcnt; ++i) {
62e76326 118 u = &urls[i];
bf8fe701 119 debugs(52, 3, "urnFindMinRtt: " << u->host << " rtt=" << u->rtt);
62e76326 120
121 if (u->rtt == 0)
122 continue;
123
124 if (u->rtt > min_rtt && min_rtt != 0)
125 continue;
126
127 min_rtt = u->rtt;
128
129 min_u = u;
23d92c64 130 }
62e76326 131
23d92c64 132 if (rtt_ret)
62e76326 133 *rtt_ret = min_rtt;
134
e0236918 135 debugs(52, DBG_IMPORTANT, "urnFindMinRtt: Returning '" <<
26ac0430
AJ
136 (min_u ? min_u->url : "NONE") << "' RTT " <<
137 min_rtt );
62e76326 138
9ce5e3e6 139 return min_u;
85491f8d 140}
141
e6ccf245 142void
51b5dcf5 143UrnState::setUriResFromRequest(HttpRequest *r)
e6ccf245 144{
6c880a16
AJ
145 const auto &query = r->url.absolute();
146 const auto host = r->url.host();
c8ab5ec6 147 // TODO: use class AnyP::Uri instead of generating a string and re-parsing
e6ccf245 148 LOCAL_ARRAY(char, local_urlres, 4096);
6c880a16 149 snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?" SQUIDSBUFPH, host, SQUIDSBUFPRINT(query));
86c63190 150 safe_free(urlres);
6c880a16 151 urlres_r = HttpRequest::FromUrlXXX(local_urlres, r->masterXaction);
62e76326 152
8babada0
AJ
153 if (!urlres_r) {
154 debugs(52, 3, "Bad uri-res URL " << local_urlres);
7e6eabbc 155 const auto err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r, ale);
8babada0 156 err->url = xstrdup(local_urlres);
62e76326 157 errorAppendEntry(entry, err);
158 return;
0adbab7c 159 }
62e76326 160
8babada0 161 urlres = xstrdup(local_urlres);
789217a2 162 urlres_r->header.putStr(Http::HdrType::ACCEPT, "text/plain");
e6ccf245 163}
164
165void
190154cf 166UrnState::start(HttpRequest * r, StoreEntry * e)
e6ccf245 167{
bf8fe701 168 debugs(52, 3, "urnStart: '" << e->url() << "'" );
e6ccf245 169 entry = e;
b248c2a3 170 request = r;
34266cde 171
1bfe9ade 172 entry->lock("UrnState::start");
e6ccf245 173 setUriResFromRequest(r);
62e76326 174
e6ccf245 175 if (urlres_r == NULL)
62e76326 176 return;
177
c2a7cefd 178 StoreEntry::getPublic (this, urlres, Http::METHOD_GET);
e6ccf245 179}
180
181void
819be284 182UrnState::fillChecklist(ACLFilledChecklist &checklist) const
e6ccf245 183{
819be284 184 checklist.setRequest(request.getRaw());
d2a6dcba 185 checklist.al = ale;
819be284 186}
62e76326 187
819be284
EB
188void
189UrnState::created(StoreEntry *e)
190{
69565793 191 if (!e || (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false))) {
c2a7cefd 192 urlres_e = storeCreateEntry(urlres, urlres, RequestFlags(), Http::METHOD_GET);
62e76326 193 sc = storeClientListAdd(urlres_e, this);
d2a6dcba 194 FwdState::Start(Comm::ConnectionPointer(), urlres_e, urlres_r.getRaw(), ale);
819be284 195 // TODO: StoreClients must either store/lock or abandon found entries.
69565793 196 //if (e)
819be284 197 // e->abandon();
cf26e54c 198 } else {
819be284 199 urlres_e = e;
1bfe9ade 200 urlres_e->lock("UrnState::created");
62e76326 201 sc = storeClientListAdd(urlres_e, this);
85491f8d 202 }
62e76326 203
e6ccf245 204 reqofs = 0;
528b2c61 205 StoreIOBuffer tempBuffer;
e6ccf245 206 tempBuffer.offset = reqofs;
c8be6d7b 207 tempBuffer.length = URN_REQBUF_SZ;
e6ccf245 208 tempBuffer.data = reqbuf;
209 storeClientCopy(sc, urlres_e,
62e76326 210 tempBuffer,
211 urnHandleReply,
212 this);
e6ccf245 213}
214
215void
d2a6dcba 216urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
e6ccf245 217{
d2a6dcba 218 const auto anUrn = new UrnState(ale);
e6ccf245 219 anUrn->start (r, e);
85491f8d 220}
221
9ce5e3e6 222static int
223url_entry_sort(const void *A, const void *B)
224{
e6ccf245 225 const url_entry *u1 = (const url_entry *)A;
226 const url_entry *u2 = (const url_entry *)B;
62e76326 227
9ce5e3e6 228 if (u2->rtt == u1->rtt)
62e76326 229 return 0;
9ce5e3e6 230 else if (0 == u1->rtt)
62e76326 231 return 1;
9ce5e3e6 232 else if (0 == u2->rtt)
62e76326 233 return -1;
9ce5e3e6 234 else
62e76326 235 return u1->rtt - u2->rtt;
9ce5e3e6 236}
237
528b2c61 238/* TODO: use the clientStream support for this */
85491f8d 239static void
c8be6d7b 240urnHandleReply(void *data, StoreIOBuffer result)
85491f8d 241{
e6ccf245 242 UrnState *urnState = static_cast<UrnState *>(data);
cf26e54c 243 StoreEntry *e = urnState->entry;
244 StoreEntry *urlres_e = urnState->urlres_e;
23d92c64 245 char *s = NULL;
2334c194 246 size_t k;
cb69b4c7 247 HttpReply *rep;
9ce5e3e6 248 url_entry *urls;
00141c96 249 url_entry *u;
9ce5e3e6 250 url_entry *min_u;
cf26e54c 251 ErrorState *err;
9ce5e3e6 252 int i;
253 int urlcnt = 0;
add2192d 254 char *buf = urnState->reqbuf;
c8be6d7b 255 StoreIOBuffer tempBuffer;
cf26e54c 256
58c0b17d 257 debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
62e76326 258
682d5190
EB
259 if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
260 delete urnState;
bb9edbb2 261 return;
85491f8d 262 }
62e76326 263
add2192d 264 /* Update reqofs to point to where in the buffer we'd be */
c8be6d7b 265 urnState->reqofs += result.length;
add2192d 266
267 /* Handle reqofs being bigger than normal */
268 if (urnState->reqofs >= URN_REQBUF_SZ) {
682d5190 269 delete urnState;
bb9edbb2 270 return;
add2192d 271 }
62e76326 272
add2192d 273 /* If we haven't received the entire object (urn), copy more */
682d5190
EB
274 if (urlres_e->store_status == STORE_PENDING) {
275 Must(result.length > 0); // zero length ought to imply STORE_OK
62e76326 276 tempBuffer.offset = urnState->reqofs;
682d5190 277 tempBuffer.length = URN_REQBUF_SZ - urnState->reqofs;
62e76326 278 tempBuffer.data = urnState->reqbuf + urnState->reqofs;
279 storeClientCopy(urnState->sc, urlres_e,
280 tempBuffer,
281 urnHandleReply,
282 urnState);
283 return;
23d92c64 284 }
62e76326 285
23d92c64 286 /* we know its STORE_OK */
add2192d 287 k = headersEnd(buf, urnState->reqofs);
62e76326 288
2334c194 289 if (0 == k) {
e0236918 290 debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url() );
682d5190 291 delete urnState;
bb9edbb2 292 return;
85491f8d 293 }
62e76326 294
2334c194 295 s = buf + k;
66d51f4f 296 // TODO: Check whether we should parse urlres_e reply, as before 528b2c61.
06a5ae20 297 rep = new HttpReply;
59eed7dc 298 rep->parseCharBuf(buf, k);
9b769c67 299 debugs(52, 3, "reply exists, code=" << rep->sline.status() << ".");
62e76326 300
9b769c67 301 if (rep->sline.status() != Http::scOkay) {
bf8fe701 302 debugs(52, 3, "urnHandleReply: failed.");
7e6eabbc 303 err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
3900307b 304 err->url = xstrdup(e->url());
62e76326 305 errorAppendEntry(e, err);
06a5ae20 306 delete rep;
682d5190 307 delete urnState;
bb9edbb2 308 return;
85491f8d 309 }
62e76326 310
06a5ae20 311 delete rep;
62e76326 312
b6a2f15e 313 while (xisspace(*s))
14942edd 314 ++s;
62e76326 315
9ce5e3e6 316 urls = urnParseReply(s, urnState->request->method);
62e76326 317
892ee3d0 318 if (!urls) { /* unknown URN error */
8a70cdbb 319 debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
7e6eabbc 320 err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
3900307b 321 err->url = xstrdup(e->url());
62e76326 322 errorAppendEntry(e, err);
682d5190 323 delete urnState;
bb9edbb2 324 return;
164f7660 325 }
62e76326 326
892ee3d0
AJ
327 for (i = 0; urls[i].url; ++i)
328 ++urlcnt;
329
330 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
331
9ce5e3e6 332 min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
333 qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
3900307b 334 e->buffer();
7e6eabbc
CT
335 SBuf body;
336 SBuf *mb = &body; // diff reduction hack; TODO: Remove
4391cd15 337 mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
f680026f
SM
338 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
339 "<H2>Select URL for %s</H2>\n"
340 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
62e76326 341
14942edd 342 for (i = 0; i < urlcnt; ++i) {
62e76326 343 u = &urls[i];
bf8fe701 344 debugs(52, 3, "URL {" << u->url << "}");
4391cd15 345 mb->appendf(
2fe7eff9 346 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
62e76326 347
348 if (urls[i].rtt > 0)
4391cd15 349 mb->appendf(
2fe7eff9 350 "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
62e76326 351 else
4391cd15 352 mb->appendf("<TD align=\"right\">Unknown</TD>");
62e76326 353
4391cd15 354 mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
cf26e54c 355 }
62e76326 356
4391cd15 357 mb->appendf(
2fe7eff9 358 "</TABLE>"
359 "<HR noshade size=\"1px\">\n"
360 "<ADDRESS>\n"
361 "Generated by %s@%s\n"
362 "</ADDRESS>\n",
7dbca7a4 363 APP_FULLNAME, getMyHostname());
06a5ae20 364 rep = new HttpReply;
7e6eabbc 365 rep->setHeaders(Http::scFound, NULL, "text/html", mb->length(), 0, squid_curtime);
62e76326 366
6c880a16 367 if (min_u) {
789217a2 368 rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
cb69b4c7 369 }
62e76326 370
7e6eabbc 371 rep->body.set(body);
db237875 372 e->replaceHttpReply(rep);
528b2c61 373 e->complete();
62e76326 374
14942edd 375 for (i = 0; i < urlcnt; ++i) {
62e76326 376 safe_free(urls[i].url);
377 safe_free(urls[i].host);
9ce5e3e6 378 }
62e76326 379
9ce5e3e6 380 safe_free(urls);
62e76326 381
682d5190 382 delete urnState;
85491f8d 383}
384
9ce5e3e6 385static url_entry *
60745f24 386urnParseReply(const char *inbuf, const HttpRequestMethod& m)
85491f8d 387{
23d92c64 388 char *buf = xstrdup(inbuf);
389 char *token;
9ce5e3e6 390 char *host;
9ce5e3e6 391 url_entry *list;
392 url_entry *old;
393 int n = 32;
394 int i = 0;
bf8fe701 395 debugs(52, 3, "urnParseReply");
e6ccf245 396 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
62e76326 397
23d92c64 398 for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
bf8fe701 399 debugs(52, 3, "urnParseReply: got '" << token << "'");
62e76326 400
401 if (i == n) {
402 old = list;
403 n <<= 2;
404 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
41d00cd3 405 memcpy(list, old, i * sizeof(*list));
62e76326 406 safe_free(old);
407 }
408
8babada0 409 host = urlHostname(token);
62e76326 410
411 if (NULL == host)
412 continue;
413
9b5c4a9a
AJ
414#if USE_ICMP
415 list[i].rtt = netdbHostRtt(host);
62e76326 416
9b5c4a9a 417 if (0 == list[i].rtt) {
bf8fe701 418 debugs(52, 3, "urnParseReply: Pinging " << host);
62e76326 419 netdbPingSite(host);
420 }
9b5c4a9a
AJ
421#else
422 list[i].rtt = 0;
423#endif
62e76326 424
8babada0 425 list[i].url = xstrdup(token);
62e76326 426 list[i].host = xstrdup(host);
5bd484b5
AR
427 // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
428 // ones.
8babada0 429 list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
14942edd 430 ++i;
85491f8d 431 }
62e76326 432
bf8fe701 433 debugs(52, 3, "urnParseReply: Found " << i << " URLs");
9ce5e3e6 434 return list;
85491f8d 435}
f53969cc 436