X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/dd3c57bc8cac59e0d657ee665ce462988d27d714..18c831dcd0ae4d660c70ccac69d27ed2a97851be:/sel/ident.c diff --git a/sel/ident.c b/sel/ident.c new file mode 100644 index 0000000..bff6ea2 --- /dev/null +++ b/sel/ident.c @@ -0,0 +1,365 @@ +/* -*-c-*- + * + * Nonblocking RFC931 client + * + * (c) 1999 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "conn.h" +#include "dstr.h" +#include "exc.h" +#include "ident.h" +#include "selbuf.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @next@ --- * + * + * Arguments: @char **pp@ = address of string pointer + * + * Returns: Address of next token. + * + * Use: Reads the next token from the result string. Tokens are + * terminated by whitespace, or `:' or `,' characters. The + * result string has had `\' escapes removed; it's stored in + * the same memory as the input string. The actual content + * of the delimiter doesn't seem to be interesting, so it + * gets thrown away. + */ + +static char *next(char **pp) +{ + char *p, *q, *r; + + /* --- Deal with reads past the end of the string --- */ + + if (!*pp) + return (""); + + /* --- Initialize various pointers into the string --- */ + + p = q = r = *pp; + + /* --- Skip past any leading whitespace --- */ + + while (isspace((unsigned char)*p)) + p++; + + /* --- Now start work on the string itself --- */ + + for (;;) { + if (*p == 0 || *p == ':' || *p == ',' || isspace((unsigned char)*p)) + break; + else if (*p == '\\') { + p++; + if (!*p) { + *q++ = '\\'; + break; + } + } + *q++ = *p++; + } + + /* --- Tidy up afterwards --- */ + + while (isspace((unsigned char)*p)) + p++; + if (*p == 0) + *pp = 0; + else if (*p == ':' || *p == ',') + *pp = p + 1; + else + *pp = p; + *q = 0; + + return (r); +} + +/* --- @parse@ --- * + * + * Arguments: @char *p@ = pointer to input string from identd + * @ident_reply *i@ = pointer to output block + * + * Returns: --- + * + * Use: Parses a result string from an RFC931 (identd) server. + */ + +static void parse(char *p, ident_reply *i) +{ + char *q; + + /* --- Read the source and destination port numbers --- */ + + i->sport = atoi(next(&p)); + i->dport = atoi(next(&p)); + + /* --- Find out what sort of a reply this is --- */ + + q = next(&p); + if (strcmp(q, "USERID") == 0) { + i->type = IDENT_USERID; + i->u.userid.os = next(&p); + i->u.userid.user = next(&p); + } else if (strcmp(q, "ERROR") == 0) { + i->type = IDENT_ERROR; + i->u.error = next(&p); + } else + i->type = IDENT_BAD; +} + +/* --- @line@ --- * + * + * Arguments: @char *s@ = pointer to string from ident server + * @size_t len@ = length of the line + * @void *p@ = pointer to my request block + * + * Returns: --- + * + * Use: Handles a string from an ident server. + */ + +static void line(char *s, size_t len, void *p) +{ + ident_request *rq = p; + + rq->state = IDENT_DONE; + close(rq->b.reader.fd); + if (!s) + rq->func(0, rq->p); + else { + ident_reply i; + parse(s, &i); + rq->func(&i, rq->p); + } + selbuf_destroy(&rq->b); +} + +/* --- @connected@ --- * + * + * Arguments: @int fd@ = file descriptor + * @void *p@ = pointer to request block + * + * Returns: --- + * + * Use: Handles a connection to a remote ident server. + */ + +static void connected(int fd, void *p) +{ + ident_request *rq = p; + dstr d = DSTR_INIT; + + /* --- Handle an error during the connect --- */ + + if (fd < 0) + goto fail_0; + + /* --- Initialize the string to send to the remote host --- */ + + TRY { + dstr_putf(&d, "%u, %u\r\n", + ntohs(rq->remote.sin_port), ntohs(rq->local.sin_port)); + } CATCH switch (exc_type) { + case EXC_NOMEM: + EXIT_TRY; + goto fail_1; + default: + RETHROW; + } END_TRY; + + /* --- Do the rest of the work --- */ + + if (write(fd, d.buf, d.len) < d.len) + goto fail_1; + dstr_destroy(&d); + rq->state = IDENT_READ; + selbuf_init(&rq->b, rq->s, fd, line, rq); + return; + + /* --- Tidy up after misfortunes --- */ + +fail_1: + dstr_destroy(&d); + close(fd); +fail_0: + rq->state = IDENT_DONE; + rq->func(0, rq->p); +} + +/* --- @ident_abort@ --- * + * + * Arguments: @ident_request *rq@ = pointer to request block + * + * Returns: --- + * + * Use: Cancels an ident request in progress. + */ + +void ident_abort(ident_request *rq) +{ + switch (rq->state) { + case IDENT_CONN: + conn_kill(&rq->c); + break; + case IDENT_READ: + close(rq->b.reader.fd); + selbuf_destroy(&rq->b); + break; + } +} + +/* --- @go@ --- * + * + * Arguments: @ident_request *rq@ = pointer to request block + * + * Returns: --- + * + * Use: Starts a connection to the remote ident server. + */ + +static void go(ident_request *rq) +{ + int fd; + struct sockaddr_in sin; + int opt; + + /* --- Create the socket I'll use --- */ + + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) + goto fail_0; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr = rq->local.sin_addr; + if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) + goto fail_1; + + /* --- Out-of-band data would confuse us --- */ + + opt = 1; + setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + + /* --- Start a connection to the remote server --- */ + + sin.sin_family = AF_INET; + sin.sin_port = htons(113); + sin.sin_addr = rq->remote.sin_addr; + if (conn_init(&rq->c, rq->s, fd, (struct sockaddr *)&sin, sizeof(sin), + connected, rq)) + goto fail_1; + + /* --- Finish off initializing the block --- */ + + rq->state = IDENT_CONN; + return; + + /* --- Tidy up after lossage --- */ + +fail_1: + close(fd); +fail_0: + rq->state = IDENT_DONE; + rq->func(0, rq->p); +} + +/* --- @ident@ --- * + * + * Arguments: @ident_request *rq@ = pointer to request block + * @sel_state *s@ = I/O multiplexor + * @const struct sockaddr_in *local, *remote@ = addresses + * @void (*func)(ident_reply *i, void *p)@ = handler function + * @void *p@ = argument for handler + * + * Returns: --- + * + * Use: Initializes an ident request. + */ + +void ident(ident_request *rq, sel_state *s, + const struct sockaddr_in *local, + const struct sockaddr_in *remote, + void (*func)(ident_reply */*i*/, void */*p*/), + void *p) +{ + memcpy(&rq->local, local, sizeof(rq->local)); + memcpy(&rq->remote, remote, sizeof(rq->remote)); + rq->func = func; + rq->p = p; + rq->s = s; + go(rq); +} + +/* --- @ident_socket@ --- * + * + * Arguments: @ident_request *rq@ = pointer to request block + * @sel_state *s@ = I/O multiplexor + * @int sk@ = connected socket file descriptor + * @void (*func)(ident_reply *i, void *p)@ = handler function + * @void *p@ = argument for handler + * + * Returns: --- + * + * Use: An alternative interface to @ident@. Initializes an ident + * request from a connected socket, rather than from an explicit + * address. This will call @getsockname@ and @getpeername@ to + * find out what the socket is actually connected to, which adds + * convenience but wastes time. + */ + +void ident_socket(ident_request *rq, sel_state *s, int sk, + void (*func)(ident_reply */*i*/, void */*p*/), + void *p) +{ + size_t sinsz; + if ((sinsz = sizeof(struct sockaddr_in), + getsockname(sk, (struct sockaddr *)&rq->local, &sinsz)) || + (sinsz = sizeof(struct sockaddr_in), + getpeername(sk, (struct sockaddr *)&rq->remote, &sinsz))) { + rq->state = IDENT_DONE; + func(0, p); + return; + } + + rq->func = func; + rq->p = p; + rq->s = s; + go(rq); +} + +/*----- That's all, folks -------------------------------------------------*/