]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ident/Ident.cc
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / ident / Ident.cc
1 /*
2 * Copyright (C) 1996-2022 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 30 Ident (RFC 931) */
10
11 #include "squid.h"
12
13 #if USE_IDENT
14 #include "base/JobWait.h"
15 #include "comm.h"
16 #include "comm/Connection.h"
17 #include "comm/ConnOpener.h"
18 #include "comm/Read.h"
19 #include "comm/Write.h"
20 #include "CommCalls.h"
21 #include "globals.h"
22 #include "ident/Config.h"
23 #include "ident/Ident.h"
24 #include "MemBuf.h"
25
26 namespace Ident
27 {
28
29 #define IDENT_PORT 113
30 #define IDENT_KEY_SZ 50
31 #define IDENT_BUFSIZE 4096
32
33 typedef struct _IdentClient {
34 IDCB *callback;
35 void *callback_data;
36
37 struct _IdentClient *next;
38 } IdentClient;
39
40 class IdentStateData
41 {
42 public:
43 hash_link hash; /* must be first */
44 private:
45 CBDATA_CLASS(IdentStateData);
46
47 public:
48 /* AsyncJob API emulated */
49 void deleteThis(const char *aReason);
50 void swanSong();
51
52 /// notify all waiting IdentClient callbacks
53 void notify(const char *result);
54
55 Comm::ConnectionPointer conn;
56 MemBuf queryMsg; ///< the lookup message sent to IDENT server
57 IdentClient *clients = nullptr;
58 char buf[IDENT_BUFSIZE];
59
60 /// waits for a connection to the IDENT server to be established/opened
61 JobWait<Comm::ConnOpener> connWait;
62
63 private:
64 // use deleteThis() to destroy
65 ~IdentStateData();
66 };
67
68 CBDATA_CLASS_INIT(IdentStateData);
69
70 // TODO: make these all a series of Async job calls. They are self-contained callbacks now.
71 static IOCB ReadReply;
72 static IOCB WriteFeedback;
73 static CLCB Close;
74 static CTCB Timeout;
75 static CNCB ConnectDone;
76 static hash_table *ident_hash = nullptr;
77 static void ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data);
78
79 } // namespace Ident
80
81 Ident::IdentConfig Ident::TheConfig;
82
83 void
84 Ident::IdentStateData::deleteThis(const char *reason)
85 {
86 debugs(30, 3, reason);
87 swanSong();
88 delete this;
89 }
90
91 void
92 Ident::IdentStateData::swanSong()
93 {
94 if (clients != nullptr)
95 notify(nullptr);
96 }
97
98 Ident::IdentStateData::~IdentStateData() {
99 assert(!clients);
100
101 if (Comm::IsConnOpen(conn)) {
102 comm_remove_close_handler(conn->fd, Ident::Close, this);
103 conn->close();
104 }
105
106 hash_remove_link(ident_hash, (hash_link *) this);
107 xfree(hash.key);
108 }
109
110 void
111 Ident::IdentStateData::notify(const char *result)
112 {
113 while (IdentClient *client = clients) {
114 void *cbdata;
115 clients = client->next;
116
117 if (cbdataReferenceValidDone(client->callback_data, &cbdata))
118 client->callback(result, cbdata);
119
120 xfree(client);
121 }
122 }
123
124 void
125 Ident::Close(const CommCloseCbParams &params)
126 {
127 IdentStateData *state = (IdentStateData *)params.data;
128 if (state->conn) {
129 state->conn->noteClosure();
130 state->conn = nullptr;
131 }
132 state->deleteThis("connection closed");
133 }
134
135 void
136 Ident::Timeout(const CommTimeoutCbParams &io)
137 {
138 debugs(30, 3, io.conn);
139 IdentStateData *state = (IdentStateData *)io.data;
140 state->deleteThis("timeout");
141 }
142
143 void
144 Ident::ConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int, void *data)
145 {
146 IdentStateData *state = (IdentStateData *)data;
147 state->connWait.finish();
148
149 // Start owning the supplied connection (so that it is not orphaned if this
150 // function bails early). As a (tiny) optimization or perhaps just diff
151 // minimization, the close handler is added later, when we know we are not
152 // bailing. This delay is safe because comm_remove_close_handler() forgives
153 // missing handlers.
154 assert(conn); // but may be closed
155 assert(!state->conn);
156 state->conn = conn;
157
158 if (status != Comm::OK) {
159 if (status == Comm::TIMEOUT)
160 debugs(30, 3, "IDENT connection timeout to " << state->conn->remote);
161 state->deleteThis(status == Comm::TIMEOUT ? "connect timeout" : "connect error");
162 return;
163 }
164
165 /*
166 * see if any of our clients still care
167 */
168 IdentClient *c;
169 for (c = state->clients; c; c = c->next) {
170 if (cbdataReferenceValid(c->callback_data))
171 break;
172 }
173
174 if (c == nullptr) {
175 state->deleteThis("client(s) aborted");
176 return;
177 }
178
179 assert(state->conn->isOpen());
180 comm_add_close_handler(state->conn->fd, Ident::Close, state);
181
182 AsyncCall::Pointer writeCall = commCbCall(5,4, "Ident::WriteFeedback",
183 CommIoCbPtrFun(Ident::WriteFeedback, state));
184 Comm::Write(conn, &state->queryMsg, writeCall);
185 AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply",
186 CommIoCbPtrFun(Ident::ReadReply, state));
187 comm_read(conn, state->buf, IDENT_BUFSIZE, readCall);
188 AsyncCall::Pointer timeoutCall = commCbCall(5,4, "Ident::Timeout",
189 CommTimeoutCbPtrFun(Ident::Timeout, state));
190 commSetConnTimeout(conn, Ident::TheConfig.timeout, timeoutCall);
191 }
192
193 void
194 Ident::WriteFeedback(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int xerrno, void *data)
195 {
196 debugs(30, 5, conn << ": Wrote IDENT request " << len << " bytes.");
197
198 // TODO handle write errors better. retry or abort?
199 if (flag != Comm::OK) {
200 debugs(30, 2, conn << " err-flags=" << flag << " IDENT write error: " << xstrerr(xerrno));
201 IdentStateData *state = (IdentStateData *)data;
202 state->deleteThis("write error");
203 }
204 }
205
206 void
207 Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int, void *data)
208 {
209 IdentStateData *state = (IdentStateData *)data;
210 char *ident = nullptr;
211 char *t = nullptr;
212
213 assert(buf == state->buf);
214 assert(conn->fd == state->conn->fd);
215
216 if (flag != Comm::OK || len <= 0) {
217 state->deleteThis("read error");
218 return;
219 }
220
221 /*
222 * XXX This isn't really very tolerant. It should read until EOL
223 * or EOF and then decode the answer... If the reply is fragmented
224 * then this will fail
225 */
226 buf[len] = '\0';
227
228 if ((t = strchr(buf, '\r')))
229 *t = '\0';
230
231 if ((t = strchr(buf, '\n')))
232 *t = '\0';
233
234 debugs(30, 5, conn << ": Read '" << buf << "'");
235
236 if (strstr(buf, "USERID")) {
237 if ((ident = strrchr(buf, ':'))) {
238 while (xisspace(*++ident));
239 if (ident && *ident == '\0')
240 ident = nullptr;
241 state->notify(ident);
242 }
243 }
244
245 state->deleteThis("completed");
246 }
247
248 void
249 Ident::ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data)
250 {
251 IdentClient *c = (IdentClient *)xcalloc(1, sizeof(*c));
252 IdentClient **C;
253 c->callback = callback;
254 c->callback_data = cbdataReference(callback_data);
255
256 for (C = &state->clients; *C; C = &(*C)->next);
257 *C = c;
258 }
259
260 /*
261 * start a TCP connection to the peer host on port 113
262 */
263 void
264 Ident::Start(const Comm::ConnectionPointer &conn, IDCB * callback, void *data)
265 {
266 IdentStateData *state;
267 char key1[IDENT_KEY_SZ];
268 char key2[IDENT_KEY_SZ];
269 char key[IDENT_KEY_SZ*2+2]; // key1 + ',' + key2 + terminator
270
271 conn->local.toUrl(key1, IDENT_KEY_SZ);
272 conn->remote.toUrl(key2, IDENT_KEY_SZ);
273 int res = snprintf(key, sizeof(key), "%s,%s", key1, key2);
274 assert(res > 0);
275 assert(static_cast<unsigned int>(res) < sizeof(key));
276
277 if (!ident_hash) {
278 ident_hash = hash_create((HASHCMP *) strcmp,
279 hashPrime(Squid_MaxFD / 8),
280 hash4);
281 }
282 if ((state = (IdentStateData *)hash_lookup(ident_hash, key)) != nullptr) {
283 ClientAdd(state, callback, data);
284 return;
285 }
286
287 state = new IdentStateData;
288 state->hash.key = xstrdup(key);
289
290 // copy the conn details. We do not want the original FD to be re-used by IDENT.
291 const auto identConn = conn->cloneProfile();
292 // NP: use random port for secure outbound to IDENT_PORT
293 identConn->local.port(0);
294 identConn->remote.port(IDENT_PORT);
295
296 // build our query from the original connection details
297 state->queryMsg.init();
298 state->queryMsg.appendf("%d, %d\r\n", conn->remote.port(), conn->local.port());
299
300 ClientAdd(state, callback, data);
301 hash_join(ident_hash, &state->hash);
302
303 AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state));
304 const auto connOpener = new Comm::ConnOpener(identConn, call, Ident::TheConfig.timeout);
305 state->connWait.start(connOpener, call);
306 }
307
308 #endif /* USE_IDENT */
309