Ident.cc
Go to the documentation of this file.
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 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
26namespace Ident
27{
28
29#define IDENT_PORT 113
30#define IDENT_KEY_SZ 50
31#define IDENT_BUFSIZE 4096
32
33typedef struct _IdentClient {
36
39
41{
42public:
43 hash_link hash; /* must be first */
44private:
46
47public:
48 /* AsyncJob API emulated */
49 void deleteThis(const char *aReason);
50 void swanSong();
51
53 void notify(const char *result);
54
57 IdentClient *clients = nullptr;
59
62
63private:
64 // use deleteThis() to destroy
66};
67
69
70// TODO: make these all a series of Async job calls. They are self-contained callbacks now.
73static CLCB Close;
76static hash_table *ident_hash = nullptr;
77static void ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data);
78
79} // namespace Ident
80
82
83void
85{
86 debugs(30, 3, reason);
87 swanSong();
88 delete this;
89}
90
91void
93{
94 if (clients != nullptr)
95 notify(nullptr);
96}
97
99 assert(!clients);
100
101 if (Comm::IsConnOpen(conn)) {
103 conn->close();
104 }
105
107 xfree(hash.key);
108}
109
110void
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
124void
125Ident::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
135void
137{
138 debugs(30, 3, io.conn);
139 IdentStateData *state = (IdentStateData *)io.data;
140 state->deleteThis("timeout");
141}
142
143void
144Ident::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",
184 Comm::Write(conn, &state->queryMsg, writeCall);
185 AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply",
187 comm_read(conn, state->buf, IDENT_BUFSIZE, readCall);
188 AsyncCall::Pointer timeoutCall = commCbCall(5,4, "Ident::Timeout",
190 commSetConnTimeout(conn, Ident::TheConfig.timeout, timeoutCall);
191}
192
193void
194Ident::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
206void
207Ident::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
248void
249Ident::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 */
263void
264Ident::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,
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
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition: CommCalls.h:312
void CTCB(const CommTimeoutCbParams &params)
Definition: CommCalls.h:37
void IOCB(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag flag, int xerrno, void *data)
Definition: CommCalls.h:34
void CLCB(const CommCloseCbParams &params)
Definition: CommCalls.h:40
void CNCB(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
Definition: CommCalls.h:33
#define IDENT_KEY_SZ
Definition: Ident.cc:30
#define IDENT_PORT
Definition: Ident.cc:29
#define IDENT_BUFSIZE
Definition: Ident.cc:31
void IDCB(const char *ident, void *data)
Definition: Ident.h:17
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
Definition: Read.h:59
int conn
the current server connection FD
Definition: Transport.cc:26
#define assert(EX)
Definition: assert.h:17
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:265
#define cbdataReference(var)
Definition: cbdata.h:343
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
Comm::ConnectionPointer conn
Definition: CommCalls.h:80
void notify(const char *result)
notify all waiting IdentClient callbacks
Definition: Ident.cc:111
char buf[IDENT_BUFSIZE]
Definition: Ident.cc:58
hash_link hash
Definition: Ident.cc:43
Comm::ConnectionPointer conn
Definition: Ident.cc:55
CBDATA_CLASS(IdentStateData)
JobWait< Comm::ConnOpener > connWait
waits for a connection to the IDENT server to be established/opened
Definition: Ident.cc:61
IdentClient * clients
Definition: Ident.cc:57
void deleteThis(const char *aReason)
Definition: Ident.cc:84
MemBuf queryMsg
the lookup message sent to IDENT server
Definition: Ident.cc:56
void finish()
Definition: JobWait.cc:44
void start(const JobPointer &aJob, const AsyncCall::Pointer &aCallback)
starts waiting for the given job to call the given callback
Definition: JobWait.h:69
Definition: MemBuf.h:24
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
Definition: cbdata.cc:38
int commSetConnTimeout(const Comm::ConnectionPointer &conn, time_t timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:595
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:949
void comm_remove_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:978
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
int Squid_MaxFD
SQUIDCEXTERN hash_table * hash_create(HASHCMP *, int, HASHHASH *)
Definition: hash.cc:108
SQUIDCEXTERN void hash_join(hash_table *, hash_link *)
Definition: hash.cc:131
SQUIDCEXTERN int hashPrime(int n)
Definition: hash.cc:293
int HASHCMP(const void *, const void *)
Definition: hash.h:13
SQUIDCEXTERN void hash_remove_link(hash_table *, hash_link *)
Definition: hash.cc:220
SQUIDCEXTERN HASHHASH hash4
Definition: hash.h:46
SQUIDCEXTERN hash_link * hash_lookup(hash_table *, const void *)
Definition: hash.cc:146
static uint32 C
Definition: md4.c:43
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:33
Flag
Definition: Flag.h:15
@ OK
Definition: Flag.h:16
@ TIMEOUT
Definition: Flag.h:18
Ident Lookup API.
Definition: Config.h:17
struct Ident::_IdentClient IdentClient
static IOCB WriteFeedback
Definition: Ident.cc:72
static IOCB ReadReply
Definition: Ident.cc:71
IdentConfig TheConfig
Definition: Ident.cc:81
static CLCB Close
Definition: Ident.cc:73
CBDATA_CLASS_INIT(IdentStateData)
static hash_table * ident_hash
Definition: Ident.cc:76
static CNCB ConnectDone
Definition: Ident.cc:75
static void ClientAdd(IdentStateData *state, IDCB *callback, void *callback_data)
Definition: Ident.cc:249
static CTCB Timeout
Definition: Ident.cc:74
void Start(const Comm::ConnectionPointer &conn, IDCB *callback, void *cbdata)
Definition: Ident.cc:264
#define xfree
#define xstrdup
IDCB * callback
Definition: Ident.cc:34
struct _IdentClient * next
Definition: Ident.cc:37
void * callback_data
Definition: Ident.cc:35
static hash_table * hash
Definition: text_backend.cc:41
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
#define xisspace(x)
Definition: xis.h:15
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors