/* * ident_client.c * Ident client for rfc1413 lookups * --------------------------------------------------------------------------- * * The majority of the code contained herein is Copyright 1995/1996 by * Neil Peter Charley. * Portions of the code (notably that for Solaris 2.x and BSD compatibility) * are Copyright 1996 James William Hawtin. Thanks also goes to James * for pointing out ways to make the code more robust. * ALL code contained herein is covered by one or the other of the above * Copyright's. * * Distributed as part of Playground+ package with permission. */ char identclientc_rcsid[] = "$Revision: 1.5 $"; #if defined(__STRICT_ANSI__) #include <features.h> #define __USE_POSIX #define __USE_BSD #define __USE_MISC #endif /* __STRICT_ANSI__ */ #include <stdlib.h> #include <stdio.h> #if !defined(LOTHLORIEN) #include <stdarg.h> #endif #include <ctype.h> #include <errno.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <signal.h> #include <sys/ioctl.h> #include <string.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #if defined(HAVE_FILIOH) #include <sys/filio.h> #endif /* HAVE_FILIOH */ #if !defined(OSF) #include <sys/wait.h> #endif /* !OSF */ #if defined(NETBSD) || defined(BSDISH) #include <sys/socket.h> #endif #include "include/config.h" #include "include/player.h" #include "include/fix.h" #include "include/proto.h" #if defined(BSD3) || defined(BSDISH) #include <sys/un.h> #else #ifdef LINUX #include "include/un.h" #else #ifdef NETBSD #include <sys/un.h> #else #include <un.h> #endif #endif #endif #ifdef IDENT #include "include/ident.h" #if defined(ANSI) extern int kill __P ((__pid_t __pid, int __sig)); extern void bzero __P ((__ptr_t __s, size_t __n)); #endif /* This is an UGLY hack, 'cos the gcc setup on rabbit precludes the * #include'ing of both <sys/wait.h> and <sys/time.h> * The lines below (inside the #if defined) are from <sys/wait.h> on rabbit */ #if defined(OSF) /* WNOHANG causes the wait to not hang if there are no stopped or terminated */ #define WNOHANG 0x1 /* dont hang in wait */ #endif /* OSF */ /* Extern functions */ extern void log(char *, char *); /* Extern variables */ #ifndef errno extern int errno; #endif #if !defined(LOTHLORIEN) extern player *flatlist_start; #endif /* !LOTHLORIEN */ /* Local functions */ void setup_itimer(void); void ident_process_reply(int msg_size); /* Local Variables */ #define BUFFER_SIZE 2048 /* Significantly bigger than the * equivalent in ident_server.c */ int ident_toclient_fds[2]; int ident_toserver_fds[2]; int ident_server_pid = 0; ident_identifier ident_id = 0; char ident_buf_input[BUFFER_SIZE]; char ident_buf_output[BUFFER_SIZE]; char reply_buf[BUFFER_SIZE]; /* * Start up the ident server */ int init_ident_server(void) { int ret; #if defined(FIONBIO) int dummy; #endif /* FIONBIO */ fd_set fds; struct timeval timeout; char namebuffer[256]; sprintf(namebuffer, "-=> %s <=- Ident server", get_config_msg("talker_name")); if (-1 == pipe(ident_toclient_fds)) { switch (errno) { case EMFILE: log("boot", "init_ident_server: Too many fd's in use by" " process"); exit(1); case ENFILE: log("boot", "init_ident_server: Too many fd's in use in" " system"); exit(1); case EFAULT: log("boot", "init_ident_server: ident_toclient_fds invalid!"); exit(1); } } if (-1 == pipe(ident_toserver_fds)) { switch (errno) { case EMFILE: log("boot", "init_ident_server: Too many fd's in use by" " process"); exit(1); case ENFILE: log("boot", "init_ident_server: Too many fd's in use in" " system"); exit(1); case EFAULT: log("boot", "init_ident_server: ident_toserver_fds invalid!"); exit(1); } } ret = fork(); switch (ret) { case -1: /* Error */ log("boot", "init_ident_server couldn't fork!"); exit(1); case 0: /* Child */ close(IDENT_CLIENT_READ); close(IDENT_CLIENT_WRITE); close(0); dup(IDENT_SERVER_READ); close(IDENT_SERVER_READ); close(1); dup(IDENT_SERVER_WRITE); close(IDENT_SERVER_WRITE); execlp("bin/ident_server", namebuffer, 0); log("boot", "init_ident_server failed to exec ident_server"); exit(1); default: /* Parent */ ident_server_pid = ret; close(IDENT_SERVER_READ); close(IDENT_SERVER_WRITE); IDENT_SERVER_READ = IDENT_SERVER_WRITE = -1; #if defined(FIONBIO) if (ioctl(IDENT_CLIENT_READ, FIONBIO, &dummy) < 0) { log("error", "Ack! Can't set non-blocking to ident read."); } if (ioctl(IDENT_CLIENT_WRITE, FIONBIO, &dummy) < 0) { log("error", "Ack! Can't set non-blocking to ident write."); } #endif /* FIONBIO */ } FD_ZERO(&fds); FD_SET(IDENT_CLIENT_READ, &fds); timeout.tv_sec = 15; timeout.tv_usec = 0; while (-1 == (ret = select(FD_SETSIZE, &fds, 0, 0, &timeout))) { if (errno == EINTR || errno == EAGAIN) { continue; } log("boot", "init_ident_server: Timed out waiting for server" " connect"); kill_ident_server(); return 0; } ioctl(IDENT_CLIENT_READ, FIONREAD, &ret); while (ret != strlen(IDENT_SERVER_CONNECT_MSG)) { sleep(1); ioctl(IDENT_CLIENT_READ, FIONREAD, &ret); } ret = read(IDENT_CLIENT_READ, ident_buf_input, ret); ident_buf_input[ret] = '\0'; if (strcmp(ident_buf_input, IDENT_SERVER_CONNECT_MSG)) { fprintf(stderr, "From Ident: '%s'\n", ident_buf_input); log("boot", "init_ident_server: Bad connect from server, killing"); kill_ident_server(); return 0; } log("boot", "Ident Server Up and Running"); setup_itimer(); return 1; } /* Shutdown the ident server */ void kill_ident_server(void) { int status; close(IDENT_CLIENT_READ); close(IDENT_CLIENT_WRITE); IDENT_CLIENT_READ = -1; IDENT_CLIENT_WRITE = -1; kill(ident_server_pid, SIGTERM); waitpid(-1, &status, WNOHANG); } void send_ident_request(player *p, struct sockaddr_in *sadd) { char *s; int bwritten; struct sockaddr_in sname; #if defined(__GLIBC__) || (__GLIBC__ >= 2) socklen_t l; #else int l; #endif l = sizeof(struct sockaddr_in); getsockname(p->fd, (struct sockaddr *)&sname, &l); #if defined(DEBUG_IDENT_TOO) fprintf(stderr, "send_ident_request: sockname = '%s'\n", inet_ntoa(sname.sin_addr)); #endif /* DEBUG_IDENT_TOO */ s = ident_buf_output; *s++ = IDENT_CLIENT_SEND_REQUEST; memcpy(s, &ident_id, sizeof(ident_id)); s += sizeof(ident_id); memcpy(s, &(sname.sin_addr.s_addr), sizeof(sname.sin_addr.s_addr)); s += sizeof(sname.sin_addr.s_addr); memcpy(s, &(sname.sin_port), sizeof(sname.sin_port)); s += sizeof(sname.sin_port); memcpy(s, &(sadd->sin_family), sizeof(sadd->sin_family)); s += sizeof(sadd->sin_family); memcpy(s, &(sadd->sin_addr.s_addr), sizeof(sadd->sin_addr.s_addr)); s += sizeof(sadd->sin_addr.s_addr); memcpy(s, &(sadd->sin_port), sizeof(sadd->sin_port)); s += sizeof(sadd->sin_port); bwritten = write(IDENT_CLIENT_WRITE, ident_buf_output, (s - ident_buf_output)); p->ident_id = ident_id++; if (bwritten < (s - ident_buf_output)) { log("ident", "Client failed to write request, killing and restarting" " Server"); kill_ident_server(); /* FIXME: Maybe test for presence of PID file? */ sleep(3); init_ident_server(); bwritten = write(IDENT_CLIENT_WRITE, ident_buf_output, (s - ident_buf_output)); if (bwritten < (s - ident_buf_output)) { log("ident", "Restart failed"); } } else { #if defined(DEBUG_IDENT) stdarg_log("ident_ids", "Player '%s', fd %d, ident_id %d", p->name[0] ? p->name : "<NOT-ENTERED>", p->fd, p->ident_id); #endif /* DEBUG_IDENT */ } #if defined(DEBUG_IDENT) fprintf(stderr, "Bytes Written %d, Should have sent %d\n", bwritten, (s - ident_buf_output)); fprintf(stderr, "Client: %08X:%d\n", (int) ntohl(sadd->sin_addr.s_addr), ntohs(sadd->sin_port)); fflush(stderr); #endif } void read_ident_reply(void) { int bread; int toread; int i; static int bufpos = 0; ioctl(IDENT_CLIENT_READ, FIONREAD, &toread); if (toread <= 0) { return; } bread = read(IDENT_CLIENT_READ, ident_buf_input, BUFFER_SIZE - 20); ident_buf_input[bread] = '\0'; for (i = 0 ; i < bread ; ) { reply_buf[bufpos++] = ident_buf_input[i++]; if ((bufpos > (sizeof(char) + sizeof(ident_identifier))) && (reply_buf[bufpos - 1] == '\n')) { ident_process_reply(bufpos); bufpos = 0; #if defined(HAVE_BZERO) bzero(reply_buf, BUFFER_SIZE); #else memset(reply_buf, 0, BUFFER_SIZE); #endif /* HAVE_BZERO */ } } } void ident_process_reply(int msg_size) { char *s, *t; char reply[MAX_REMOTE_USER + 1]; int i; ident_identifier id; #if defined(LOTHLORIEN) connection *c; #define FORC(c) for (c = firstc ; c ; c = c->next) #else /* !LOTHLORIEN */ player *c; #define FORC(c) for (c = flatlist_start ; c ; c = c->flat_next) #endif /* LOTHLORIEN */ for (i = 0 ; i < msg_size ;) { switch (reply_buf[i++]) { case IDENT_SERVER_SEND_REPLY: memcpy(&id, &reply_buf[i], sizeof(ident_identifier)); i += sizeof(ident_identifier); #if defined(DEBUG_IDENT) stdarg_log("ident_ids", "Got reply for ident_id %d", id); #endif /* DEBUG_IDENT */ FORC(c) { if (c->ident_id == id) { #if defined(DEBUG_IDENT) stdarg_log("ident_ids", "Matched ident_id %d to Player '%s'," " fd %d", id, c->name[0] ? c->name : "<NOT-ENTERED>", c->fd); #endif /* DEBUG_IDENT */ break; } } #if defined(DEBUG_IDENT) fprintf(stderr, "Ident Client: Got reply '%s'\n", &reply_buf[i]); #endif /* DEBUG_IDENT */ s = strchr(&reply_buf[i], '\n'); if (s) { *s++ = '\0'; } else { s = strchr(reply_buf, '\0'); *s++ = '\n'; *s = '\0'; } if (c) { /* *sigh*, some git might set up an identd that sends crap * stuff back, so we need to filter the reply * RFC1413 does state that the 'result' should consist * of printable characters: "Returned user identifiers are expected to be printable in the character set indicated." * Default character set is US-ASCII, and we're not catering * for anything else, at least not yet. */ #if defined(LOTHLORIEN) FREE(c->remote_user); c->remote_user = CALLOC(1, strlen(&reply_buf[i]) + 1); s = &reply_buf[i]; t = c->remote_user; while (*s) #else s = &reply_buf[i]; t = reply; while (*s && t < &reply[MAX_REMOTE_USER]) #endif /* LOTHLORIEN */ { if (isprint(*s)) { *t++ = *s++; } else { s++; } } *t = '\0'; #if !defined(LOTHLORIEN) strncpy(c->remote_user, reply, MAX_REMOTE_USER); #endif /* !LOTHLORIEN */ #if defined(DEBUG_IDENT) stdarg_log("ident_ids", "Write ident_id %d, Reply '%s' to player '%s'" " fd %d", id, c->remote_user, c->name[0] ? c->name : "<NOT-ENTERED>", c->fd); #endif /* DEBUG_IDENT */ } else { /* Can only assume connection dropped from here and we still * somehow got a reply, throw it away */ #if defined(DEBUG_IDENT) stdarg_log("ident_ids", "Threw away response for ident_id %d", id); #endif /* DEBUG_IDENT */ } while (reply_buf[i] != '\n') { i++; } break; default: #if defined(DEBUG_IDENT_TOO) stdarg_log("ident", "Bad reply from server '%d'", reply_buf[i]); #endif /* DEBUG_IDENT_TOO */ i++; } } } /* log using stdarg variable length arguments */ void stdarg_log(char *str, ...) { va_list args; FILE *fp; char *fmt; struct tm *tim; char the_time[256]; char fname[21]; time_t current_time; va_start(args, str); current_time = time(0); tim = localtime(¤t_time); strftime((char *)&the_time, 255, "%H:%M:%S %Z - %d/%m/%Y - ", tim); sprintf(fname, "logs/%s.log", str); fp = fopen(fname, "a"); if (!fp) { fprintf(stderr, "Eek! Can't open file to log: %s\n", fname); return; } fprintf(fp, "%s", the_time); fmt = va_arg(args, char *); vfprintf(fp, fmt, args); va_end(args); fprintf(fp, "\n"); fclose(fp); } void ident_version(void) { sprintf(stack, " -=*> Ident server v1.10 (by Athanasius and Oolon) enabled.\n"); stack = strchr(stack, 0); } #endif