/* * IMC2 - an inter-mud communications protocol * * imc-util.c: misc utility functions for IMC * * Copyright (C) 1996,1997 Oliver Jowett <oliver@jowett.manawatu.planet.co.nz> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, write to the * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> #include <errno.h> #include "imc.h" /* * I needed to split up imc.c (2600 lines and counting..) so this file now * contains: * * in general: anything which is called from more than one file and is not * obviously an interface function is a candidate for here. * * specifically: * - general string manipulation functions * - flag and state lookup functions * - error logging * - imc_reminfo creation/lookup/deletion * - imc_info lookup * - connection naming * - reconnect setup * - static buffer allocation */ char imc_lasterror[IMC_DATA_LENGTH]; /* last error reported */ /* * Static buffer allocation - greatly reduces IMC's memory footprint */ /* reserves about 64k for static buffers */ #define ssize (IMC_DATA_LENGTH * 4) static char sspace[ssize]; static int soffset; static char *lastalloc; char *imc_getsbuf(int len) { char *buf; if (soffset >= ssize-len) soffset=0; buf=&sspace[soffset]; soffset = (soffset + len) % ssize; buf[0]=0; lastalloc=buf; return buf; } void imc_shrinksbuf(char *buf) { int offset; if (!buf || buf!=lastalloc) return; offset=buf-sspace; soffset=offset+strlen(buf)+1; } /* * Key/value manipulation */ /* clone packet data */ void imc_clonedata(const imc_data *p, imc_data *n) { int i; for (i=0; i<IMC_MAX_KEYS; i++) { if (p->key[i]) n->key[i]=imc_strdup(p->key[i]); else n->key[i]=NULL; if (p->value[i]) n->value[i]=imc_strdup(p->value[i]); else n->value[i]=NULL; } } /* get the value of "key" from "p"; if it isn't present, return "def" */ const char *imc_getkey(const imc_data * p, const char *key, const char *def) { int i; for (i=0; i<IMC_MAX_KEYS; i++) if (p->key[i] && !strcasecmp(p->key[i], key)) return p->value[i]; return def; } /* identical to imc_getkey, except get the integer value of the key */ int imc_getkeyi(const imc_data *p, const char *key, int def) { int i; for (i=0; i<IMC_MAX_KEYS; i++) if (p->key[i] && !strcasecmp(p->key[i], key)) return atoi(p->value[i]); return def; } /* add "key=value" to "p" */ void imc_addkey(imc_data *p, const char *key, const char *value) { int i; for (i=0; i<IMC_MAX_KEYS; i++) if (p->key[i] && !strcasecmp(key, p->key[i])) { imc_strfree(p->key[i]); imc_strfree(p->value[i]); p->key[i] = NULL; p->value[i] = NULL; break; } if (!value) return; for (i=0; i<IMC_MAX_KEYS; i++) if (!p->key[i]) { p->key[i] = imc_strdup(key); p->value[i] = imc_strdup(value); return; } } /* add "key=value" for an integer value */ void imc_addkeyi(imc_data *p, const char *key, int value) { char temp[20]; sprintf(temp, "%d", value); imc_addkey(p, key, temp); } /* clear all keys in "p" */ void imc_initdata(imc_data *p) { int i; for (i=0; i<IMC_MAX_KEYS; i++) { p->key[i] = NULL; p->value[i] = NULL; } } /* free all the keys in "p" */ void imc_freedata(imc_data * p) { int i; for (i=0; i<IMC_MAX_KEYS; i++) { if (p->key[i]) imc_strfree(p->key[i]); if (p->value[i]) imc_strfree(p->value[i]); } } /* * Error logging */ /* log a string */ void imc_logstring(const char *format, ...) { char buf[IMC_DATA_LENGTH]; va_list ap; va_start(ap, format); vsnprintf(buf, IMC_DATA_LENGTH - 1, format, ap); va_end(ap); imc_log(buf); } /* log an error (log string and copy to lasterror) */ void imc_logerror(const char *format,...) { va_list ap; va_start(ap, format); vsnprintf(imc_lasterror, IMC_DATA_LENGTH - 1, format, ap); va_end(ap); imc_log(imc_lasterror); } /* log an error quietly (just copy to lasterror) */ void imc_qerror(const char *format,...) { va_list ap; va_start(ap, format); vsnprintf(imc_lasterror, IMC_DATA_LENGTH - 1, format, ap); va_end(ap); } /* log a system error (log string, ": ", string representing errno) */ /* this is particularly broken on SunOS (which doesn't have strerror) */ void imc_lerror(const char *format,...) { va_list ap; va_start(ap, format); vsnprintf(imc_lasterror, IMC_DATA_LENGTH - 1, format, ap); strcat(imc_lasterror, ": "); strcat(imc_lasterror, strerror(errno)); imc_log(imc_lasterror); } const char *imc_error(void) { return imc_lasterror; } /* * String manipulation functions, mostly exported */ /* lowercase what */ void imc_slower(char *what) { char *p=what; while (*p) { *p=tolower(*p); p++; } } /* copy src->dest, max count, null-terminate */ void imc_sncpy(char *dest, const char *src, int count) { strncpy(dest, src, count-1); dest[count-1] = 0; } /* return 'mud' from 'player@mud' */ const char *imc_mudof(const char *fullname) { char *buf=imc_getsbuf(IMC_MNAME_LENGTH); char *where; where=strchr(fullname, '@'); if (!where) imc_sncpy(buf, fullname, IMC_MNAME_LENGTH); else imc_sncpy(buf, where+1, IMC_MNAME_LENGTH); imc_shrinksbuf(buf); return buf; } /* return 'player' from 'player@mud' */ const char *imc_nameof(const char *fullname) { char *buf=imc_getsbuf(IMC_PNAME_LENGTH); char *where=buf; int count=0; while (*fullname && *fullname != '@' && count < IMC_PNAME_LENGTH-1) *where++=*fullname++, count++; *where = 0; imc_shrinksbuf(buf); return buf; } /* return 'player@mud' from 'player' and 'mud' */ const char *imc_makename(const char *player, const char *mud) { char *buf=imc_getsbuf(IMC_NAME_LENGTH); imc_sncpy(buf, player, IMC_PNAME_LENGTH); strcat(buf, "@"); imc_sncpy(buf + strlen(buf), mud, IMC_MNAME_LENGTH); imc_shrinksbuf(buf); return buf; } /* return 'e' from 'a!b!c!d!e' */ const char *imc_lastinpath(const char *path) { const char *where; char *buf=imc_getsbuf(IMC_NAME_LENGTH); where=path + strlen(path)-1; while (*where != '!' && where >= path) where--; imc_sncpy(buf, where+1, IMC_NAME_LENGTH); imc_shrinksbuf(buf); return buf; } /* return 'a' from 'a!b!c!d!e' */ const char *imc_firstinpath(const char *path) { char *buf=imc_getsbuf(IMC_NAME_LENGTH); char *p; for (p=buf; *path && *path != '!'; *p++=*path++) ; *p=0; imc_shrinksbuf(buf); return buf; } /* imc_getarg: extract a single argument (with given max length) from * argument to arg; if arg==NULL, just skip an arg, don't copy it out */ const char *imc_getarg(const char *argument, char *arg, int length) { int len = 0; while (*argument && isspace(*argument)) argument++; if (arg) while (*argument && !isspace(*argument) && len < length-1) *arg++=*argument++, len++; else while (*argument && !isspace(*argument)) argument++; while (*argument && !isspace(*argument)) argument++; while (*argument && isspace(*argument)) argument++; if (arg) *arg = 0; return argument; } /* Check for a name in a list */ int imc_hasname(const char *list, const char *name) { const char *p; char arg[IMC_NAME_LENGTH]; if(!list) return(0); p=imc_getarg(list, arg, IMC_NAME_LENGTH); while (arg[0]) { if (!strcasecmp(name, arg)) return 1; p=imc_getarg(p, arg, IMC_NAME_LENGTH); } return 0; } /* Add a name to a list */ void imc_addname(char **list, const char *name) { char buf[IMC_DATA_LENGTH]; if (imc_hasname(*list, name)) return; if ((*list)[0]) sprintf(buf, "%s %s", *list, name); else strcpy(buf, name); imc_strfree(*list); *list=imc_strdup(buf); } /* Remove a name from a list */ void imc_removename(char **list, const char *name) { char buf[1000]; char arg[IMC_NAME_LENGTH]; const char *p; buf[0]=0; p=imc_getarg(*list, arg, IMC_NAME_LENGTH); while (arg[0]) { if (strcasecmp(arg, name)) { if (buf[0]) strcat(buf, " "); strcat(buf, arg); } p=imc_getarg(p, arg, IMC_NAME_LENGTH); } imc_strfree(*list); *list=imc_strdup(buf); } /* * Flag interpretation */ /* look up a value in a table */ const char *imc_statename(int value, const imc_flag_type *table) { int i; for (i=0; table[i].name; i++) if (value==table[i].value) return table[i].name; return "unknown"; } /* return the name of a particular set of flags */ const char *imc_flagname(int value, const imc_flag_type *table) { char *buf=imc_getsbuf(100); int i; buf[0]=0; for (i=0; table[i].name; i++) if ((value & table[i].value) == table[i].value) { strcat(buf, table[i].name); strcat(buf, " "); value &= ~table[i].value; } if (buf[0]) buf[strlen(buf)-1] = 0; else strcpy(buf, "none"); imc_shrinksbuf(buf); return buf; } /* return the value corresponding to a set of names */ int imc_flagvalue(const char *name, const imc_flag_type *table) { char buf[20]; int i; int value = 0; while (1) { name=imc_getarg(name, buf, 20); if (!buf[0]) return value; for (i=0; table[i].name; i++) if (!strcasecmp(table[i].name, buf)) value |= table[i].value; } } /* return the value corresponding to a name */ int imc_statevalue(const char *name, const imc_flag_type *table) { int i; char buf[20]; imc_getarg(name, buf, 20); for (i=0; table[i].name; i++) if (!strcasecmp(table[i].name, buf)) return table[i].value; return -1; } /* * imc_reminfo handling */ /* find an info entry for "name" */ imc_reminfo *imc_find_reminfo(const char *name, int type) { imc_reminfo *p; for (p = imc_reminfo_list; p; p = p->next) { // if (p->type == IMC_REMINFO_EXPIRED && !type) // continue; if (!strcasecmp(name, p->name)) return p; } return NULL; } /* create a new info entry, insert into list */ imc_reminfo *imc_new_reminfo(void) { imc_reminfo *p; p=imc_malloc(sizeof(imc_reminfo)); p->name = NULL; p->version = NULL; p->route = NULL; p->path = NULL; p->alive = 0; p->ping = 0; p->top_sequence = 0; p->next = imc_reminfo_list; p->type = IMC_REMINFO_NORMAL; imc_reminfo_list=p; return p; } /* delete the info entry "p" */ void imc_delete_reminfo(imc_reminfo *p) { imc_reminfo *last; if (!imc_reminfo_list || !p) return; if (p == imc_reminfo_list) imc_reminfo_list = p->next; else { for (last=imc_reminfo_list; last && last->next != p; last=last->next) ; if (!last) return; last->next=p->next; } imc_strfree(p->name); imc_strfree(p->version); imc_strfree(p->route); imc_cancel_event(NULL, p); imc_free(p, sizeof(*p)); } /* get info struct for given mud */ imc_info *imc_getinfo(const char *mud) { imc_info *p; for (p=imc_info_list; p; p=p->next) if (!strcasecmp(mud, p->name)) return p; return NULL; } /* get name of a connection */ const char *imc_getconnectname(const imc_connect *c) { char *buf=imc_getsbuf(IMC_NAME_LENGTH); const char *n; if (c->info) n=c->info->name; else n="unknown"; sprintf(buf, "%s[%d]", n, c->desc); imc_shrinksbuf(buf); return buf; } /* set up for a reconnect */ void imc_setup_reconnect(imc_info *i) { time_t temp; int t; /* add a bit of randomness so we don't get too many simultaneous * reconnects */ temp=i->timer_duration + (rand()%21) - 20; t=imc_next_event(ev_reconnect, i); if (t >= 0 && t < temp) return; /* routers should never do reconnects to muds - shogar */ /* and limit RECONNECTs to 3 attempts until successful connect */ /* or they connect to us until we reboot, then we give them a second */ /* chance */ if(!imc_is_router || (i->flags & IMC_HUB)) { if(i->connect_attempts < 3) { i->timer_duration*=2; if (i->timer_duration > IMC_MAX_RECONNECT_TIME) i->timer_duration = IMC_MAX_RECONNECT_TIME; imc_add_event(temp, ev_reconnect, i, 1); i->connect_attempts += 1; /* count the attempts - shogar */ } } } const char *imc_make_password(void) { /* Generate random passwords for the auto-reconnect feature -- Scion */ time_t temp; int a=0; char letters[] = "abcdefghijklmnopqrstuvwxyz"; /* You can change this if you want */ static char buf[9]; char *pass=NULL; for (a=0; a<9; a++) { temp=(rand()%(strlen(letters)-1)+1); if (temp<0 || temp>strlen(letters)-1) temp=0; /* We'll make 8 character passwords */ buf[a]=letters[temp]; } buf[8]='\0'; pass=buf; return pass; }