]> git.ipfire.org Git - thirdparty/squid.git/blob - src/whois.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / whois.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 /* DEBUG: section 75 WHOIS protocol */
10
11 #include "squid.h"
12 #include "comm.h"
13 #include "comm/Read.h"
14 #include "comm/Write.h"
15 #include "errorpage.h"
16 #include "FwdState.h"
17 #include "HttpReply.h"
18 #include "HttpRequest.h"
19 #include "SquidConfig.h"
20 #include "StatCounters.h"
21 #include "Store.h"
22 #include "tools.h"
23 #include "whois.h"
24
25 #include <cerrno>
26
27 class WhoisState
28 {
29 CBDATA_CLASS(WhoisState);
30
31 public:
32 void readReply(const Comm::ConnectionPointer &, char *aBuffer, size_t aBufferLength, Comm::Flag flag, int xerrno);
33 void setReplyToOK(StoreEntry *sentry);
34 StoreEntry *entry;
35 HttpRequest::Pointer request;
36 FwdState::Pointer fwd;
37 char buf[BUFSIZ+1]; /* readReply adds terminating NULL */
38 bool dataWritten;
39 };
40
41 CBDATA_CLASS_INIT(WhoisState);
42
43 static CLCB whoisClose;
44 static CTCB whoisTimeout;
45 static IOCB whoisReadReply;
46
47 /* PUBLIC */
48
49 static void
50 whoisWriteComplete(const Comm::ConnectionPointer &, char *buf, size_t, Comm::Flag, int, void *)
51 {
52 xfree(buf);
53 }
54
55 void
56 whoisStart(FwdState * fwd)
57 {
58 WhoisState *p = new WhoisState;
59 p->request = fwd->request;
60 p->entry = fwd->entry;
61 p->fwd = fwd;
62 p->dataWritten = false;
63
64 p->entry->lock("whoisStart");
65 comm_add_close_handler(fwd->serverConnection()->fd, whoisClose, p);
66
67 size_t l = p->request->url.path().length() + 3;
68 char *buf = (char *)xmalloc(l);
69
70 const SBuf str_print = p->request->url.path().substr(1);
71 snprintf(buf, l, SQUIDSBUFPH "\r\n", SQUIDSBUFPRINT(str_print));
72
73 AsyncCall::Pointer writeCall = commCbCall(5,5, "whoisWriteComplete",
74 CommIoCbPtrFun(whoisWriteComplete, p));
75 Comm::Write(fwd->serverConnection(), buf, strlen(buf), writeCall, nullptr);
76 AsyncCall::Pointer readCall = commCbCall(5,4, "whoisReadReply",
77 CommIoCbPtrFun(whoisReadReply, p));
78 comm_read(fwd->serverConnection(), p->buf, BUFSIZ, readCall);
79 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "whoisTimeout",
80 CommTimeoutCbPtrFun(whoisTimeout, p));
81 commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall);
82 }
83
84 /* PRIVATE */
85
86 static void
87 whoisTimeout(const CommTimeoutCbParams &io)
88 {
89 WhoisState *p = static_cast<WhoisState *>(io.data);
90 debugs(75, 3, io.conn << ", URL " << p->entry->url());
91 io.conn->close();
92 }
93
94 static void
95 whoisReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
96 {
97 WhoisState *p = (WhoisState *)data;
98 p->readReply(conn, buf, len, flag, xerrno);
99 }
100
101 void
102 WhoisState::setReplyToOK(StoreEntry *sentry)
103 {
104 HttpReply *reply = new HttpReply;
105 sentry->buffer();
106 reply->setHeaders(Http::scOkay, "Gatewaying", "text/plain", -1, -1, -2);
107 reply->sources |= Http::Message::srcWhois;
108 sentry->replaceHttpReply(reply);
109 }
110
111 void
112 WhoisState::readReply(const Comm::ConnectionPointer &conn, char *aBuffer, size_t aBufferLength, Comm::Flag flag, int xerrno)
113 {
114 /* Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us */
115 if (flag == Comm::ERR_CLOSING)
116 return;
117
118 aBuffer[aBufferLength] = '\0';
119 debugs(75, 3, conn << " read " << aBufferLength << " bytes");
120 debugs(75, 5, "{" << aBuffer << "}");
121
122 // TODO: Honor delay pools.
123
124 // XXX: Update statCounter before bailing
125 if (!entry->isAccepting()) {
126 debugs(52, 3, "terminating due to bad " << *entry);
127 // TODO: Do not abuse connection for triggering cleanup.
128 conn->close();
129 return;
130 }
131
132 if (flag != Comm::OK) {
133 debugs(50, 2, conn << ": read failure: " << xstrerr(xerrno));
134
135 if (ignoreErrno(xerrno)) {
136 AsyncCall::Pointer call = commCbCall(5,4, "whoisReadReply",
137 CommIoCbPtrFun(whoisReadReply, this));
138 comm_read(conn, aBuffer, BUFSIZ, call);
139 } else {
140 const auto err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, fwd->request, fwd->al);
141 err->xerrno = xerrno;
142 fwd->fail(err);
143 conn->close();
144 }
145 return;
146 }
147
148 if (aBufferLength > 0) {
149 if (!dataWritten)
150 setReplyToOK(entry);
151
152 statCounter.server.all.kbytes_in += aBufferLength;
153 statCounter.server.http.kbytes_in += aBufferLength;
154
155 /* No range support, we always grab it all */
156 dataWritten = true;
157 entry->append(aBuffer, aBufferLength);
158 entry->flush();
159
160 AsyncCall::Pointer call = commCbCall(5,4, "whoisReadReply",
161 CommIoCbPtrFun(whoisReadReply, this));
162 comm_read(conn, aBuffer, BUFSIZ, call);
163 return;
164 }
165
166 /* no bytes read. stop reading */
167 entry->timestampsSet();
168 entry->flush();
169
170 if (!entry->makePublic())
171 entry->makePrivate(true);
172
173 if (dataWritten) // treat zero-length responses as incomplete
174 fwd->markStoredReplyAsWhole("whois received/stored the entire response");
175
176 fwd->complete();
177 debugs(75, 3, "whoisReadReply: Done: " << entry->url());
178 conn->close();
179 }
180
181 static void
182 whoisClose(const CommCloseCbParams &params)
183 {
184 WhoisState *p = (WhoisState *)params.data;
185 debugs(75, 3, "whoisClose: FD " << params.fd);
186 // We do not own a Connection. Assume that FwdState is also monitoring.
187 p->entry->unlock("whoisClose");
188 delete p;
189 }
190