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