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