]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ident/Ident.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / ident / Ident.cc
1 /*
2 * Copyright (C) 1996-2014 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 "comm.h"
15 #include "comm/Connection.h"
16 #include "comm/ConnOpener.h"
17 #include "comm/Read.h"
18 #include "comm/Write.h"
19 #include "CommCalls.h"
20 #include "globals.h"
21 #include "ident/Config.h"
22 #include "ident/Ident.h"
23 #include "MemBuf.h"
24
25 namespace Ident
26 {
27
28 #define IDENT_PORT 113
29 #define IDENT_KEY_SZ 50
30 #define IDENT_BUFSIZE 4096
31
32 typedef struct _IdentClient {
33 IDCB *callback;
34 void *callback_data;
35
36 struct _IdentClient *next;
37 } IdentClient;
38
39 typedef struct _IdentStateData {
40 hash_link hash; /* must be first */
41 Comm::ConnectionPointer conn;
42 MemBuf queryMsg; ///< the lookup message sent to IDENT server
43 IdentClient *clients;
44 char buf[IDENT_BUFSIZE];
45 } IdentStateData;
46
47 // TODO: make these all a series of Async job calls. They are self-contained callbacks now.
48 static IOCB ReadReply;
49 static IOCB WriteFeedback;
50 static CLCB Close;
51 static CTCB Timeout;
52 static CNCB ConnectDone;
53 static hash_table *ident_hash = NULL;
54 static void ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data);
55 static void identCallback(IdentStateData * state, char *result);
56
57 } // namespace Ident
58
59 Ident::IdentConfig Ident::TheConfig;
60
61 /**** PRIVATE FUNCTIONS ****/
62
63 void
64 Ident::identCallback(IdentStateData * state, char *result)
65 {
66 IdentClient *client;
67
68 if (result && *result == '\0')
69 result = NULL;
70
71 while ((client = state->clients)) {
72 void *cbdata;
73 state->clients = client->next;
74
75 if (cbdataReferenceValidDone(client->callback_data, &cbdata))
76 client->callback(result, cbdata);
77
78 xfree(client);
79 }
80 }
81
82 void
83 Ident::Close(const CommCloseCbParams &params)
84 {
85 IdentStateData *state = (IdentStateData *)params.data;
86 identCallback(state, NULL);
87 state->conn->close();
88 hash_remove_link(ident_hash, (hash_link *) state);
89 xfree(state->hash.key);
90 cbdataFree(state);
91 }
92
93 void
94 Ident::Timeout(const CommTimeoutCbParams &io)
95 {
96 debugs(30, 3, HERE << io.conn);
97 io.conn->close();
98 }
99
100 void
101 Ident::ConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
102 {
103 IdentStateData *state = (IdentStateData *)data;
104
105 if (status != Comm::OK) {
106 if (status == Comm::TIMEOUT)
107 debugs(30, 3, "IDENT connection timeout to " << state->conn->remote);
108 Ident::identCallback(state, NULL);
109 return;
110 }
111
112 assert(conn != NULL && conn == state->conn);
113
114 /*
115 * see if any of our clients still care
116 */
117 IdentClient *c;
118 for (c = state->clients; c; c = c->next) {
119 if (cbdataReferenceValid(c->callback_data))
120 break;
121 }
122
123 if (c == NULL) {
124 /* no clients care */
125 conn->close();
126 return;
127 }
128
129 comm_add_close_handler(conn->fd, Ident::Close, state);
130
131 AsyncCall::Pointer writeCall = commCbCall(5,4, "Ident::WriteFeedback",
132 CommIoCbPtrFun(Ident::WriteFeedback, state));
133 Comm::Write(conn, &state->queryMsg, writeCall);
134 AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply",
135 CommIoCbPtrFun(Ident::ReadReply, state));
136 comm_read(conn, state->buf, IDENT_BUFSIZE, readCall);
137 AsyncCall::Pointer timeoutCall = commCbCall(5,4, "Ident::Timeout",
138 CommTimeoutCbPtrFun(Ident::Timeout, state));
139 commSetConnTimeout(conn, Ident::TheConfig.timeout, timeoutCall);
140 }
141
142 void
143 Ident::WriteFeedback(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
144 {
145 debugs(30, 5, HERE << conn << ": Wrote IDENT request " << len << " bytes.");
146
147 // TODO handle write errors better. retry or abort?
148 if (flag != Comm::OK) {
149 debugs(30, 2, HERE << conn << " err-flags=" << flag << " IDENT write error: " << xstrerr(xerrno));
150 conn->close();
151 }
152 }
153
154 void
155 Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
156 {
157 IdentStateData *state = (IdentStateData *)data;
158 char *ident = NULL;
159 char *t = NULL;
160
161 assert(buf == state->buf);
162 assert(conn->fd == state->conn->fd);
163
164 if (flag != Comm::OK || len <= 0) {
165 state->conn->close();
166 return;
167 }
168
169 /*
170 * XXX This isn't really very tolerant. It should read until EOL
171 * or EOF and then decode the answer... If the reply is fragmented
172 * then this will fail
173 */
174 buf[len] = '\0';
175
176 if ((t = strchr(buf, '\r')))
177 *t = '\0';
178
179 if ((t = strchr(buf, '\n')))
180 *t = '\0';
181
182 debugs(30, 5, HERE << conn << ": Read '" << buf << "'");
183
184 if (strstr(buf, "USERID")) {
185 if ((ident = strrchr(buf, ':'))) {
186 while (xisspace(*++ident));
187 Ident::identCallback(state, ident);
188 }
189 }
190
191 state->conn->close();
192 }
193
194 void
195 Ident::ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data)
196 {
197 IdentClient *c = (IdentClient *)xcalloc(1, sizeof(*c));
198 IdentClient **C;
199 c->callback = callback;
200 c->callback_data = cbdataReference(callback_data);
201
202 for (C = &state->clients; *C; C = &(*C)->next);
203 *C = c;
204 }
205
206 CBDATA_TYPE(IdentStateData);
207
208 /**** PUBLIC FUNCTIONS ****/
209
210 /*
211 * start a TCP connection to the peer host on port 113
212 */
213 void
214 Ident::Start(const Comm::ConnectionPointer &conn, IDCB * callback, void *data)
215 {
216 IdentStateData *state;
217 char key1[IDENT_KEY_SZ];
218 char key2[IDENT_KEY_SZ];
219 char key[IDENT_KEY_SZ];
220
221 conn->local.toUrl(key1, IDENT_KEY_SZ);
222 conn->remote.toUrl(key2, IDENT_KEY_SZ);
223 snprintf(key, IDENT_KEY_SZ, "%s,%s", key1, key2);
224
225 if (!ident_hash) {
226 Init();
227 }
228 if ((state = (IdentStateData *)hash_lookup(ident_hash, key)) != NULL) {
229 ClientAdd(state, callback, data);
230 return;
231 }
232
233 CBDATA_INIT_TYPE(IdentStateData);
234 state = cbdataAlloc(IdentStateData);
235 state->hash.key = xstrdup(key);
236
237 // copy the conn details. We dont want the original FD to be re-used by IDENT.
238 state->conn = conn->copyDetails();
239 // NP: use random port for secure outbound to IDENT_PORT
240 state->conn->local.port(0);
241 state->conn->remote.port(IDENT_PORT);
242
243 // build our query from the original connection details
244 state->queryMsg.init();
245 state->queryMsg.Printf("%d, %d\r\n", conn->remote.port(), conn->local.port());
246
247 ClientAdd(state, callback, data);
248 hash_join(ident_hash, &state->hash);
249
250 AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state));
251 AsyncJob::Start(new Comm::ConnOpener(state->conn, call, Ident::TheConfig.timeout));
252 }
253
254 void
255 Ident::Init(void)
256 {
257 if (ident_hash) {
258 debugs(30, DBG_CRITICAL, "WARNING: Ident already initialized.");
259 return;
260 }
261
262 ident_hash = hash_create((HASHCMP *) strcmp,
263 hashPrime(Squid_MaxFD / 8),
264 hash4);
265 }
266
267 #endif /* USE_IDENT */