]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ident/Ident.cc
Merged from trunk r13474.
[thirdparty/squid.git] / src / ident / Ident.cc
1 /*
2 * DEBUG: section 30 Ident (RFC 931)
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32 #include "squid.h"
33
34 #if USE_IDENT
35 #include "comm.h"
36 #include "comm/Connection.h"
37 #include "comm/ConnOpener.h"
38 #include "comm/Read.h"
39 #include "comm/Write.h"
40 #include "CommCalls.h"
41 #include "globals.h"
42 #include "ident/Config.h"
43 #include "ident/Ident.h"
44 #include "MemBuf.h"
45
46 namespace Ident
47 {
48
49 #define IDENT_PORT 113
50 #define IDENT_KEY_SZ 50
51 #define IDENT_BUFSIZE 4096
52
53 typedef struct _IdentClient {
54 IDCB *callback;
55 void *callback_data;
56
57 struct _IdentClient *next;
58 } IdentClient;
59
60 typedef struct _IdentStateData {
61 hash_link hash; /* must be first */
62 Comm::ConnectionPointer conn;
63 MemBuf queryMsg; ///< the lookup message sent to IDENT server
64 IdentClient *clients;
65 char buf[IDENT_BUFSIZE];
66 } IdentStateData;
67
68 // TODO: make these all a series of Async job calls. They are self-contained callbacks now.
69 static IOCB ReadReply;
70 static IOCB WriteFeedback;
71 static CLCB Close;
72 static CTCB Timeout;
73 static CNCB ConnectDone;
74 static hash_table *ident_hash = NULL;
75 static void ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data);
76 static void identCallback(IdentStateData * state, char *result);
77
78 } // namespace Ident
79
80 Ident::IdentConfig Ident::TheConfig;
81
82 /**** PRIVATE FUNCTIONS ****/
83
84 void
85 Ident::identCallback(IdentStateData * state, char *result)
86 {
87 IdentClient *client;
88
89 if (result && *result == '\0')
90 result = NULL;
91
92 while ((client = state->clients)) {
93 void *cbdata;
94 state->clients = client->next;
95
96 if (cbdataReferenceValidDone(client->callback_data, &cbdata))
97 client->callback(result, cbdata);
98
99 xfree(client);
100 }
101 }
102
103 void
104 Ident::Close(const CommCloseCbParams &params)
105 {
106 IdentStateData *state = (IdentStateData *)params.data;
107 identCallback(state, NULL);
108 state->conn->close();
109 hash_remove_link(ident_hash, (hash_link *) state);
110 xfree(state->hash.key);
111 cbdataFree(state);
112 }
113
114 void
115 Ident::Timeout(const CommTimeoutCbParams &io)
116 {
117 debugs(30, 3, HERE << io.conn);
118 io.conn->close();
119 }
120
121 void
122 Ident::ConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int xerrno, void *data)
123 {
124 IdentStateData *state = (IdentStateData *)data;
125
126 if (status != Comm::OK) {
127 if (status == Comm::TIMEOUT) {
128 debugs(30, 3, "IDENT connection timeout to " << state->conn->remote);
129 }
130 return;
131 }
132
133 assert(conn != NULL && conn == state->conn);
134
135 /*
136 * see if any of our clients still care
137 */
138 IdentClient *c;
139 for (c = state->clients; c; c = c->next) {
140 if (cbdataReferenceValid(c->callback_data))
141 break;
142 }
143
144 if (c == NULL) {
145 /* no clients care */
146 conn->close();
147 return;
148 }
149
150 comm_add_close_handler(conn->fd, Ident::Close, state);
151
152 AsyncCall::Pointer writeCall = commCbCall(5,4, "Ident::WriteFeedback",
153 CommIoCbPtrFun(Ident::WriteFeedback, state));
154 Comm::Write(conn, &state->queryMsg, writeCall);
155 AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply",
156 CommIoCbPtrFun(Ident::ReadReply, state));
157 comm_read(conn, state->buf, IDENT_BUFSIZE, readCall);
158 AsyncCall::Pointer timeoutCall = commCbCall(5,4, "Ident::Timeout",
159 CommTimeoutCbPtrFun(Ident::Timeout, state));
160 commSetConnTimeout(conn, Ident::TheConfig.timeout, timeoutCall);
161 }
162
163 void
164 Ident::WriteFeedback(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
165 {
166 debugs(30, 5, HERE << conn << ": Wrote IDENT request " << len << " bytes.");
167
168 // TODO handle write errors better. retry or abort?
169 if (flag != Comm::OK) {
170 debugs(30, 2, HERE << conn << " err-flags=" << flag << " IDENT write error: " << xstrerr(xerrno));
171 conn->close();
172 }
173 }
174
175 void
176 Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
177 {
178 IdentStateData *state = (IdentStateData *)data;
179 char *ident = NULL;
180 char *t = NULL;
181
182 assert(buf == state->buf);
183 assert(conn->fd == state->conn->fd);
184
185 if (flag != Comm::OK || len <= 0) {
186 state->conn->close();
187 return;
188 }
189
190 /*
191 * XXX This isn't really very tolerant. It should read until EOL
192 * or EOF and then decode the answer... If the reply is fragmented
193 * then this will fail
194 */
195 buf[len] = '\0';
196
197 if ((t = strchr(buf, '\r')))
198 *t = '\0';
199
200 if ((t = strchr(buf, '\n')))
201 *t = '\0';
202
203 debugs(30, 5, HERE << conn << ": Read '" << buf << "'");
204
205 if (strstr(buf, "USERID")) {
206 if ((ident = strrchr(buf, ':'))) {
207 while (xisspace(*++ident));
208 Ident::identCallback(state, ident);
209 }
210 }
211
212 state->conn->close();
213 }
214
215 void
216 Ident::ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data)
217 {
218 IdentClient *c = (IdentClient *)xcalloc(1, sizeof(*c));
219 IdentClient **C;
220 c->callback = callback;
221 c->callback_data = cbdataReference(callback_data);
222
223 for (C = &state->clients; *C; C = &(*C)->next);
224 *C = c;
225 }
226
227 CBDATA_TYPE(IdentStateData);
228
229 /**** PUBLIC FUNCTIONS ****/
230
231 /*
232 * start a TCP connection to the peer host on port 113
233 */
234 void
235 Ident::Start(const Comm::ConnectionPointer &conn, IDCB * callback, void *data)
236 {
237 IdentStateData *state;
238 char key1[IDENT_KEY_SZ];
239 char key2[IDENT_KEY_SZ];
240 char key[IDENT_KEY_SZ];
241
242 conn->local.toUrl(key1, IDENT_KEY_SZ);
243 conn->remote.toUrl(key2, IDENT_KEY_SZ);
244 snprintf(key, IDENT_KEY_SZ, "%s,%s", key1, key2);
245
246 if (!ident_hash) {
247 Init();
248 }
249 if ((state = (IdentStateData *)hash_lookup(ident_hash, key)) != NULL) {
250 ClientAdd(state, callback, data);
251 return;
252 }
253
254 CBDATA_INIT_TYPE(IdentStateData);
255 state = cbdataAlloc(IdentStateData);
256 state->hash.key = xstrdup(key);
257
258 // copy the conn details. We dont want the original FD to be re-used by IDENT.
259 state->conn = conn->copyDetails();
260 // NP: use random port for secure outbound to IDENT_PORT
261 state->conn->local.port(0);
262 state->conn->remote.port(IDENT_PORT);
263
264 // build our query from the original connection details
265 state->queryMsg.init();
266 state->queryMsg.Printf("%d, %d\r\n", conn->remote.port(), conn->local.port());
267
268 ClientAdd(state, callback, data);
269 hash_join(ident_hash, &state->hash);
270
271 AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state));
272 AsyncJob::Start(new Comm::ConnOpener(state->conn, call, Ident::TheConfig.timeout));
273 }
274
275 void
276 Ident::Init(void)
277 {
278 if (ident_hash) {
279 debugs(30, DBG_CRITICAL, "WARNING: Ident already initialized.");
280 return;
281 }
282
283 ident_hash = hash_create((HASHCMP *) strcmp,
284 hashPrime(Squid_MaxFD / 8),
285 hash4);
286 }
287
288 #endif /* USE_IDENT */