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