]>
Commit | Line | Data |
---|---|---|
f1443bd9 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
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. | |
f1443bd9 | 7 | */ |
bbc27441 AJ |
8 | |
9 | /* DEBUG: section 30 Ident (RFC 931) */ | |
10 | ||
a016163c | 11 | #include "squid.h" |
4daaf3cb AJ |
12 | |
13 | #if USE_IDENT | |
a46d2c0e | 14 | #include "comm.h" |
55cbb02b | 15 | #include "comm/Connection.h" |
aed188fd | 16 | #include "comm/ConnOpener.h" |
7e66d5e2 | 17 | #include "comm/Read.h" |
ec41b64c | 18 | #include "comm/Write.h" |
602d9612 | 19 | #include "CommCalls.h" |
582c2af2 | 20 | #include "globals.h" |
4daaf3cb AJ |
21 | #include "ident/Config.h" |
22 | #include "ident/Ident.h" | |
0eb49b6d | 23 | #include "MemBuf.h" |
f1443bd9 | 24 | |
04f7fd38 AJ |
25 | namespace Ident |
26 | { | |
4daaf3cb | 27 | |
f1443bd9 | 28 | #define IDENT_PORT 113 |
05832ae1 | 29 | #define IDENT_KEY_SZ 50 |
abd8f140 | 30 | #define IDENT_BUFSIZE 4096 |
05832ae1 | 31 | |
26ac0430 | 32 | typedef struct _IdentClient { |
05832ae1 | 33 | IDCB *callback; |
34 | void *callback_data; | |
62e76326 | 35 | |
05832ae1 | 36 | struct _IdentClient *next; |
2fadd50d | 37 | } IdentClient; |
05832ae1 | 38 | |
20a17bdd AJ |
39 | class IdentStateData |
40 | { | |
5c2f68b7 | 41 | public: |
f53969cc | 42 | hash_link hash; /* must be first */ |
5c2f68b7 AJ |
43 | private: |
44 | CBDATA_CLASS(IdentStateData); | |
45 | ||
20a17bdd AJ |
46 | public: |
47 | /* AsyncJob API emulated */ | |
48 | void deleteThis(const char *aReason); | |
49 | void swanSong(); | |
50 | ||
51 | /// notify all waiting IdentClient callbacks | |
52 | void notify(const char *result); | |
53 | ||
aed188fd | 54 | Comm::ConnectionPointer conn; |
173b7229 | 55 | MemBuf queryMsg; ///< the lookup message sent to IDENT server |
05832ae1 | 56 | IdentClient *clients; |
abd8f140 | 57 | char buf[IDENT_BUFSIZE]; |
20a17bdd AJ |
58 | }; |
59 | ||
60 | CBDATA_CLASS_INIT(IdentStateData); | |
f1443bd9 | 61 | |
e0d28505 | 62 | // TODO: make these all a series of Async job calls. They are self-contained callbacks now. |
4daaf3cb | 63 | static IOCB ReadReply; |
fdde4c47 | 64 | static IOCB WriteFeedback; |
575d05c4 | 65 | static CLCB Close; |
8d77a37c | 66 | static CTCB Timeout; |
4daaf3cb | 67 | static CNCB ConnectDone; |
05832ae1 | 68 | static hash_table *ident_hash = NULL; |
4daaf3cb | 69 | static void ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data); |
4daaf3cb | 70 | |
e5519212 | 71 | } // namespace Ident |
4daaf3cb AJ |
72 | |
73 | Ident::IdentConfig Ident::TheConfig; | |
05832ae1 | 74 | |
20a17bdd | 75 | void |
ced8def3 | 76 | Ident::IdentStateData::deleteThis(const char *) |
20a17bdd AJ |
77 | { |
78 | swanSong(); | |
79 | delete this; | |
80 | } | |
05832ae1 | 81 | |
52631ccc | 82 | void |
20a17bdd | 83 | Ident::IdentStateData::swanSong() |
05832ae1 | 84 | { |
20a17bdd AJ |
85 | if (clients != NULL) |
86 | notify(NULL); | |
62e76326 | 87 | |
20a17bdd AJ |
88 | if (Comm::IsConnOpen(conn)) { |
89 | comm_remove_close_handler(conn->fd, Ident::Close, this); | |
90 | conn->close(); | |
91 | } | |
62e76326 | 92 | |
20a17bdd AJ |
93 | hash_remove_link(ident_hash, (hash_link *) this); |
94 | xfree(hash.key); | |
95 | } | |
96 | ||
97 | void | |
98 | Ident::IdentStateData::notify(const char *result) | |
99 | { | |
100 | while (IdentClient *client = clients) { | |
62e76326 | 101 | void *cbdata; |
20a17bdd | 102 | clients = client->next; |
62e76326 | 103 | |
104 | if (cbdataReferenceValidDone(client->callback_data, &cbdata)) | |
105 | client->callback(result, cbdata); | |
106 | ||
107 | xfree(client); | |
05832ae1 | 108 | } |
109 | } | |
f1443bd9 | 110 | |
52631ccc | 111 | void |
575d05c4 | 112 | Ident::Close(const CommCloseCbParams ¶ms) |
f1443bd9 | 113 | { |
575d05c4 | 114 | IdentStateData *state = (IdentStateData *)params.data; |
20a17bdd | 115 | state->deleteThis("connection closed"); |
f1443bd9 | 116 | } |
117 | ||
52631ccc | 118 | void |
8d77a37c | 119 | Ident::Timeout(const CommTimeoutCbParams &io) |
86cf9987 | 120 | { |
8d77a37c | 121 | debugs(30, 3, HERE << io.conn); |
20a17bdd AJ |
122 | IdentStateData *state = (IdentStateData *)io.data; |
123 | state->deleteThis("timeout"); | |
86cf9987 | 124 | } |
125 | ||
52631ccc | 126 | void |
ced8def3 | 127 | Ident::ConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int, void *data) |
e5f6c5c2 | 128 | { |
be005338 | 129 | IdentStateData *state = (IdentStateData *)data; |
62e76326 | 130 | |
c8407295 | 131 | if (status != Comm::OK) { |
dbb4956c | 132 | if (status == Comm::TIMEOUT) |
aed188fd | 133 | debugs(30, 3, "IDENT connection timeout to " << state->conn->remote); |
20a17bdd | 134 | state->deleteThis(status == Comm::TIMEOUT ? "connect timeout" : "connect error"); |
62e76326 | 135 | return; |
f1443bd9 | 136 | } |
62e76326 | 137 | |
05832ae1 | 138 | /* |
fa80a8ef | 139 | * see if any of our clients still care |
05832ae1 | 140 | */ |
cfd66529 | 141 | IdentClient *c; |
05832ae1 | 142 | for (c = state->clients; c; c = c->next) { |
62e76326 | 143 | if (cbdataReferenceValid(c->callback_data)) |
144 | break; | |
05832ae1 | 145 | } |
62e76326 | 146 | |
ac2a30fc | 147 | if (c == NULL) { |
20a17bdd | 148 | state->deleteThis("client(s) aborted"); |
62e76326 | 149 | return; |
ac2a30fc | 150 | } |
62e76326 | 151 | |
20a17bdd | 152 | assert(conn != NULL && conn == state->conn); |
cfd66529 AJ |
153 | comm_add_close_handler(conn->fd, Ident::Close, state); |
154 | ||
fdde4c47 | 155 | AsyncCall::Pointer writeCall = commCbCall(5,4, "Ident::WriteFeedback", |
1057efa8 | 156 | CommIoCbPtrFun(Ident::WriteFeedback, state)); |
31ecec74 | 157 | Comm::Write(conn, &state->queryMsg, writeCall); |
8d77a37c | 158 | AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply", |
dc49061a | 159 | CommIoCbPtrFun(Ident::ReadReply, state)); |
8d77a37c AJ |
160 | comm_read(conn, state->buf, IDENT_BUFSIZE, readCall); |
161 | AsyncCall::Pointer timeoutCall = commCbCall(5,4, "Ident::Timeout", | |
dc49061a | 162 | CommTimeoutCbPtrFun(Ident::Timeout, state)); |
8d77a37c | 163 | commSetConnTimeout(conn, Ident::TheConfig.timeout, timeoutCall); |
f1443bd9 | 164 | } |
165 | ||
fdde4c47 | 166 | void |
ced8def3 | 167 | Ident::WriteFeedback(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int xerrno, void *data) |
fdde4c47 AJ |
168 | { |
169 | debugs(30, 5, HERE << conn << ": Wrote IDENT request " << len << " bytes."); | |
170 | ||
171 | // TODO handle write errors better. retry or abort? | |
c8407295 | 172 | if (flag != Comm::OK) { |
fdde4c47 | 173 | debugs(30, 2, HERE << conn << " err-flags=" << flag << " IDENT write error: " << xstrerr(xerrno)); |
20a17bdd AJ |
174 | IdentStateData *state = (IdentStateData *)data; |
175 | state->deleteThis("write error"); | |
fdde4c47 AJ |
176 | } |
177 | } | |
178 | ||
52631ccc | 179 | void |
ced8def3 | 180 | Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int, void *data) |
f1443bd9 | 181 | { |
be005338 | 182 | IdentStateData *state = (IdentStateData *)data; |
05832ae1 | 183 | char *ident = NULL; |
f1443bd9 | 184 | char *t = NULL; |
c4b7a5a9 | 185 | |
cfd66529 | 186 | assert(buf == state->buf); |
e0d28505 | 187 | assert(conn->fd == state->conn->fd); |
62e76326 | 188 | |
c8407295 | 189 | if (flag != Comm::OK || len <= 0) { |
20a17bdd | 190 | state->deleteThis("read error"); |
62e76326 | 191 | return; |
05832ae1 | 192 | } |
62e76326 | 193 | |
05832ae1 | 194 | /* |
195 | * XXX This isn't really very tolerant. It should read until EOL | |
196 | * or EOF and then decode the answer... If the reply is fragmented | |
197 | * then this will fail | |
198 | */ | |
199 | buf[len] = '\0'; | |
62e76326 | 200 | |
05832ae1 | 201 | if ((t = strchr(buf, '\r'))) |
62e76326 | 202 | *t = '\0'; |
203 | ||
05832ae1 | 204 | if ((t = strchr(buf, '\n'))) |
62e76326 | 205 | *t = '\0'; |
206 | ||
e0d28505 | 207 | debugs(30, 5, HERE << conn << ": Read '" << buf << "'"); |
62e76326 | 208 | |
05832ae1 | 209 | if (strstr(buf, "USERID")) { |
62e76326 | 210 | if ((ident = strrchr(buf, ':'))) { |
3d0ac046 | 211 | while (xisspace(*++ident)); |
20a17bdd AJ |
212 | if (ident && *ident == '\0') |
213 | ident = NULL; | |
214 | state->notify(ident); | |
62e76326 | 215 | } |
f1443bd9 | 216 | } |
62e76326 | 217 | |
20a17bdd | 218 | state->deleteThis("completed"); |
ba1d2afa | 219 | } |
220 | ||
52631ccc | 221 | void |
4daaf3cb | 222 | Ident::ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data) |
05832ae1 | 223 | { |
be005338 | 224 | IdentClient *c = (IdentClient *)xcalloc(1, sizeof(*c)); |
05832ae1 | 225 | IdentClient **C; |
226 | c->callback = callback; | |
fa80a8ef | 227 | c->callback_data = cbdataReference(callback_data); |
62e76326 | 228 | |
3d0ac046 | 229 | for (C = &state->clients; *C; C = &(*C)->next); |
05832ae1 | 230 | *C = c; |
231 | } | |
232 | ||
05832ae1 | 233 | /* |
234 | * start a TCP connection to the peer host on port 113 | |
235 | */ | |
236 | void | |
00406b24 | 237 | Ident::Start(const Comm::ConnectionPointer &conn, IDCB * callback, void *data) |
05832ae1 | 238 | { |
239 | IdentStateData *state; | |
05832ae1 | 240 | char key1[IDENT_KEY_SZ]; |
241 | char key2[IDENT_KEY_SZ]; | |
242 | char key[IDENT_KEY_SZ]; | |
cc192b50 | 243 | |
4dd643d5 AJ |
244 | conn->local.toUrl(key1, IDENT_KEY_SZ); |
245 | conn->remote.toUrl(key2, IDENT_KEY_SZ); | |
05832ae1 | 246 | snprintf(key, IDENT_KEY_SZ, "%s,%s", key1, key2); |
62e76326 | 247 | |
4daaf3cb AJ |
248 | if (!ident_hash) { |
249 | Init(); | |
250 | } | |
26ac0430 | 251 | if ((state = (IdentStateData *)hash_lookup(ident_hash, key)) != NULL) { |
4daaf3cb | 252 | ClientAdd(state, callback, data); |
62e76326 | 253 | return; |
05832ae1 | 254 | } |
62e76326 | 255 | |
20a17bdd | 256 | state = new IdentStateData; |
186477c1 | 257 | state->hash.key = xstrdup(key); |
aed188fd AJ |
258 | |
259 | // copy the conn details. We dont want the original FD to be re-used by IDENT. | |
260 | state->conn = conn->copyDetails(); | |
261 | // NP: use random port for secure outbound to IDENT_PORT | |
4dd643d5 AJ |
262 | state->conn->local.port(0); |
263 | state->conn->remote.port(IDENT_PORT); | |
cfd66529 | 264 | |
173b7229 AJ |
265 | // build our query from the original connection details |
266 | state->queryMsg.init(); | |
4391cd15 | 267 | state->queryMsg.appendf("%d, %d\r\n", conn->remote.port(), conn->local.port()); |
173b7229 | 268 | |
4daaf3cb | 269 | ClientAdd(state, callback, data); |
186477c1 | 270 | hash_join(ident_hash, &state->hash); |
cfd66529 AJ |
271 | |
272 | AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state)); | |
855150a4 | 273 | AsyncJob::Start(new Comm::ConnOpener(state->conn, call, Ident::TheConfig.timeout)); |
05832ae1 | 274 | } |
275 | ||
276 | void | |
4daaf3cb | 277 | Ident::Init(void) |
ba1d2afa | 278 | { |
04f7fd38 | 279 | if (ident_hash) { |
4daaf3cb AJ |
280 | debugs(30, DBG_CRITICAL, "WARNING: Ident already initialized."); |
281 | return; | |
282 | } | |
283 | ||
30abd221 | 284 | ident_hash = hash_create((HASHCMP *) strcmp, |
62e76326 | 285 | hashPrime(Squid_MaxFD / 8), |
286 | hash4); | |
f1443bd9 | 287 | } |
4daaf3cb AJ |
288 | |
289 | #endif /* USE_IDENT */ | |
f53969cc | 290 |