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