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