]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ident/Ident.cc
2 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 30 Ident (RFC 931) */
14 #include "base/JobWait.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"
22 #include "ident/Config.h"
23 #include "ident/Ident.h"
29 #define IDENT_PORT 113
30 #define IDENT_KEY_SZ 50
31 #define IDENT_BUFSIZE 4096
33 typedef struct _IdentClient
{
37 struct _IdentClient
*next
;
43 hash_link hash
; /* must be first */
45 CBDATA_CLASS(IdentStateData
);
48 /* AsyncJob API emulated */
49 void deleteThis(const char *aReason
);
52 /// notify all waiting IdentClient callbacks
53 void notify(const char *result
);
55 Comm::ConnectionPointer conn
;
56 MemBuf queryMsg
; ///< the lookup message sent to IDENT server
57 IdentClient
*clients
= nullptr;
58 char buf
[IDENT_BUFSIZE
];
60 /// waits for a connection to the IDENT server to be established/opened
61 JobWait
<Comm::ConnOpener
> connWait
;
64 // use deleteThis() to destroy
68 CBDATA_CLASS_INIT(IdentStateData
);
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
;
75 static CNCB ConnectDone
;
76 static hash_table
*ident_hash
= nullptr;
77 static void ClientAdd(IdentStateData
* state
, IDCB
* callback
, void *callback_data
);
81 Ident::IdentConfig
Ident::TheConfig
;
84 Ident::IdentStateData::deleteThis(const char *reason
)
86 debugs(30, 3, reason
);
92 Ident::IdentStateData::swanSong()
94 if (clients
!= nullptr)
98 Ident::IdentStateData::~IdentStateData() {
101 if (Comm::IsConnOpen(conn
)) {
102 comm_remove_close_handler(conn
->fd
, Ident::Close
, this);
106 hash_remove_link(ident_hash
, (hash_link
*) this);
111 Ident::IdentStateData::notify(const char *result
)
113 while (IdentClient
*client
= clients
) {
115 clients
= client
->next
;
117 if (cbdataReferenceValidDone(client
->callback_data
, &cbdata
))
118 client
->callback(result
, cbdata
);
125 Ident::Close(const CommCloseCbParams
¶ms
)
127 IdentStateData
*state
= (IdentStateData
*)params
.data
;
129 state
->conn
->noteClosure();
130 state
->conn
= nullptr;
132 state
->deleteThis("connection closed");
136 Ident::Timeout(const CommTimeoutCbParams
&io
)
138 debugs(30, 3, io
.conn
);
139 IdentStateData
*state
= (IdentStateData
*)io
.data
;
140 state
->deleteThis("timeout");
144 Ident::ConnectDone(const Comm::ConnectionPointer
&conn
, Comm::Flag status
, int, void *data
)
146 IdentStateData
*state
= (IdentStateData
*)data
;
147 state
->connWait
.finish();
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
154 assert(conn
); // but may be closed
155 assert(!state
->conn
);
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");
166 * see if any of our clients still care
169 for (c
= state
->clients
; c
; c
= c
->next
) {
170 if (cbdataReferenceValid(c
->callback_data
))
175 state
->deleteThis("client(s) aborted");
179 assert(state
->conn
->isOpen());
180 comm_add_close_handler(state
->conn
->fd
, Ident::Close
, state
);
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
);
194 Ident::WriteFeedback(const Comm::ConnectionPointer
&conn
, char *, size_t len
, Comm::Flag flag
, int xerrno
, void *data
)
196 debugs(30, 5, conn
<< ": Wrote IDENT request " << len
<< " bytes.");
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");
207 Ident::ReadReply(const Comm::ConnectionPointer
&conn
, char *buf
, size_t len
, Comm::Flag flag
, int, void *data
)
209 IdentStateData
*state
= (IdentStateData
*)data
;
210 char *ident
= nullptr;
213 assert(buf
== state
->buf
);
214 assert(conn
->fd
== state
->conn
->fd
);
216 if (flag
!= Comm::OK
|| len
<= 0) {
217 state
->deleteThis("read error");
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
228 if ((t
= strchr(buf
, '\r')))
231 if ((t
= strchr(buf
, '\n')))
234 debugs(30, 5, conn
<< ": Read '" << buf
<< "'");
236 if (strstr(buf
, "USERID")) {
237 if ((ident
= strrchr(buf
, ':'))) {
238 while (xisspace(*++ident
));
239 if (ident
&& *ident
== '\0')
241 state
->notify(ident
);
245 state
->deleteThis("completed");
249 Ident::ClientAdd(IdentStateData
* state
, IDCB
* callback
, void *callback_data
)
251 IdentClient
*c
= (IdentClient
*)xcalloc(1, sizeof(*c
));
253 c
->callback
= callback
;
254 c
->callback_data
= cbdataReference(callback_data
);
256 for (C
= &state
->clients
; *C
; C
= &(*C
)->next
);
261 * start a TCP connection to the peer host on port 113
264 Ident::Start(const Comm::ConnectionPointer
&conn
, IDCB
* callback
, void *data
)
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
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
);
275 assert(static_cast<unsigned int>(res
) < sizeof(key
));
278 ident_hash
= hash_create((HASHCMP
*) strcmp
,
279 hashPrime(Squid_MaxFD
/ 8),
282 if ((state
= (IdentStateData
*)hash_lookup(ident_hash
, key
)) != nullptr) {
283 ClientAdd(state
, callback
, data
);
287 state
= new IdentStateData
;
288 state
->hash
.key
= xstrdup(key
);
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
);
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());
300 ClientAdd(state
, callback
, data
);
301 hash_join(ident_hash
, &state
->hash
);
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
);
308 #endif /* USE_IDENT */