]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ident/Ident.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / ident / Ident.cc
CommitLineData
f1443bd9 1/*
f70aedc4 2 * Copyright (C) 1996-2021 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.
f1443bd9 7 */
bbc27441
AJ
8
9/* DEBUG: section 30 Ident (RFC 931) */
10
a016163c 11#include "squid.h"
4daaf3cb
AJ
12
13#if USE_IDENT
a46d2c0e 14#include "comm.h"
55cbb02b 15#include "comm/Connection.h"
aed188fd 16#include "comm/ConnOpener.h"
7e66d5e2 17#include "comm/Read.h"
ec41b64c 18#include "comm/Write.h"
602d9612 19#include "CommCalls.h"
582c2af2 20#include "globals.h"
4daaf3cb
AJ
21#include "ident/Config.h"
22#include "ident/Ident.h"
0eb49b6d 23#include "MemBuf.h"
f1443bd9 24
04f7fd38
AJ
25namespace Ident
26{
4daaf3cb 27
f1443bd9 28#define IDENT_PORT 113
05832ae1 29#define IDENT_KEY_SZ 50
abd8f140 30#define IDENT_BUFSIZE 4096
05832ae1 31
26ac0430 32typedef struct _IdentClient {
05832ae1 33 IDCB *callback;
34 void *callback_data;
62e76326 35
05832ae1 36 struct _IdentClient *next;
2fadd50d 37} IdentClient;
05832ae1 38
20a17bdd
AJ
39class IdentStateData
40{
5c2f68b7 41public:
f53969cc 42 hash_link hash; /* must be first */
5c2f68b7
AJ
43private:
44 CBDATA_CLASS(IdentStateData);
45
20a17bdd
AJ
46public:
47 /* AsyncJob API emulated */
48 void deleteThis(const char *aReason);
49 void swanSong();
50
51 /// notify all waiting IdentClient callbacks
52 void notify(const char *result);
53
aed188fd 54 Comm::ConnectionPointer conn;
173b7229 55 MemBuf queryMsg; ///< the lookup message sent to IDENT server
05832ae1 56 IdentClient *clients;
abd8f140 57 char buf[IDENT_BUFSIZE];
20a17bdd
AJ
58};
59
60CBDATA_CLASS_INIT(IdentStateData);
f1443bd9 61
e0d28505 62// TODO: make these all a series of Async job calls. They are self-contained callbacks now.
4daaf3cb 63static IOCB ReadReply;
fdde4c47 64static IOCB WriteFeedback;
575d05c4 65static CLCB Close;
8d77a37c 66static CTCB Timeout;
4daaf3cb 67static CNCB ConnectDone;
05832ae1 68static hash_table *ident_hash = NULL;
4daaf3cb 69static void ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data);
4daaf3cb 70
e5519212 71} // namespace Ident
4daaf3cb
AJ
72
73Ident::IdentConfig Ident::TheConfig;
05832ae1 74
20a17bdd 75void
ced8def3 76Ident::IdentStateData::deleteThis(const char *)
20a17bdd
AJ
77{
78 swanSong();
79 delete this;
80}
05832ae1 81
52631ccc 82void
20a17bdd 83Ident::IdentStateData::swanSong()
05832ae1 84{
20a17bdd
AJ
85 if (clients != NULL)
86 notify(NULL);
62e76326 87
20a17bdd
AJ
88 if (Comm::IsConnOpen(conn)) {
89 comm_remove_close_handler(conn->fd, Ident::Close, this);
90 conn->close();
91 }
62e76326 92
20a17bdd
AJ
93 hash_remove_link(ident_hash, (hash_link *) this);
94 xfree(hash.key);
95}
96
97void
98Ident::IdentStateData::notify(const char *result)
99{
100 while (IdentClient *client = clients) {
62e76326 101 void *cbdata;
20a17bdd 102 clients = client->next;
62e76326 103
104 if (cbdataReferenceValidDone(client->callback_data, &cbdata))
105 client->callback(result, cbdata);
106
107 xfree(client);
05832ae1 108 }
109}
f1443bd9 110
52631ccc 111void
575d05c4 112Ident::Close(const CommCloseCbParams &params)
f1443bd9 113{
575d05c4 114 IdentStateData *state = (IdentStateData *)params.data;
20a17bdd 115 state->deleteThis("connection closed");
f1443bd9 116}
117
52631ccc 118void
8d77a37c 119Ident::Timeout(const CommTimeoutCbParams &io)
86cf9987 120{
8d77a37c 121 debugs(30, 3, HERE << io.conn);
20a17bdd
AJ
122 IdentStateData *state = (IdentStateData *)io.data;
123 state->deleteThis("timeout");
86cf9987 124}
125
52631ccc 126void
ced8def3 127Ident::ConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int, void *data)
e5f6c5c2 128{
be005338 129 IdentStateData *state = (IdentStateData *)data;
62e76326 130
c8407295 131 if (status != Comm::OK) {
dbb4956c 132 if (status == Comm::TIMEOUT)
aed188fd 133 debugs(30, 3, "IDENT connection timeout to " << state->conn->remote);
20a17bdd 134 state->deleteThis(status == Comm::TIMEOUT ? "connect timeout" : "connect error");
62e76326 135 return;
f1443bd9 136 }
62e76326 137
05832ae1 138 /*
fa80a8ef 139 * see if any of our clients still care
05832ae1 140 */
cfd66529 141 IdentClient *c;
05832ae1 142 for (c = state->clients; c; c = c->next) {
62e76326 143 if (cbdataReferenceValid(c->callback_data))
144 break;
05832ae1 145 }
62e76326 146
ac2a30fc 147 if (c == NULL) {
20a17bdd 148 state->deleteThis("client(s) aborted");
62e76326 149 return;
ac2a30fc 150 }
62e76326 151
20a17bdd 152 assert(conn != NULL && conn == state->conn);
cfd66529
AJ
153 comm_add_close_handler(conn->fd, Ident::Close, state);
154
fdde4c47 155 AsyncCall::Pointer writeCall = commCbCall(5,4, "Ident::WriteFeedback",
1057efa8 156 CommIoCbPtrFun(Ident::WriteFeedback, state));
31ecec74 157 Comm::Write(conn, &state->queryMsg, writeCall);
8d77a37c 158 AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply",
dc49061a 159 CommIoCbPtrFun(Ident::ReadReply, state));
8d77a37c
AJ
160 comm_read(conn, state->buf, IDENT_BUFSIZE, readCall);
161 AsyncCall::Pointer timeoutCall = commCbCall(5,4, "Ident::Timeout",
dc49061a 162 CommTimeoutCbPtrFun(Ident::Timeout, state));
8d77a37c 163 commSetConnTimeout(conn, Ident::TheConfig.timeout, timeoutCall);
f1443bd9 164}
165
fdde4c47 166void
ced8def3 167Ident::WriteFeedback(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int xerrno, void *data)
fdde4c47
AJ
168{
169 debugs(30, 5, HERE << conn << ": Wrote IDENT request " << len << " bytes.");
170
171 // TODO handle write errors better. retry or abort?
c8407295 172 if (flag != Comm::OK) {
fdde4c47 173 debugs(30, 2, HERE << conn << " err-flags=" << flag << " IDENT write error: " << xstrerr(xerrno));
20a17bdd
AJ
174 IdentStateData *state = (IdentStateData *)data;
175 state->deleteThis("write error");
fdde4c47
AJ
176 }
177}
178
52631ccc 179void
ced8def3 180Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int, void *data)
f1443bd9 181{
be005338 182 IdentStateData *state = (IdentStateData *)data;
05832ae1 183 char *ident = NULL;
f1443bd9 184 char *t = NULL;
c4b7a5a9 185
cfd66529 186 assert(buf == state->buf);
e0d28505 187 assert(conn->fd == state->conn->fd);
62e76326 188
c8407295 189 if (flag != Comm::OK || len <= 0) {
20a17bdd 190 state->deleteThis("read error");
62e76326 191 return;
05832ae1 192 }
62e76326 193
05832ae1 194 /*
195 * XXX This isn't really very tolerant. It should read until EOL
196 * or EOF and then decode the answer... If the reply is fragmented
197 * then this will fail
198 */
199 buf[len] = '\0';
62e76326 200
05832ae1 201 if ((t = strchr(buf, '\r')))
62e76326 202 *t = '\0';
203
05832ae1 204 if ((t = strchr(buf, '\n')))
62e76326 205 *t = '\0';
206
e0d28505 207 debugs(30, 5, HERE << conn << ": Read '" << buf << "'");
62e76326 208
05832ae1 209 if (strstr(buf, "USERID")) {
62e76326 210 if ((ident = strrchr(buf, ':'))) {
3d0ac046 211 while (xisspace(*++ident));
20a17bdd
AJ
212 if (ident && *ident == '\0')
213 ident = NULL;
214 state->notify(ident);
62e76326 215 }
f1443bd9 216 }
62e76326 217
20a17bdd 218 state->deleteThis("completed");
ba1d2afa 219}
220
52631ccc 221void
4daaf3cb 222Ident::ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data)
05832ae1 223{
be005338 224 IdentClient *c = (IdentClient *)xcalloc(1, sizeof(*c));
05832ae1 225 IdentClient **C;
226 c->callback = callback;
fa80a8ef 227 c->callback_data = cbdataReference(callback_data);
62e76326 228
3d0ac046 229 for (C = &state->clients; *C; C = &(*C)->next);
05832ae1 230 *C = c;
231}
232
05832ae1 233/*
234 * start a TCP connection to the peer host on port 113
235 */
236void
00406b24 237Ident::Start(const Comm::ConnectionPointer &conn, IDCB * callback, void *data)
05832ae1 238{
239 IdentStateData *state;
05832ae1 240 char key1[IDENT_KEY_SZ];
241 char key2[IDENT_KEY_SZ];
b56b37cf 242 char key[IDENT_KEY_SZ*2+2]; // key1 + ',' + key2 + terminator
cc192b50 243
4dd643d5
AJ
244 conn->local.toUrl(key1, IDENT_KEY_SZ);
245 conn->remote.toUrl(key2, IDENT_KEY_SZ);
b56b37cf
AJ
246 const auto res = snprintf(key, sizeof(key), "%s,%s", key1, key2);
247 assert(res > 0);
248 assert(static_cast<std::make_unsigned<decltype(res)>::type>(res) < sizeof(key));
62e76326 249
4daaf3cb
AJ
250 if (!ident_hash) {
251 Init();
252 }
26ac0430 253 if ((state = (IdentStateData *)hash_lookup(ident_hash, key)) != NULL) {
4daaf3cb 254 ClientAdd(state, callback, data);
62e76326 255 return;
05832ae1 256 }
62e76326 257
20a17bdd 258 state = new IdentStateData;
186477c1 259 state->hash.key = xstrdup(key);
aed188fd 260
61beade2 261 // copy the conn details. We do not want the original FD to be re-used by IDENT.
9b7992d9 262 state->conn = conn->cloneIdentDetails();
aed188fd 263 // NP: use random port for secure outbound to IDENT_PORT
4dd643d5
AJ
264 state->conn->local.port(0);
265 state->conn->remote.port(IDENT_PORT);
cfd66529 266
173b7229
AJ
267 // build our query from the original connection details
268 state->queryMsg.init();
4391cd15 269 state->queryMsg.appendf("%d, %d\r\n", conn->remote.port(), conn->local.port());
173b7229 270
4daaf3cb 271 ClientAdd(state, callback, data);
186477c1 272 hash_join(ident_hash, &state->hash);
cfd66529
AJ
273
274 AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state));
855150a4 275 AsyncJob::Start(new Comm::ConnOpener(state->conn, call, Ident::TheConfig.timeout));
05832ae1 276}
277
278void
4daaf3cb 279Ident::Init(void)
ba1d2afa 280{
04f7fd38 281 if (ident_hash) {
4daaf3cb
AJ
282 debugs(30, DBG_CRITICAL, "WARNING: Ident already initialized.");
283 return;
284 }
285
30abd221 286 ident_hash = hash_create((HASHCMP *) strcmp,
62e76326 287 hashPrime(Squid_MaxFD / 8),
288 hash4);
f1443bd9 289}
4daaf3cb
AJ
290
291#endif /* USE_IDENT */
f53969cc 292