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