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