chiark / gitweb /
Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/mLib
[mLib] / sel / ident.c
CommitLineData
7bd2091e 1/* -*-c-*-
7bd2091e 2 *
3 * Nonblocking RFC931 client
4 *
5 * (c) 1999 Mark Wooding
6 */
7
d4efbcd9 8/*----- Licensing notice --------------------------------------------------*
7bd2091e 9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
d4efbcd9 16 *
7bd2091e 17 * mLib is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
d4efbcd9 21 *
7bd2091e 22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
7bd2091e 28/*----- Header files ------------------------------------------------------*/
29
30#include <ctype.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
35#include <sys/types.h>
36#include <unistd.h>
37
38#include <sys/socket.h>
39#include <netinet/in.h>
40#include <arpa/inet.h>
41#include <netdb.h>
42
43#include "conn.h"
44#include "dstr.h"
45#include "exc.h"
46#include "ident.h"
47#include "selbuf.h"
48
49/*----- Main code ---------------------------------------------------------*/
50
51/* --- @next@ --- *
52 *
53 * Arguments: @char **pp@ = address of string pointer
54 *
55 * Returns: Address of next token.
56 *
57 * Use: Reads the next token from the result string. Tokens are
58 * terminated by whitespace, or `:' or `,' characters. The
59 * result string has had `\' escapes removed; it's stored in
60 * the same memory as the input string. The actual content
61 * of the delimiter doesn't seem to be interesting, so it
62 * gets thrown away.
63 */
64
65static char *next(char **pp)
66{
67 char *p, *q, *r;
68
69 /* --- Deal with reads past the end of the string --- */
70
71 if (!*pp)
72 return ("");
73
74 /* --- Initialize various pointers into the string --- */
75
76 p = q = r = *pp;
77
78 /* --- Skip past any leading whitespace --- */
79
80 while (isspace((unsigned char)*p))
81 p++;
82
83 /* --- Now start work on the string itself --- */
84
85 for (;;) {
86 if (*p == 0 || *p == ':' || *p == ',' || isspace((unsigned char)*p))
87 break;
88 else if (*p == '\\') {
89 p++;
90 if (!*p) {
91 *q++ = '\\';
92 break;
93 }
94 }
95 *q++ = *p++;
96 }
97
98 /* --- Tidy up afterwards --- */
99
100 while (isspace((unsigned char)*p))
101 p++;
102 if (*p == 0)
103 *pp = 0;
104 else if (*p == ':' || *p == ',')
105 *pp = p + 1;
106 else
107 *pp = p;
108 *q = 0;
109
110 return (r);
111}
112
113/* --- @parse@ --- *
114 *
115 * Arguments: @char *p@ = pointer to input string from identd
116 * @ident_reply *i@ = pointer to output block
117 *
118 * Returns: ---
119 *
120 * Use: Parses a result string from an RFC931 (identd) server.
121 */
122
123static void parse(char *p, ident_reply *i)
124{
125 char *q;
126
127 /* --- Read the source and destination port numbers --- */
128
129 i->sport = atoi(next(&p));
130 i->dport = atoi(next(&p));
131
132 /* --- Find out what sort of a reply this is --- */
133
134 q = next(&p);
135 if (strcmp(q, "USERID") == 0) {
136 i->type = IDENT_USERID;
137 i->u.userid.os = next(&p);
138 i->u.userid.user = next(&p);
139 } else if (strcmp(q, "ERROR") == 0) {
140 i->type = IDENT_ERROR;
141 i->u.error = next(&p);
142 } else
143 i->type = IDENT_BAD;
144}
145
146/* --- @line@ --- *
147 *
148 * Arguments: @char *s@ = pointer to string from ident server
b342b114 149 * @size_t len@ = length of the line
7bd2091e 150 * @void *p@ = pointer to my request block
151 *
152 * Returns: ---
153 *
154 * Use: Handles a string from an ident server.
155 */
156
b342b114 157static void line(char *s, size_t len, void *p)
7bd2091e 158{
159 ident_request *rq = p;
160
161 rq->state = IDENT_DONE;
7bd2091e 162 close(rq->b.reader.fd);
163 if (!s)
164 rq->func(0, rq->p);
165 else {
166 ident_reply i;
167 parse(s, &i);
168 rq->func(&i, rq->p);
169 }
e2a18bd0 170 selbuf_destroy(&rq->b);
7bd2091e 171}
172
173/* --- @connected@ --- *
174 *
175 * Arguments: @int fd@ = file descriptor
176 * @void *p@ = pointer to request block
177 *
178 * Returns: ---
179 *
180 * Use: Handles a connection to a remote ident server.
181 */
182
183static void connected(int fd, void *p)
184{
185 ident_request *rq = p;
186 dstr d = DSTR_INIT;
187
188 /* --- Handle an error during the connect --- */
189
190 if (fd < 0)
191 goto fail_0;
192
193 /* --- Initialize the string to send to the remote host --- */
194
195 TRY {
196 dstr_putf(&d, "%u, %u\r\n",
197 ntohs(rq->remote.sin_port), ntohs(rq->local.sin_port));
198 } CATCH switch (exc_type) {
199 case EXC_NOMEM:
200 EXIT_TRY;
201 goto fail_1;
202 default:
203 RETHROW;
204 } END_TRY;
205
206 /* --- Do the rest of the work --- */
207
208 if (write(fd, d.buf, d.len) < d.len)
209 goto fail_1;
210 dstr_destroy(&d);
211 rq->state = IDENT_READ;
212 selbuf_init(&rq->b, rq->s, fd, line, rq);
213 return;
214
215 /* --- Tidy up after misfortunes --- */
216
217fail_1:
218 dstr_destroy(&d);
219 close(fd);
220fail_0:
221 rq->state = IDENT_DONE;
222 rq->func(0, rq->p);
223}
224
225/* --- @ident_abort@ --- *
226 *
227 * Arguments: @ident_request *rq@ = pointer to request block
228 *
229 * Returns: ---
230 *
231 * Use: Cancels an ident request in progress.
232 */
233
234void ident_abort(ident_request *rq)
235{
236 switch (rq->state) {
237 case IDENT_CONN:
238 conn_kill(&rq->c);
239 break;
240 case IDENT_READ:
7bd2091e 241 close(rq->b.reader.fd);
e2a18bd0 242 selbuf_destroy(&rq->b);
7bd2091e 243 break;
244 }
245}
246
247/* --- @go@ --- *
248 *
249 * Arguments: @ident_request *rq@ = pointer to request block
250 *
251 * Returns: ---
252 *
253 * Use: Starts a connection to the remote ident server.
254 */
255
256static void go(ident_request *rq)
257{
258 int fd;
259 struct sockaddr_in sin;
260 int opt;
261
262 /* --- Create the socket I'll use --- */
d4efbcd9 263
7bd2091e 264 if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
265 goto fail_0;
266 memset(&sin, 0, sizeof(sin));
267 sin.sin_family = AF_INET;
268 sin.sin_port = 0;
269 sin.sin_addr = rq->local.sin_addr;
270 if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)))
271 goto fail_1;
272
273 /* --- Out-of-band data would confuse us --- */
274
275 opt = 1;
276 setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt));
277
278 /* --- Start a connection to the remote server --- */
279
280 sin.sin_family = AF_INET;
281 sin.sin_port = htons(113);
282 sin.sin_addr = rq->remote.sin_addr;
e569f554 283 if (conn_init(&rq->c, rq->s, fd, (struct sockaddr *)&sin, sizeof(sin),
284 connected, rq))
4e377975 285 goto fail_0;
7bd2091e 286
287 /* --- Finish off initializing the block --- */
288
289 rq->state = IDENT_CONN;
290 return;
291
292 /* --- Tidy up after lossage --- */
293
294fail_1:
295 close(fd);
296fail_0:
297 rq->state = IDENT_DONE;
298 rq->func(0, rq->p);
299}
300
301/* --- @ident@ --- *
302 *
303 * Arguments: @ident_request *rq@ = pointer to request block
304 * @sel_state *s@ = I/O multiplexor
305 * @const struct sockaddr_in *local, *remote@ = addresses
306 * @void (*func)(ident_reply *i, void *p)@ = handler function
307 * @void *p@ = argument for handler
308 *
309 * Returns: ---
310 *
311 * Use: Initializes an ident request.
312 */
313
314void ident(ident_request *rq, sel_state *s,
315 const struct sockaddr_in *local,
d4efbcd9 316 const struct sockaddr_in *remote,
7bd2091e 317 void (*func)(ident_reply */*i*/, void */*p*/),
318 void *p)
319{
320 memcpy(&rq->local, local, sizeof(rq->local));
321 memcpy(&rq->remote, remote, sizeof(rq->remote));
322 rq->func = func;
323 rq->p = p;
324 rq->s = s;
325 go(rq);
326}
327
328/* --- @ident_socket@ --- *
329 *
330 * Arguments: @ident_request *rq@ = pointer to request block
331 * @sel_state *s@ = I/O multiplexor
332 * @int sk@ = connected socket file descriptor
333 * @void (*func)(ident_reply *i, void *p)@ = handler function
334 * @void *p@ = argument for handler
335 *
336 * Returns: ---
337 *
338 * Use: An alternative interface to @ident@. Initializes an ident
339 * request from a connected socket, rather than from an explicit
340 * address. This will call @getsockname@ and @getpeername@ to
341 * find out what the socket is actually connected to, which adds
342 * convenience but wastes time.
343 */
344
345void ident_socket(ident_request *rq, sel_state *s, int sk,
346 void (*func)(ident_reply */*i*/, void */*p*/),
347 void *p)
348{
0ce34834 349 socklen_t sinsz;
7bd2091e 350 if ((sinsz = sizeof(struct sockaddr_in),
351 getsockname(sk, (struct sockaddr *)&rq->local, &sinsz)) ||
352 (sinsz = sizeof(struct sockaddr_in),
353 getpeername(sk, (struct sockaddr *)&rq->remote, &sinsz))) {
354 rq->state = IDENT_DONE;
355 func(0, p);
356 return;
357 }
358
359 rq->func = func;
360 rq->p = p;
361 rq->s = s;
362 go(rq);
363}
364
365/*----- That's all, folks -------------------------------------------------*/