From 699fe68c44266932060013b5a323b64e69ee1bed Mon Sep 17 00:00:00 2001 From: "psyc://psyced.org/~lynX" <@> Date: Tue, 27 Jan 2009 23:02:44 +0100 Subject: [PATCH] some missing files --- config/blueprint/README | 42 + config/blueprint/banner.txt | 8 + config/blueprint/config.c | 66 + config/blueprint/config.h | 23 + config/blueprint/hosts.h | 7 + config/blueprint/init.ls | 3 + config/blueprint/local.h | 5 + config/blueprint/path.h | 50 + config/blueprint/ports.h | 33 + config/gentoo/ldmud/files/erq/Makefile.in | 68 + config/gentoo/ldmud/files/erq/erq.c | 2612 +++++++++++++++++++++ config/gentoo/ldmud/files/erq/erq.h | 60 + config/gentoo/ldmud/files/erq/srv.c | 215 ++ config/gentoo/ldmud/files/erq/srv.h | 32 + config/gentoo/ldmud/files/psyced.settings | 311 +++ pike/net.pmod/psyc.pmod/common.pike | 3 + utility/latency.pike | 35 + world/drivers/ldmud/master/accept.c | 440 ++++ world/net/include/trust.h | 32 + world/net/spyc/dispatch.i | 239 ++ 20 files changed, 4284 insertions(+) create mode 100644 config/blueprint/README create mode 100644 config/blueprint/banner.txt create mode 100644 config/blueprint/config.c create mode 100644 config/blueprint/config.h create mode 100644 config/blueprint/hosts.h create mode 100644 config/blueprint/init.ls create mode 100644 config/blueprint/local.h create mode 100644 config/blueprint/path.h create mode 100644 config/blueprint/ports.h create mode 100644 config/gentoo/ldmud/files/erq/Makefile.in create mode 100644 config/gentoo/ldmud/files/erq/erq.c create mode 100644 config/gentoo/ldmud/files/erq/erq.h create mode 100644 config/gentoo/ldmud/files/erq/srv.c create mode 100644 config/gentoo/ldmud/files/erq/srv.h create mode 100644 config/gentoo/ldmud/files/psyced.settings create mode 100644 pike/net.pmod/psyc.pmod/common.pike create mode 100644 utility/latency.pike create mode 100644 world/drivers/ldmud/master/accept.c create mode 100644 world/net/include/trust.h create mode 100644 world/net/spyc/dispatch.i diff --git a/config/blueprint/README b/config/blueprint/README new file mode 100644 index 0000000..1f42edf --- /dev/null +++ b/config/blueprint/README @@ -0,0 +1,42 @@ +========================================== +YOU SHOULD READ THIS MESSAGE IN YOUR local/ +DIRECTORY. config/blueprint ONLY CONTAINS +THE DEFAULT COPY OF THE CONFIGURATION. IT +IS NOT IN USE AS YOU RUN THE SERVER. SO +DON'T EDIT ANY FILES IN config/blueprint. +THX ====================================== + +banner.txt is the file that is shown to +telnet users at login + +config.c is currently not being used, really. + +config.h is of no interest. + +hosts.h lets you specify ip ranges that are +particularely trustworthy or untrustworthy. + +init.ls contains the list of classes that +will be compiled at boot-time. it is mostly +useful for debugging, as in a healthy +system any required class can be compiled +at the moment it is used for the first time. +so for production use this is kept to the +bare minimum. only the parts of psyced you +are actually using will be loaded. + +local.h hosts your personal tunings to the +psyced system. + +path.h lets you rearrange the directories +where the code for the protocol implementations +reside. you don't need to do that normally. + +ports.h is generated by the installation +procedure and contains the port numbers you +chose for the services you want to use. + +psyconf.h is also generated that way by the +psyconf utility. it contains the settings +from psyced.ini in a form pleasant for LPC. + diff --git a/config/blueprint/banner.txt b/config/blueprint/banner.txt new file mode 100644 index 0000000..62c7cb1 --- /dev/null +++ b/config/blueprint/banner.txt @@ -0,0 +1,8 @@ +[connected] + + PROTOCOL for SYNCHRONOUS CONFERENCING http://www.psyced.org/ + --------------- ___ __ _ _ __ ___ __ ------------------ + | \ (__ \ / / | | \ + |__/ \ V | |- | ) + >>> | (__/ | \__ |__ |_/ <<< + diff --git a/config/blueprint/config.c b/config/blueprint/config.c new file mode 100644 index 0000000..57e62a0 --- /dev/null +++ b/config/blueprint/config.c @@ -0,0 +1,66 @@ +// $Id: config.c,v 1.9 2006/11/07 07:58:36 lynx Exp $ vim:syntax=lpc +#include + +/* a data file or an include can be read by any file in the system but + * we want this information to be readable by the gateway code only + * that's why it has to be lpc code + */ + +#ifdef GATEWAY_PATH + +/* if you are positive that you want to run your own gateways to + * legacy messaging systems, please insert your gateway credentials + * into the fields below and activate the code by turning #if 0 to #if 1 + * + * update: these bot-style gateways are not functional. don't switch + * them to 1 as either the python scripts or the jabber code isn't + * up to date with them. + */ + +# define USE_ICQ_GATEWAY 0 // don't change +# define USE_AIM_GATEWAY 0 // don't change + +qConfig() { + string p = file_name(previous_object()); +# ifdef __COMPAT_MODE__ + p = "/"+p; +# endif + P3(("\n%O: config requested by %s\n", ME, p)) +# if USE_ICQ_GATEWAY + if (abbrev(GATEWAY_PATH "icq", p)) return + (["host" : "icq.localhost", + "port" : 5234, + "scheme" : "icq", + "name" : "icqlinker", + "secret" : "myicqsecret", + "nickname" : "your uin here", + "password" : "and your password please" ]); +# endif +# if USE_AIM_GATEWAY + if (abbrev(GATEWAY_PATH "aim2", p)) return + (["host" : "aim.localhost", + "port" : 5233, + "scheme" : "aim", + "name" : "aimlinker", + "secret" : "myaimsecret", + "nickname" : "screen name", + "password" : "and your password please" ]); +# endif +} + +load() { +# if USE_ICQ_GATEWAY + D(" " GATEWAY_PATH "icq"); + load_object(GATEWAY_PATH "icq"); +# endif +# if USE_AIM_GATEWAY + D(" " GATEWAY_PATH "aim2"); + load_object(GATEWAY_PATH "aim2"); +# endif +# ifdef RELAY_OBJECT + D(" " RELAY_OBJECT "\n"); + call_out(load_object, 0, RELAY_OBJECT); +# endif +} + +#endif diff --git a/config/blueprint/config.h b/config/blueprint/config.h new file mode 100644 index 0000000..0cd4cb5 --- /dev/null +++ b/config/blueprint/config.h @@ -0,0 +1,23 @@ +// $Id: config.h,v 1.1 2007/04/26 13:34:02 lynx Exp $ // vim:syntax=lpc +// +// This file is not intended to be modified. Make your local changes +// in psyced.ini, and if that wasn't good enough, use local.h. +// If you are integrating psyced with other LPC applications, feel +// free to modify path.h. If you aren't, better leave it as it is. + +#ifndef CONFIG_H +# define CONFIG_H + +// load local modifications by the admin +# include "local.h" + +// load port configuration as generated by psyconf +# include "ports.h" + +// load layout of psyced software modules +# include "path.h" + +// load psyced.ini settings converted by psyconf +# include "psyconf.h" + +#endif diff --git a/config/blueprint/hosts.h b/config/blueprint/hosts.h new file mode 100644 index 0000000..d137eca --- /dev/null +++ b/config/blueprint/hosts.h @@ -0,0 +1,7 @@ +// Examples of specially enabled or disabled hosts. +// +// +// #define ENABLED_HOSTS "192.168.0.", "10." +// +// #define DISABLED_HOSTS "207.46.197.", "207.46.19." +// diff --git a/config/blueprint/init.ls b/config/blueprint/init.ls new file mode 100644 index 0000000..73ffa94 --- /dev/null +++ b/config/blueprint/init.ls @@ -0,0 +1,3 @@ +net/user +net/place/basic +local/config diff --git a/config/blueprint/local.h b/config/blueprint/local.h new file mode 100644 index 0000000..296dac4 --- /dev/null +++ b/config/blueprint/local.h @@ -0,0 +1,5 @@ +// This is local.h for advanced tunings to the psyced runtime and +// LPC compilation process. The format looks just like a regular +// header file for the C language. Settings you can specify +// here are documented at http://about.psyc.eu/Tuning + diff --git a/config/blueprint/path.h b/config/blueprint/path.h new file mode 100644 index 0000000..bf4a72b --- /dev/null +++ b/config/blueprint/path.h @@ -0,0 +1,50 @@ +#ifndef _INCLUDE_PATH_H +#define _INCLUDE_PATH_H + +#define NET_PATH "/net/" +#define PLACE_PATH "/place/" +#define SERVICE_PATH "/service/" +#define DATA_PATH "/data/" +#define CONFIG_PATH "/local/" +#define DAEMON_PATH NET_PATH "d/" +#define GATEWAY_PATH NET_PATH "gateway/" + +// protocol for synchronous conferencing +#define PSYC_PATH "/net/psyc/" + +// experimental PSYC 1.0 interface +#define SPYC_PATH "/net/spyc/" + +// irc server emulation +#define IRC_PATH "/net/irc/" + +// jabber server emulation +#define JABBER_PATH "/net/jabber/" + +// telnet access +#define TELNET_PATH "/net/tn/" + +// java applet server, uses a very simple protocol +#define APPLET_PATH "/net/applet/" + +// accept messages and simple mails via smtp +#define SMTP_PATH "/net/smtp/" + +// experimental: access message log via pop3 +#define POP3_PATH "/net/pop/" + +// experimental: serve as a sip "proxy" +#define SIP_PATH "/net/sip/" + +// allow access to subscribed threaded discussion groups via nntp +#define NNTP_PATH "/net/nntp/" + +// accept messages and allow lastlog access via wap +#define WAP_PATH "/net/wap/" + +// simple http server +#ifndef HTTP_PATH +# define HTTP_PATH "/net/http/" +#endif + +#endif diff --git a/config/blueprint/ports.h b/config/blueprint/ports.h new file mode 100644 index 0000000..a17620f --- /dev/null +++ b/config/blueprint/ports.h @@ -0,0 +1,33 @@ +/* this file shall automatically be generated by psyconf. + * + * the values in here may be the same as in services.h + * or they may not. so always be conscious which ones you + * are using to which purpose. -lynX + */ + +#echo This is just the demo ports.h. I shouldn't get loaded. +#echo If I do, then you didn't run psyconf successfully!! + +#define PSYC_PORT 4404 + +#define HTTP_PORT 33333 +#define HTTPS_PORT 34443 + +#define IRC_PORT 6667 +#define IRCS_PORT 9999 + +#define JABBER_PORT 5222 +#define JABBERS_PORT 5223 +#define JABBER_S2S_PORT 5269 + +#define TELNET_PORT 2323 +#define TELNETS_PORT + +#define APPLET_PORT 2008 + +#define SMTP_PORT +#define SMTPS_PORT + +#define NTTP_PORT +#define NNTPS_PORT + diff --git a/config/gentoo/ldmud/files/erq/Makefile.in b/config/gentoo/ldmud/files/erq/Makefile.in new file mode 100644 index 0000000..c4260b7 --- /dev/null +++ b/config/gentoo/ldmud/files/erq/Makefile.in @@ -0,0 +1,68 @@ + +# These lines are needed on some machines. +MAKE=make +SHELL=@CONFIG_SHELL@ +INSTALL=@INSTALL@ +mkinstalldirs=$(SHELL) @top_srcdir@/mkinstalldirs +# +CC=@CC@ + +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +SUBDIRS = indent make_docs xerq +SED = sed + +BINDIR=@bindir@ +MUD_LIB=@libdir@ +ERQ_DIR=@libexecdir@ + +#PROFIL= -DOPCPROF -DVERBOSE_OPCPROF +#PROFIL=-p -DMARK +#PROFIL=-pg +PROFIL= +#Enable warnings from the compiler, if wanted. +WARN= # no warning options - will work with all compilers :-) +#WARN= -Wall -Wshadow -Dlint +#WARN= -Wall -Wshadow -Wno-parentheses # gcc settings +# +# Optimization and source level debugging options. +# adding a -fomit-frame-pointer on the NeXT (gcc version 1.93 (68k, MIT syntax)) +# will corrupt the driver. +HIGH_OPTIMIZE = @OCFLAGS@ # high optimization +MED_OPTIMIZE= @MCFLAGS@ # medium optimization +LOW_OPTIMIZE = @LCFLAGS@ # minimal optimization +NO_OPTIMIZE= @DCFLAGS@ # no optimization; for frequent recompilations. + +OPTIMIZE= $(@val_optimize@_OPTIMIZE) + +# The main debugging level is define in config.h +# Add additional options here. +DEBUG= +# +MPATH=-DMUD_LIB='"$(MUD_LIB)"' -DBINDIR='"$(BINDIR)"' -DERQ_DIR='"$(ERQ_DIR)"' +# +TOPINC=-I@top_srcdir@ +# +CFLAGS= @EXTRA_CFLAGS@ $(OPTIMIZE) $(DEBUG) $(WARN) $(MPATH) $(PROFIL) $(TOPINC) +# +LIBS=@ERQ_LIBS@ -lresolv +# +LDFLAGS=@LDFLAGS@ + + + +all: erq@EXEEXT@ + +FORCE: install + +erq@EXEEXT@: erq.c +# $(CC) erq.c -lresolv -o erq@EXEEXT@ + $(CC) $(CFLAGS) $(LDFLAGS) erq.c -o erq@EXEEXT@ $(LIBS) + +install: erq@EXEEXT@ + $(mkinstalldirs) $(BINDIR) + $(INSTALL) erq@EXEEXT@ $(BINDIR)/erq@EXEEXT@ + +clean: + -rm -f *.o erq@EXEEXT@ *~ diff --git a/config/gentoo/ldmud/files/erq/erq.c b/config/gentoo/ldmud/files/erq/erq.c new file mode 100644 index 0000000..00c57c1 --- /dev/null +++ b/config/gentoo/ldmud/files/erq/erq.c @@ -0,0 +1,2612 @@ +/*--------------------------------------------------------------------------- + * External Request Daemon + * + *--------------------------------------------------------------------------- + * This is the standard implementation of the forked ERQ daemon. The + * implementation is straightforward and follows what is documented in + * doc/util/erq. + * + * The erq can handle up to MAX_CHILDS (yes, it should be "children") + * concurrent tasks. Simple non-blocking tasks (all the TCP/UDP functions) + * are handled by the master ERQ, the other tasks are loaded off to + * subservers - forked copies of the ERQ. If the driver requests to + * start a program, one of the subservers is selected and forks again + * to run the program; the communication chain is then driver <-> ERQ + * <-> subserver <-> program. If a subserver is finished with its + * task, it is allowed to stay around for a certain idle time CHILD_EXPIRE, + * reducing the number of costly fork() operations when several requests + * follow after each other. + * + * This ERQ implementation uses two type of tickets. + * For programs: + * { int32 child_number } + * For sockets: + * { int32 child_number; int32 rnd; int32 seq; } + * + * TODO: This program could be commented MUCH better, but in comparison + * TODO:: to its original state it's already a big improvement. + * TODO:: And it surely still contains bugs. + *--------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "machine.h" + +#ifdef HAVE_SYS_TIME_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#else +# include +#endif +#ifdef HAVE_BSTRING_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_SYS_TERMIOS_H +# include +#endif +#ifdef _AIX +# include +#endif + +#ifdef WEXITSTATUS +# define wait_status_t int +#else +# define wait_status_t union wait +# define WEXITSTATUS(status) ((status).w_retcode) +# define WTERMSIG(status) ((status).w_termsig) +#endif + +#ifdef sun +time_t time(time_t *); +#endif + +#ifndef SIGCLD +# define SIGCLD SIGCHLD +#endif +#ifndef SIGKILL +# define SIGKILL 9 +#endif +#ifndef SIGTERM +# define SIGTERM 15 +#endif + +#ifdef _AIX +typedef unsigned long length_t; /* *sigh* */ +#else +typedef int length_t; +#endif + +#define AUTH_PORT 113 /* according to RFC 931 */ + +#include "config.h" + +#ifdef USE_IPV6 +# define __IPV6__ +#endif + +#include "erq.h" +#include "srv.c" + +#define randomize_tickets(n) srandom(n) +#define get_ticket() random() + +#ifndef ERQ_DEBUG +# define ERQ_DEBUG 0 +#endif + +/*-------------------------------------------------------------------------*/ + +/* #define DETACH */ + /* Define this for automatic detach from console. + */ + +#define MAX_CHILDS 36 + /* Maximum number of children. + */ + +#define TIME_TO_CHECK_CHILDS 900 + /* Delaytime for select() if no TCP children have data to send to + * the driver. + */ + +#define CHILD_EXPIRE 900 + /* Maximum idle time before a child is killed. + */ + +#define ERQ_BUFSIZE 1024 + /* Size of the data buffer. + */ + +#define MAX_REPLY ERQ_MAX_REPLY + + +#if MAX_REPLY >= ERQ_BUFSIZE +# undef MAX_REPLY +# define MAX_REPLY (ERQ_BUFSIZE - 1) +#endif + +/*-------------------------------------------------------------------------*/ + +typedef struct child_s child_t; + +/* --- ticket: one ticket for a TCP connection --- + * + * The actual ticket is composed of the .rnd and .seq + * field - the union with c[] helps in memcpy()s. + */ + +union ticket_u +{ + struct ticket_s + { + long rnd, seq; + } s; + char c[1]; +}; + +/* --- struct child_s: one child --- + * + * This structure is used to describe one child. + */ + +struct child_s +{ + int socket; /* Socket this child holds */ + + union /* Child specific information */ + { + struct /* Subserver child */ + { + pid_t pid; /* pid of the subserver */ + time_t last_used; /* time of the last use */ + } c; + + struct /* Communication child */ + { + char handle[4]; + /* The handle assigned by the driver */ + union ticket_u ticket; + time_t last_recv; + /* Time of the last received transmission */ + int bytes_recv; + /* Number of bytes left to send to the driver */ + } s; + } u; + + int state; +# define CHILD_FREE 0 /* Idle subserver */ +# define CHILD_LISTEN 1 +# define CHILD_BUSY 2 /* Active subserver */ +# define CHILD_UDP 3 /* UDP socket child */ +# define CHILD_TCP 4 /* TCP socket child */ +# define CHILD_ACCEPT 5 /* TCP port waiting for connections */ + + child_t *next_free; /* Next child in free list */ + child_t *next_all; /* Next child in its list */ +}; + +/*-------------------------------------------------------------------------*/ + +static child_t childs[MAX_CHILDS] = {{0}}; + /* Table of all children created so far. + */ + +static int next_child_index = 0; + /* Next index in childs[] to use. + */ + +static child_t *all_childs = NULL; + /* List of all children connected to active subservers. + */ + +static child_t *free_childs = NULL; + /* List of all children connected to idle subservers. + */ + +static child_t *udp_sockets = NULL; + /* List of children with an open UDP socket. + */ + +static child_t *tcp_sockets = NULL; + /* List of children with an open TCP connection. + */ + +static child_t *accept_sockets = NULL; + /* List of children waiting for TCP connections. + */ + +static child_t *child_slots = NULL; + /* List of unused children. + */ + +static int childs_waited_for = 0; + /* Number of children which termination has been acknowledged by wait(). + * If waitpid() is not available, this value is compared with + * childs_terminated to see if there are zombies to be waited for. + */ + +static volatile int childs_terminated = 0; + /* Count of terminated children, maintained by SIGCLD. + */ + +static fd_set current_fds, readfds; + /* Master 'read' fdset and the copy for select(). + */ + +static fd_set current_fds2, writefds; + /* Master 'write' fdset and the copy for select(). + */ + +static int nfds = 2; + /* Number of opened fds for select(). + */ + +static time_t current_time; + /* The current time() after select() returns. + */ + +static char buf[ERQ_BUFSIZE]; + /* The receive buffer. + */ + +static const char * erq_dir = ERQ_DIR; + /* The filename of the directory with the ERQ executables. */ + +/*-------------------------------------------------------------------------*/ +/* Forward declarations */ + +static void start_subserver (long server_num, long seed); + +/*-------------------------------------------------------------------------*/ +static char * +time_stamp (void) + +/* Return a textual representation of the current time + * in the form "YYYY.MM.DD HH:MM:SS [xerq]". + * Result is a pointer to a static buffer. + * + * Putting this function in strfuns is not a good idea, because + * it is need by almost every module anyway. + */ + +{ + time_t t; + static char result[27]; + struct tm *tm; + + t = time(NULL); + tm = localtime(&t); + strftime(result, sizeof(result), "%Y.%m.%d %H:%M:%S [erq]", tm); + return result; +} /* time_stamp() */ + +/*-------------------------------------------------------------------------*/ +static long +get_seed(void) + +/* Return a seed for the random generator. + */ + +{ +#ifdef HAVE_GETTIMEOFDAY + + struct timeval tv; struct timezone tz; + + gettimeofday(&tv, &tz); + return tv.tv_sec * 1000000L + tv.tv_usec; + +#else + + /* SunOS crashes on times(0), and times returns 0/-1 */ + + ret = (long)times(&dummy_tms); + if (!ret) + ret = (long)time((time_t *)0) + + dummy_tms.tms_utime + dummy_tms.tms_stime + + dummy_tms.tms_cutime + + dummy_tms.tms_cstime; + return ret; + +#endif +} /* get_seed() */ + +/*-------------------------------------------------------------------------*/ +static long +read_32 (char *str) + +/* Extract an int32 from the data stream at and return it. + * TODO: Put functions like this, and complete msg-functions into + * TODO:: an erqlib.c module. + */ + +{ + unsigned char *p = (unsigned char *)str; + return (long)p[0]<<24 | (long)p[1]<<16 | (long)p[2]<<8 | p[3]; +} /* read_32() */ + +/*-------------------------------------------------------------------------*/ +static void +write_32 (char *str, long n) + +/* Store the int32 into the data stream at . + */ + +{ + *(str+=3) = n; + *--str = n >>= 8; + *--str = n >>= 8; + *--str = n >>= 8; +} /* write_32() */ + +/*-------------------------------------------------------------------------*/ +static long +readn (int s, void *buf, long expected) + +/* Read at least bytes from fd into . + * Return the number of bytes actually read; 0 for EOF and -1 on error. + */ + +{ + long num, total = 0; + char * pbuf; + + pbuf = (char *)buf; + do { + do + num = read(s, pbuf+total, expected-total); + while (num == -1 && errno == EINTR); + + if (num <= 0) + { + if (!num) + { + fprintf(stderr, "%s read: EOF\n", time_stamp()); + return 0; + } + else + { + perror("read"); + return -1; + } + } + total += num; +#if ERQ_DEBUG > 0 + if (total < expected) + fprintf(stderr, "%s read fragment %ld\n", time_stamp(), num); +#endif + } while (num > 0 && total < expected); + + return total; +} /* readn() */ + +/*-------------------------------------------------------------------------*/ +static void +writen (int n, void *message, long length) + +/* Write the of bytes onto fd . + */ + +{ + long num; + + do + num = write(n, message, length); + while (num == -1 && errno == EINTR); + + if (num != length) + { + fprintf(stderr, "%s wrote %ld, should be %ld\n" + , time_stamp(), num, length); + fprintf(stderr, "%s Giving up.\n", time_stamp()); + abort(); + } +} /* writen() */ + +/*-------------------------------------------------------------------------*/ +static void +write1 (void *message, long length) + +/* Write the of bytes onto stdout, ie to the gamedriver. + */ + +{ + writen(1, message, length); +} /* write1() */ + +/*-------------------------------------------------------------------------*/ +static int +execute (char *buf, long buflen, char *status, int *sockets) + +/* exec() the command (length ), store the ERQ_-success code + * into and return the pid (0 on failure). + * If is non-NULL, it points to an int[4] into which two + * socketpairs are generated. + */ + +{ + char path[256], argbuf[1024], *p, *args[96], **argp, c; + pid_t pid; + int quoted; + + quoted = 0; + status[0] = ERQ_E_FORKFAIL; /* Good default */ + status[1] = 0; + + if (buflen >= sizeof argbuf) + { + status[0] = ERQ_E_ARGLENGTH; + return 0; + } + + argp = &args[0]; + *argp++ = p = argbuf; + while (--buflen >= 0) + { + c = *buf++; + if (c == '\\') + { + if (--buflen >= 0) + { + *p++ = *buf++; + } + else + { + status[0] = ERQ_E_ARGFORMAT; + return 0; + } + } + else if (c == '"') + { + quoted = !quoted; + } + else if (isgraph(c) || quoted) + { + *p++ = c; + } + else + { + *p++ = '\0'; + *argp++ = p; + if (argp == &args[sizeof args/sizeof args[0]]) + { + status[0] = ERQ_E_ARGNUMBER; + return 0; + } + while( ( (c = *buf) == ' ' || c == '\t') && --buflen >= 0) + buf++; + } + } + + *p++ = '\0'; + *argp++ = 0; + p = args[0]; + if (p[0] == '/' || strstr(p, "..")) + { + status[0] = ERQ_E_ILLEGAL; + return 0; + } + + if (strlen(erq_dir) + strlen(p) + 2 > sizeof(path)) + { + status[0] = ERQ_E_PATHLEN; + return 0; + } + + sprintf(path, "%s/%s", erq_dir, p); + if (sockets) + { + if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) + { + perror("socketpair"); + status[0] = ERQ_E_FORKFAIL; + status[1] = errno; + return 0; + } + if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets + 2) < 0) + { + perror("socketpair"); + close(sockets[0]); + close(sockets[1]); + status[0] = ERQ_E_FORKFAIL; + status[1] = errno; + return 0; + } + } + + pid = fork(); + if (!pid) + { + close(0); + close(1); + if (sockets) + { + dup2(sockets[0], 0); + dup2(sockets[0], 1); + dup2(sockets[2], 2); + close(sockets[0]); + close(sockets[1]); + close(sockets[2]); + close(sockets[3]); + } + execv(path, args); + _exit(1); + } + + if (sockets) + { + close(sockets[0]); + close(sockets[2]); + } + + if (pid < 0) + { + if (sockets) + { + close(sockets[1]); + close(sockets[3]); + } + status[0] = ERQ_E_FORKFAIL; + status[1] = errno; + return 0; + } + + return pid; +} /* execute() */ + +/*-------------------------------------------------------------------------*/ +static void +count_sigcld (int sig) + +/* SIGCLD handler, which is called whenever a child process terminates. + * The function takes care to handle multiple SIGCLD occuring at + * the same time - the tricky point is to setup the the next signal(SIGCLD,...) + * after the last pending SIGCLD has been resolved. + */ + +{ +#ifdef __MWERKS__ +# pragma unused(sig) +#endif + +#ifndef HAVE_WAITPID + static volatile int calling_signal = 0; /* Mutex */ + static volatile int call_signal_again = 0; /* Number of pending calls */ + + if (!calling_signal) + { + calling_signal = 1; + (void)signal(SIGCLD, (RETSIGTYPE(*)())count_sigcld); + while (call_signal_again) + { + --call_signal_again; + (void)signal(SIGCLD, (RETSIGTYPE(*)())count_sigcld); + } + calling_signal = 0; + } + else + { + call_signal_again++; + } +#if ERQ_DEBUG > 0 + write(2, "child terminated\n", 17); +#endif + childs_terminated++; +#endif +} /* count_sigcld() */ + +/*-------------------------------------------------------------------------*/ +static void +dispose_child (child_t *child) + +/* The subserver is dead and to be cleant up. + */ + +{ + struct child_s **chp; + + /* Remove it from the fdsets */ + FD_CLR(child->socket, ¤t_fds); + FD_CLR(child->socket, ¤t_fds2); + close(child->socket); + + /* Remove it from the free list */ + if (child->state == CHILD_FREE) + { + for (chp = &free_childs; *chp != child; chp = &(*chp)->next_free) /* NOOP */; + *chp = child->next_free; + } + + child->state = CHILD_FREE; + + /* Remove it from the all list */ + for (chp = &all_childs; *chp != child; chp = &(*chp)->next_all) /* NOOP */; + *chp = child->next_all; + + /* Put it into the list of unused children */ + child->next_all = child_slots; + child_slots = child; +} /* dispose_child() */ + +/*-------------------------------------------------------------------------*/ +static void +kill_child (child_t *child) + +/* Kill the subserver child . + */ + +{ +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s kill_child called\n", time_stamp()); +#endif + kill(child->u.c.pid, SIGKILL); + dispose_child(child); +} /* kill_child() */ + +/*-------------------------------------------------------------------------*/ +static int +free_socket_childs (void) + +/* Is there a child available? + */ + +{ + return !(!child_slots && next_child_index >= MAX_CHILDS); +} /* free_socket_childs() */ + +/*-------------------------------------------------------------------------*/ +static child_t * +get_socket_child(void) + +/* Get a new socket child - if possible, reuse an earlier child. + */ + +{ + struct child_s *child; + + if ( !(child = child_slots) ) + { + child = &childs[next_child_index++]; + } + else + { + child_slots = child->next_all; + } + + return child; +} /* get_socket_child() */ + +/*-------------------------------------------------------------------------*/ +static void +free_socket_child (child_t *child, child_t **listp) + +/* The socket in list is no longer used - remove it + * from the list and make it available again. + */ + +{ + while (*listp != child) + listp = &(*listp)->next_all; + *listp = child->next_all; + child->next_all = child_slots; + child->state = CHILD_FREE; + child_slots = child; +} /* free_socket_child() */ + +/*-------------------------------------------------------------------------*/ +static int +get_subserver (void) + +/* Start a new subserver, resp. reactivate an idle one, and return + * its socket. + */ + +{ + struct child_s *child; + +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s get_subserver called\n", time_stamp()); +#endif + + if (free_childs) + { + /* There is an idle one we can use */ + + child = free_childs; + free_childs = child->next_free; + } + else + { + /* We have to start a new subserver */ + + int sockets[2]; + int pid; + + if (!free_socket_childs()) + return -1; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) + { + int err = errno; + fprintf(stderr, "%s Error in socketpair()", time_stamp()); + errno = err; + perror("socketpair"); + return -1; + } + + pid = fork(); + if(pid == -1) + { + close(sockets[0]); + close(sockets[1]); + return -1; + } + + child = get_socket_child(); + if (!pid) + { + /* This is the child process */ + + dup2(sockets[0], 0); /* Read and write on the same socket */ + dup2(sockets[0], 1); + close(sockets[0]); + close(sockets[1]); + start_subserver(child - &childs[0], (long)get_ticket()); + /* NOTREACHED */ + } + + /* Set up the child and put it into the subserver list */ + + close(sockets[0]); + child->u.c.pid = pid; + child->socket = sockets[1]; + FD_SET(child->socket, ¤t_fds); + if (child->socket >= nfds) + nfds = child->socket + 1; + + child->next_all = all_childs; + all_childs = child; + } + + child->state = CHILD_BUSY; + return child->socket; +} /* get_subserver() */ + +/*-------------------------------------------------------------------------*/ +static void +start_subserver (long server_num, long seed) + +/* We are ERQ subserver number and shall use for our ticket. + * The ERQ main process forks us off to do some time-expensive things + * (like looking up hostnames). If we are idle too long, we will be killed + * off again. Communication to the main process is through stdin/stdout. + */ + +{ + union ticket_u ticket; + char header[16]; + long num, msglen; + int request; + pid_t child = 0; + int child_sockets[4]; + char child_handle[4]; + +#if defined(DETACH) && defined(TIOCNOTTY) + /* Detach from console */ + + num = open("/dev/tty", O_RDWR); + if (num >= 0) + { + /* We supply header as a 'dummy' argument in case typing is + * too strict + */ + ioctl(num, TIOCNOTTY, header); + close(num); + } +#endif + randomize_tickets(seed ^ get_seed()); + FD_ZERO(&readfds); + FD_ZERO(&writefds); + nfds = 1; + childs_terminated = 0; + childs_waited_for = 0; + + /* possible race conditions make switching the signal handler awkward */ + (void)signal(SIGCLD, (RETSIGTYPE(*)())count_sigcld); + + for (;;) /* The Loop (tm) */ + { + /* select() for data */ + + if (child) + { + FD_SET(child_sockets[1], &readfds); + FD_SET(child_sockets[3], &readfds); + } + FD_SET(0, &readfds); + num = select(nfds, &readfds, 0, 0, 0); + if (num < 0) + { + if (errno == EINTR) + continue; + perror ("select"); + abort (); + } + + /* Look for data from our child. + * We do this before we wait for a died child to make sure that + * all the data produced by the child is received by us. + */ + + if (child) + { + int n = 3; + do { + if (FD_ISSET(child_sockets[n], &readfds)) + { + do + num = read(child_sockets[n], buf+14, MAX_REPLY - 13); + while (num == -1 && errno == EINTR); + + if (num <= 0) + { + perror("read from spawned child\n"); + } + else + { +#if ERQ_DEBUG > 0 + fprintf(stderr, + "%s %d bytes from socket no. %d\n", time_stamp(), num, n); + fprintf(stderr, + "%s '%.*s'\n", time_stamp(), num, buf+14); +#endif + write_32(buf, (num += 14) - 1); + write_32(buf+4, ERQ_HANDLE_KEEP_HANDLE); + buf[8] = CHILD_LISTEN; + memcpy(buf+9, child_handle, 4); + buf[13] = n == 1 ? ERQ_STDOUT : ERQ_STDERR; + write1(buf, num); + } + } + } while ((n-=2) > 0); + } + + /* Check for zombie children and wait for them */ + +#ifdef HAVE_WAITPID + while(1) { +#else + while (childs_terminated != childs_waited_for) { +#endif + wait_status_t status; + pid_t pid; + + do { +#ifdef HAVE_WAITPID + pid = waitpid(-1, &status, WNOHANG); +#else + pid = wait(&status); +#endif +#ifdef ERESTARTSYS + if (pid < 0 && errno == ERESTARTSYS) + continue; +#endif + } while (0); + +#ifdef HAVE_WAITPID + if (pid <= 0) + break; +#else + if (pid <= 0) + { + fprintf(stderr, + "%s SIGCLD handler %d times called, but wait not sucessful\n", + time_stamp(), childs_terminated - childs_waited_for + ); + abort(); + } +#endif + if (pid == child) + { + /* Our child terminated - prepare the header for the reply. + */ + header[8] = CHILD_FREE; + if (WIFEXITED(status)) + { + header[9] = ERQ_EXITED; + header[10] = WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) + { + header[9] = ERQ_SIGNALED; + header[10] = WTERMSIG(status); + } + else + { + header[9] = ERQ_E_UNKNOWN; + header[10] = 0; + } + child = 0; + close(child_sockets[1]); + close(child_sockets[3]); + FD_CLR(child_sockets[1], &readfds); + FD_CLR(child_sockets[3], &readfds); + write_32(header, 10); + memcpy(header+4, child_handle, 4); + write1(header, 11); /* releases handle */ + } + childs_waited_for++; + } /* wait for zombies */ + + if (!FD_ISSET(0, &readfds)) + continue; + + /* Receive the new request from the master ERQ */ + + do + num = readn(0, header, 9); + while (num == -1 && errno == EINTR); + + if (num != 9) + { + fprintf(stderr, "%s read %ld, should be 9!\n", time_stamp(), num); + if (num < 0) + perror("read"); + break; + } + + msglen = read_32(header) - 9; + request = header[8]; + + /* ... and the rest of the data */ + + if (msglen > 0) + { + if (msglen > sizeof(buf)) + { + /* Prevent a buffer overflow */ + fprintf(stderr, "%s ERROR: Read %ld > buffer size %ld\n", time_stamp(), msglen, sizeof(buf)); + num = readn(0, buf, sizeof(buf)); + + /* Discard the rest of the input from the channel */ + msglen -= num; + while (msglen > 0) + { + char tmp[256]; + num = readn(0, tmp, sizeof(tmp)); + msglen -= num; + } + + msglen = sizeof(buf); /* Pretent this was all */ + } + else + { + num = readn(0, buf, msglen); + if (num != msglen) { + fprintf(stderr, "%s Read %ld, should be %ld\n", time_stamp(), num, msglen); + break; + } + } + } + + /* What does the ERQ want? */ + + switch(request) + { + case ERQ_RLOOKUP: + { + /* Lookup ip -> name */ + + struct hostent *hp; + + /* handle stays in header[4..7] */ + header[8] = CHILD_FREE; + memcpy(header+9, buf, 4); /* copy address */ + hp = gethostbyaddr(buf, 4, AF_INET); + if (!hp && h_errno == TRY_AGAIN) + { + /* DNS needs a bit more time */ + sleep(3); + hp = gethostbyaddr(buf, 4, AF_INET); + } + if (!hp && h_errno == TRY_AGAIN) + { + /* DNS needs a even more time */ + sleep(7); + hp = gethostbyaddr(buf, 4, AF_INET); + } + + if (hp) + { + /* Send the address and the name */ + msglen = strlen(hp->h_name) + 1; + write_32(header, msglen + 12); + write1(header, 13); + write1((void*)hp->h_name, msglen); + } + else + { + /* No answer, send just the address */ + write_32(header, 12); + write1(header, 13); + } + break; + } + +#ifdef ERQ_LOOKUP + case ERQ_LOOKUP: + { + /* Lookup name -> ip */ + + struct hostent *hp; + + /* handle stays in header[4..7] */ + header[8] = CHILD_FREE; + memcpy(header+9, buf, msglen); /* copy address */ + buf[msglen] = '\0'; /* Make sure the string is terminated */ +#if ERQ_DEBUG > 3 + fprintf(stderr, "%s: ERQ_LOOKUP '%s'\n", time_stamp(), buf); +#endif + hp = gethostbyname(buf); + if (!hp && h_errno == TRY_AGAIN) + { +#if ERQ_DEBUG > 2 + fprintf(stderr, "%s: ERQ_LOOKUP '%s': trying again in 3 sec.\n", time_stamp(), buf); +#endif + /* DNS needs more time */ + sleep(3); + hp = gethostbyname(buf); + } + if (!hp && h_errno == TRY_AGAIN) + { +#if ERQ_DEBUG > 2 + fprintf(stderr, "%s: ERQ_LOOKUP '%s': trying again in 7 sec.\n", time_stamp(), buf); +#endif + /* DNS needs even more time */ + sleep(7); + hp = gethostbyname(buf); + } + if (hp) + { +#if ERQ_DEBUG > 3 + fprintf(stderr, "%s: ERQ_LOOKUP '%s' -> %d.%d.%d.%d\n" + , time_stamp(), buf + , hp->h_addr[0] & 255 + , hp->h_addr[1] & 255 + , hp->h_addr[2] & 255 + , hp->h_addr[3] & 255 + ); +#endif + /* Send the name and the address */ + msglen = 4; + write_32(header, msglen + 8); + write1(header, 9); + write1(hp->h_addr, msglen); + } + else + { + /* No answer, send an empty message */ + write_32(header, 8); + write1(header, 9); + } + break; + } +#endif + +#ifdef ERQ_RLOOKUPV6 + case ERQ_RLOOKUPV6: + { + int i; + char *mbuff; + struct addrinfo req, *ai, *ai2; + + /* handle stays in header[4..7] */ + header[8] = CHILD_FREE; + buf[msglen] = 0; + + memset(&req, 0, sizeof(struct addrinfo)); + req.ai_family = AF_INET6; + req.ai_flags = AI_CANONNAME; + + i = getaddrinfo(buf, NULL, &req, &ai); + + if (!i) + for (ai2 = ai + ; ai2 && (ai2->ai_family != AF_INET) + && (ai2->ai_family != AF_INET6) + ; ai2 = ai2->ai_next) /* NOOP */; + + if (!i && ai2 &&ai2->ai_canonname) + { + mbuff = malloc(strlen(ai2->ai_canonname)+strlen(buf)+2); + if (!mbuff) + { + perror("malloc"); + exit(errno); + } + strcpy(mbuff, buf); + strcat(mbuff, " "); + strcat(mbuff, ai2->ai_canonname); + msglen = strlen(mbuff) + 1; + } + else + { + mbuff = malloc(strlen("invalid-format")+strlen(buf)+1); + if (!mbuff) + { + perror("malloc"); + exit(errno); + } + strcpy(mbuff, "invalid-format"); + msglen = strlen(mbuff) + 1; + } + + write_32(header, msglen + 8); + write1(header, 9); + write1(mbuff, msglen); + free(mbuff); + if (!i) + freeaddrinfo(ai); + break; + } +#endif /* ERQ_RLOOKUPV6 */ +#ifdef ERQ_LOOKUP_SRV + case ERQ_LOOKUP_SRV: + { + /* lookup list of srv records */ + struct srvhost * r; + struct srvhost * s; + char service[50], proto[20], hostname[200]; + int counter; + char srvbuf[MAX_REPLY]; + + header[8] = CHILD_FREE; + counter = 0; + srvbuf[0] = '\0'; + // TODO: sscanf this from req string + // format: .. + // example: _psyc._tcp.ve.symlynX.com + // getsrv(hostname, service, proto) + memcpy(header + 9, buf, msglen); + buf[msglen] = '\0'; + sscanf(buf, "%49[^.].%19[^.].%199s", service, proto, hostname); +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s: ERQ_LOOKUP_SRV '%s', %s,%s,%s\n", time_stamp(), buf, service, proto, hostname); +#endif + r = getsrv(hostname, service, proto); + if (!r) { + /* lookup failed, send empty message*/ + write_32(header, 8); + write1(header, 9); +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s: ERQ_LOOKUP_SRV could not srv_resolve '%s'\n", time_stamp(), buf); +#endif + break; + } else { + s = r; + while (s) { + /* walk list of structures */ + /* important members: name, port, pref, weight */ + if (strlen(srvbuf) < MAX_REPLY - 1) { + strncat(srvbuf, s -> name, MAX_REPLY - strlen(srvbuf)); + strncat(srvbuf, "\n", MAX_REPLY - strlen(srvbuf)); + + + if (strlen(srvbuf) < MAX_REPLY - 1) { + snprintf(hostname, MAX_REPLY - 1, "%d%c", s -> port, '\n'); + strncat(srvbuf, hostname, MAX_REPLY - strlen(srvbuf)); + + if (strlen(srvbuf) < MAX_REPLY - 1) { + snprintf(hostname, MAX_REPLY - 1, "%d%c", s -> pref, '\n'); + strncat(srvbuf, hostname, MAX_REPLY - strlen(srvbuf)); + if (strlen(srvbuf) < MAX_REPLY - 1) { + snprintf(hostname, MAX_REPLY - 1, "%d%c", s -> weight, '\n'); + strncat(srvbuf, hostname, MAX_REPLY - strlen(srvbuf)); + } else s = NULL; + } else s = NULL; + } else s = NULL; + } else s = NULL; + + s = s -> next; + counter++; + } + freesrvhost(r); + } + write_32(header, strlen(srvbuf) + 9); + write1(header, 9); + write1(srvbuf, strlen(srvbuf) + 1); + + break; + } +#endif /* ERQ_LOOKUP_SRV */ + case ERQ_EXECUTE: + { + /* Execute a program, wait for its termination and + * return the exit status. + */ + + pid_t pid1, pid2; + + header[8] = CHILD_FREE; + if ((pid1 = execute(buf, msglen, &header[9], 0))) + { + wait_status_t status; + + do { + pid2 = wait(&status); +#ifdef ERESTARTSYS + if (pid2 < 0 && errno == ERESTARTSYS) + continue; +#endif + if (pid2 > 0) + childs_waited_for++; + } while (pid2 >= 1 && pid2 != pid1); + + if (pid2 < 1) { + header[9] = ERQ_E_NOTFOUND; + } else if (WIFEXITED(status)) { + header[9] = ERQ_OK; + header[10] = WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + header[9] = ERQ_SIGNALED; + header[10] = WTERMSIG(status); + } else { + header[9] = ERQ_E_UNKNOWN; + } + } + write_32(header, 10); + write1(header, 11); + break; + } + + case ERQ_FORK: + /* Fork off a process and let it run */ + + header[8] = CHILD_FREE; + if (execute(buf, msglen, &header[9], 0)) { + header[9] = ERQ_OK; + } + write_32(header, 10); + write1(header, 11); + break; + + case ERQ_SPAWN: + + /* Similar to ERQ_FORK, but we send back additional + * information to the ERQ so that further communcation + * is possible. + */ + +#if ERQ_DEBUG > 0 + if (child) { + fprintf(stderr, "%s ERQ_SPAWN: busy\n", time_stamp()); + abort(); + } +#endif + + if ((child = execute(buf, msglen, &header[9], child_sockets))) + { + /* We want to avoid race conditions when a stale ticket + * accesses a fresh process (in the unlikely event that + * the rnd part is equal). The below formula ensures that + * more than 71 minutes pass before a stale ticket can + * refer to a new process (assuming gettimeofday get_seed()) + * It also makes ticket guessing harder. + */ + ticket.s.seq += ((get_seed() - ticket.s.seq) & 0x7fffffff) + 1; + ticket.s.rnd = get_ticket(); + if (child_sockets[1] >= nfds) + nfds = child_sockets[1] + 1; + if (child_sockets[3] >= nfds) + nfds = child_sockets[3] + 1; + memcpy(child_handle, header+4, 4); + write_32(buf, 17 + sizeof ticket); + write_32(buf+4, ERQ_HANDLE_KEEP_HANDLE); + buf[8] = CHILD_LISTEN; + memcpy(buf+9, header+4, 4); + buf[13] = ERQ_OK; + write_32(buf+14, server_num); + memcpy(buf+18, ticket.c, sizeof ticket); + write1(buf, 18 + sizeof ticket); + } + else + { + header[8] = CHILD_FREE; + write_32(header, 10); + write1(header, 11); + } + break; + + case ERQ_SEND: + /* Send some data to our child process */ + + if (msglen < sizeof ticket + 1) + goto bad_request; + if (!child) + goto no_child; + + header[8] = CHILD_LISTEN; + if (memcmp(buf, ticket.c, sizeof ticket)) + goto bad_ticket; + + msglen -= sizeof ticket; + do + num = write(child_sockets[1], buf + sizeof ticket, msglen); + while (num == -1 && errno == EINTR); + + if (num == msglen) { + header[9] = ERQ_OK; + write_32(header, 9); + write1(header, 10); + } else { + if (num >= 0) { + header[9] = ERQ_E_INCOMPLETE; + write_32(header+10, num); + } else { + write_32(header+10, errno); + if (0 +#ifdef EAGAIN + || errno == EAGAIN +#endif +#ifdef EWOULDBLOCK + || errno == EWOULDBLOCK +#endif + ) + { + header[9] = ERQ_E_WOULDBLOCK; + } else if (errno == EPIPE) { + header[9] = ERQ_E_PIPE; + } else { + header[9] = ERQ_E_UNKNOWN; + } + } + write_32(header, 13); + write1(header, 14); + } + break; + + case ERQ_KILL: + + /* Send the child a signal */ + + if (msglen != sizeof ticket + 4 && msglen != sizeof ticket) + goto bad_request; + if (!child) { +no_child: + /* could happen due to a race condition */ +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s ERQ_SEND/ERQ_KILL: No child\n", time_stamp()); +#endif + header[8] = CHILD_FREE; + goto notify_bad_ticket; + } + + header[8] = CHILD_LISTEN; + if (memcmp(buf, ticket.c, sizeof ticket)) { +bad_ticket: +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s ticket.s.rnd: %x vs. %x\n", + time_stamp(), ((struct ticket_s *)buf)->rnd, ticket.s.rnd); + fprintf(stderr, "%s ticket.s.seq: %x vs. %x\n", + time_stamp(), ((struct ticket_s *)buf)->seq, ticket.s.seq); +#endif +notify_bad_ticket: + header[9] = ERQ_E_TICKET; + } else { + int sig; + + sig = msglen >= 4 ? read_32(buf+sizeof ticket) : SIGKILL; +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s len: %d sig: %d\n", time_stamp(), msglen, sig); +#endif + if (sig >= 0) + sig = kill(child, sig); + header[9] = sig < 0 ? ERQ_E_ILLEGAL : ERQ_OK; +#if ERQ_DEBUG > 0 + if (sig < 0) + perror("kill"); +#endif + } + write_32(header, 9); + write1(header, 10); + break; + +#ifdef ERQ_AUTH + case ERQ_AUTH: + { + /* Connect to the authd on the remote system, and send + * its answer back the the master ERQ resp. the driver. + */ + struct sockaddr_in server_addr, *remote; + int s; + long mud_port; + long num, total; + + if (msglen != sizeof (struct sockaddr_in) + 4) + goto bad_request; + header[8] = CHILD_FREE; + + remote = (struct sockaddr_in *)buf; + server_addr.sin_family = remote->sin_family; + server_addr.sin_port = htons(AUTH_PORT); + server_addr.sin_addr = remote->sin_addr; + s = socket(remote->sin_family, SOCK_STREAM, 0); + if (s < 0) { + perror("socket"); + goto die; + } + if ( + connect(s, (struct sockaddr *)&server_addr, sizeof server_addr) + < 0) + { + perror("connect"); + write_32(header, 8); + write1(header, 9); + break; + } + mud_port = read_32(buf + msglen - 4); + sprintf(buf, "%d,%ld\r\n", ntohs(remote->sin_port), mud_port); + writen(s, buf, strlen(buf)); + total = 0; + do { + do { + num = read(s, buf+total, MAX_REPLY - 8 - total); + } while (num == -1 && errno == EINTR); + if (num <= 0) { + if (buf[total-2] == '\r' && buf[total-1] == '\n') + break; + perror("read (auth)"); + goto die; + } + total += num; + } while (num > 0 && total < sizeof buf); + close(s); + write_32(header, 8 + total); + write1(header, 9); + write1(buf, total); + break; + } +#endif /* ERQ_AUTH */ + default: +bad_request: + fprintf(stderr, "%s Bad request %d\n", time_stamp(), request); + fprintf(stderr, "%s %x %x %x %x %x %x %x %x %x\n" + , time_stamp() + , header[0],header[1],header[2],header[3] + ,header[4],header[5],header[6],header[7] + ,header[8]); + fprintf(stderr, "%s %c %c %c %c %c %c %c %c %c\n" + , time_stamp() + , header[0],header[1],header[2],header[3] + ,header[4],header[5],header[6],header[7] + ,header[8]); + write_32(header, 8); + header[8] = child ? CHILD_LISTEN : CHILD_FREE; + write1(header, 9); + break; + } + } /* for(;;) */ + +die: + /* We terminate - and also the child if there is one */ + + if (child) + { + if (kill(child, SIGTERM)) + perror("kill"); + sleep(1); + kill(child, SIGKILL); + /* make an attempt to release the handle, but without error + * checking, because things are likely to be screwed up. + */ + write_32(header, 10); + memcpy(header+4, child_handle, 4); + header[9] = ERQ_E_UNKNOWN; + header[10] = 0; + write(1, header, 11); /* releases handle */ + } + + fprintf(stderr, "%s Subserver giving up.\n", time_stamp()); + exit(1); +} /* start_subserver() */ + +/*-------------------------------------------------------------------------*/ +int +main (int argc, char **argv) + +/* Main program of the ERQ. It contains the main loop waiting for requests. + */ + +{ + char header[32]; + long num; + long msglen; + int subserver; + int s; + int num_ready; + struct timeval timeout; + child_t *child, *next_child; + union ticket_u ticket; + + /* Print information about this daemon to help debugging */ + { + fprintf(stderr, "%s Amylaar ERQ %s: Path '%s', debuglevel %d\n" + , time_stamp(), __DATE__, argv[0], ERQ_DEBUG + ); + } + + /* Quick and dirty commandline parser */ + { + int is_forked = 0; + int i; + + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "--forked")) + is_forked = 1; + else if (!strcmp(argv[i], "--execdir")) + { + if (i+1 >= argc) + { + fprintf(stderr, "%s Missing value for --execdir.\n" + , time_stamp()); + goto die; + } + erq_dir = argv[i+1]; + i++; + } + else + { + fprintf(stderr, "%s Unknown argument '%s'.\n" + , time_stamp(), argv[i]); + goto die; + } + } + /* Check if we have been forked off the driver */ + if (is_forked) + { + write1("1", 1); /* indicate sucessful fork/execl */ + } + else + { + fprintf(stderr, "%s dynamic attachement unimplemented\n", time_stamp()); + goto die; + } + } + +#if defined(DETACH) && defined(TIOCNOTTY) + /* Detach from console */ + s = open("/dev/tty", O_RDWR); + if (s >= 0) + { + /* We supply header as a 'dummy' argument in case typing is + * too strict + */ + ioctl(s, TIOCNOTTY, header); + close(s); + } +#endif + + /* Initialize variables */ + + randomize_tickets(get_seed()); + FD_ZERO(¤t_fds); + FD_ZERO(¤t_fds2); + FD_SET(1, ¤t_fds); + (void)signal(SIGCLD, (RETSIGTYPE(*)())count_sigcld); + + /* The main loop */ + + for (subserver = 0;;) + { + int still_corked; /* Determines the select() wait time */ + + /* Scan the list of TCP children and check which sent data + * to the driver more than 3 seconds ago. Those are added + * back to current_fds. + * Existence of children which sent data less than 3 seconds + * ago are flagged as 'still_corked'. + */ + still_corked = 0; + for (next_child = tcp_sockets; NULL != (child = next_child); ) + { + + next_child = child->next_all; + + /* Uncork the bottle */ + if (child->u.s.bytes_recv) + { + if (child->u.s.last_recv + 3 < time(NULL)) + { +#if ERQ_DEBUG > 1 + fprintf(stderr,"%s Uncorking child\n", time_stamp()); +#endif + child->u.s.bytes_recv = 0; + FD_SET(child->socket, ¤t_fds); + } + else + still_corked = 1; + } + } /* for(tcpsockets) */ +#if ERQ_DEBUG > 0 + fprintf(stderr,"%s still_corked = %d\n", time_stamp(), still_corked); +#endif + + /* select() for data. + */ + readfds = current_fds; + writefds = current_fds2; + timeout.tv_sec = (still_corked ? 3 : TIME_TO_CHECK_CHILDS); + timeout.tv_usec = 0; + +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s calling select (nfds = %d)\n", time_stamp(), nfds); +#endif + num_ready = select(nfds, &readfds, &writefds, 0, &timeout); +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s select returns %d\n", time_stamp(), num_ready); +#endif + if (num_ready < 0 && errno != EINTR) + { + perror ("select"); + abort (); + } + current_time = time(NULL); + + /* Kill off idle free children */ + + { + time_t expired; + + expired = current_time - CHILD_EXPIRE; + for (next_child = free_childs; (child = next_child); ) + { + next_child = child->next_free; + if (child->u.c.last_used > expired) + continue; +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s Max child idle time expired.\n", time_stamp()); +#endif + kill_child(child); + } + } + + /* Check for zombie children and wait for them */ + +#ifdef HAVE_WAITPID + while(1) { +#else + while (childs_terminated != childs_waited_for) { +#endif + wait_status_t status; + pid_t pid; + + do { +#ifdef HAVE_WAITPID + pid = waitpid(-1, &status, WNOHANG); +#else + pid = wait(&status); +#endif +#ifdef ERESTARTSYS + if (pid < 0 && errno == ERESTARTSYS) + continue; +#endif + } while (0); + + if (pid <= 0) + break; + + for (child = all_childs; child; child = child->next_all) + { + if (child->u.c.pid == pid) + { + dispose_child(child); + break; + } + } + childs_waited_for++; + } + + if (num_ready > 0) + { + /* Check for data from the subservers for the gamedriver */ + + for (next_child = all_childs; (child = next_child); ) + { + long replylen; + char replyheader[12]; + char replybuf[ERQ_BUFSIZE]; + + next_child = child->next_all; + + s = child->socket; + if (!FD_ISSET(s, &readfds)) + continue; +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s query child %d\n", time_stamp(), child - &childs[0]); +#endif + /* Read the standard erq header plus the one-byte + * child state. + */ + num = readn(s, replyheader, 9); + if (num != 9) + { + fprintf(stderr, "%s read %ld, should be 9.\n", time_stamp(), num); + kill_child(child); + continue; + } + + /* If there is more data, read the rest of message */ + replylen = read_32(replyheader) - 8; + if (replylen > 0) + { + if (replylen > sizeof replybuf) + { + fprintf(stderr, "%s Too long reply.\n", time_stamp()); + kill_child(child); + continue; + } + num = readn(s, replybuf, replylen); + if (num != replylen) + { + fprintf(stderr, "%s read %ld, should be %ld\n" + , time_stamp(), num, replylen); + kill_child(child); + continue; + } + } + + /* Pass the message received (sans the state byte) on + * to the gamedriver. + */ + write1(replyheader, 8); + write1(replybuf, replylen); + /* We can't simply test for ERQ_HANDLE_KEEP_HANDLE, because a + * subserver can have several handles at a time. + */ + if ((child->state = replyheader[8]) == CHILD_FREE) + { + child->u.c.last_used = current_time; + child->next_free = free_childs; + free_childs = child; + } + } +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s queried all children\n", time_stamp()); +#endif + +#ifdef ERQ_OPEN_UDP + /* Check for data received on UDP sockets for the gamedriver */ + + for (next_child = udp_sockets; (child = next_child); ) + { + length_t length; + long replylen; + char replyheader[16]; + char replybuf[ERQ_BUFSIZE]; + struct sockaddr_in addr; + int cnt; + + next_child = child->next_all; + s = child->socket; + if (!FD_ISSET(s, &readfds)) + continue; + + /* Receive the UDP message */ + length = sizeof addr; + cnt = recvfrom( s, replybuf, sizeof(replybuf), 0 + , (struct sockaddr *)&addr, &length); + + /* Compose and send the UDP-erq message to the driver */ + replylen = length + 19; + if (replylen > MAX_REPLY) + replylen = MAX_REPLY; + write_32(replyheader, replylen); + write_32(replyheader+4, ERQ_HANDLE_KEEP_HANDLE); + memcpy(replyheader+8, child->u.s.handle, 4); + replyheader[12] = ERQ_STDOUT; + write1(replyheader, 13); + write1(&addr.sin_addr.s_addr, 4); + write1(&addr.sin_port, 2); + write1(replybuf, replylen - 19); + } +#endif /* ERQ_OPEN_UDP */ + +#ifdef ERQ_OPEN_TCP + + /* Exchange data between the TCP sockets and the driver */ + + for (next_child = tcp_sockets; (child = next_child); ) + { + int length; + long replylen; + char replyheader[30]; + char replybuf[ERQ_BUFSIZE]; + int cnt; + + next_child = child->next_all; + s = child->socket; + + if (FD_ISSET(s, &writefds)) + { + /* Socket is ready for writing - nevertheless check + * if there is data pending. + */ + FD_CLR(s, ¤t_fds2); + cnt = recv(s, replybuf, 1, MSG_PEEK); + + if (cnt < 0 && (errno != EWOULDBLOCK && errno != EAGAIN)) + { + /* TCP connection died - inform the driver and + * get rid of the child. + */ + + replyheader[8] = ERQ_E_UNKNOWN; + replyheader[9] = errno; + replylen = 10; + write_32(replyheader, replylen); + memcpy(replyheader+4, child->u.s.handle, 4); + write1(replyheader, replylen); /* ..and release handle */ + FD_CLR(child->socket,¤t_fds); + FD_CLR(child->socket,¤t_fds2); + close(child->socket); + free_socket_child(child,&tcp_sockets); + break; + } + + /* Inform the driver that there is data pending */ + + replyheader[12] = ERQ_OK; + write_32(replyheader, 17+sizeof(child->u.s.ticket)); + write_32(replyheader+4, ERQ_HANDLE_KEEP_HANDLE); + memcpy(replyheader+8, child->u.s.handle, 4); + write_32(replyheader+13, child - &childs[0]); + memcpy(replyheader+17, child->u.s.ticket.c + , sizeof(child->u.s.ticket)); + write1(replyheader, 17+sizeof(child->u.s.ticket)); + break; + } + else if (!FD_ISSET(s, &readfds)) + continue; + + /* Read the data */ + + cnt = read(s, replybuf, MAX_REPLY-100); + FD_CLR(s,&readfds); + if (cnt <= 0) + { + /* No data there - EOF or error */ + + if (!cnt) + { + replyheader[8] = ERQ_EXITED; + replylen = 9; + } + else + { + replyheader[8] = ERQ_E_UNKNOWN; + replyheader[9] = errno; + replylen = 10; + } + write_32(replyheader, replylen); + memcpy(replyheader+4, child->u.s.handle, 4); + write1(replyheader, replylen); /* ..and release handle */ + FD_CLR(child->socket,¤t_fds); + FD_CLR(child->socket,¤t_fds2); + close(child->socket); + free_socket_child(child,&tcp_sockets); + break; + } + else + { + /* We got data - send it to the driver (but make + * sure not to overrun it). + */ + + length = cnt; +#if 0 + /* Update the "data pending" strategy */ + child->u.s.last_recv = time(NULL); + child->u.s.bytes_recv += cnt; +#endif + + if (child->u.s.bytes_recv > 4096) + { + /* Cork the bottle. Let the MUD swallow first */ + FD_CLR(s, ¤t_fds); +#if ERQ_DEBUG > 1 + fprintf(stderr,"%s Corking child.\n", time_stamp()); +#endif + } + + /* Compose the message for the driver and send it */ + + replylen = length + 13; + if (replylen > MAX_REPLY) + replylen = MAX_REPLY; + write_32(replyheader, replylen); + write_32(replyheader+4, ERQ_HANDLE_KEEP_HANDLE); + memcpy(replyheader+8, child->u.s.handle, 4); + replyheader[12] = ERQ_STDOUT; + write1(replyheader, 13); + write1(replybuf, replylen - 13); + } + } +#endif /* ERQ_OPEN_TCP */ + +#ifdef ERQ_LISTEN + for (next_child = accept_sockets; (child = next_child); ) + { + int length; + long replylen; + char replyheader[30]; + char replybuf[ERQ_BUFSIZE]; + int cnt; + + next_child = child->next_all; + s = child->socket; + if (!FD_ISSET(s, &readfds)) + continue; + FD_CLR(s,¤t_fds); + + /* Connection pending - inform the driver */ + + replylen = 13; + write_32(replyheader, replylen); + write_32(replyheader+4, ERQ_HANDLE_KEEP_HANDLE); + memcpy(replyheader+8, child->u.s.handle, 4); + replyheader[12] = ERQ_STDOUT; + write1(replyheader, replylen); + + } +#endif /* ERQ_LISTEN */ + } /* if (num_ready) */ + + /* We needed to send data to a subserver in the previous loop, + * but didn't get one then. Now try again. + */ + if (subserver < 0) + { + subserver = get_subserver(); + if (subserver < 0) + continue; + writen(subserver, header, 9); + writen(subserver, buf, msglen); + } + + if (num_ready > 0 && FD_ISSET(1, &readfds)) + { + /* TODO: We read from 0 when 1 is ready? */ + + /* Read the erq message incl. request */ + num = readn(0, header, 9); + if (num != 9) + { + fprintf(stderr, "%s Read %ld, should be 9!\n" + , time_stamp(), num); + if (num < 0) + perror("read"); + break; + } +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s read command %d\n", time_stamp(), header[8]); +#endif + + /* Read the rest of the message */ + + msglen = read_32(header) - 9; + if (msglen > 0) + { + num = readn(0, buf, msglen); + if (num != msglen) { + fprintf(stderr, "%s Read %ld, should be %ld\n" + , time_stamp(), num, msglen); + break; + } + } + + /* Switch on the request received */ + switch(header[8]) + { + + /* ----- Send a signal or data ----- */ + case ERQ_SEND: + case ERQ_KILL: + { + long n; + + /* Check the ticket - the next_child_index at the time + * of creation. + */ + n = read_32(buf); + if ((unsigned long)n >= (unsigned long)next_child_index) + goto bad_ticket; + + child = &childs[n]; + switch(child->state) + { + struct child_s **listp; + + bad_ticket: + default: + { +#if ERQ_DEBUG > 0 + fprintf(stderr, "%s Ticket rejected n: 0x%x nxt: 0x%x state: %d\n", + time_stamp(), n, next_child_index, + (unsigned long)n >= (unsigned long)next_child_index ? + 0 : childs[n].state); +#endif + subserver = 0; /* ready for new commands */ + write_32(header, 9); + header[8] = ERQ_E_TICKET; + write1(header, 9); + break; + } + + case CHILD_LISTEN: + /* Pass on the message to the listening child */ + subserver = child->socket; + write_32(header, 9 + msglen - 4); + writen(subserver, header, 9); + writen(subserver, buf+4, msglen-4); + break; + +#ifdef ERQ_LISTEN + case CHILD_ACCEPT: + /* Can only KILL accept children, not SEND data */ + + if (header[8] == ERQ_SEND) + goto bad_ticket; + listp = &accept_sockets; + goto handle_send_on_socket; + + case CHILD_TCP: + listp = &tcp_sockets; + goto handle_send_on_socket; +#endif +#ifdef ERQ_OPEN_UDP + case CHILD_UDP: + listp = &udp_sockets; +#endif +#if defined(ERQ_OPEN_UDP) || defined(ERQ_LISTEN) + handle_send_on_socket: + + /* Check the rest of the ticket */ + + if (msglen < sizeof(union ticket_u) + || memcmp(buf+4, child->u.s.ticket.c, + sizeof(union ticket_u))) + { +#if ERQ_DEBUG > 0 + fprintf(stderr,"%s Ticket mismatch. (%d, %d)\n", time_stamp(), msglen,sizeof(union ticket_u)); +#endif + goto bad_ticket; + } + msglen -= sizeof(union ticket_u); + + subserver = 0; /* ready for new commands */ + write_32(header, 9); /* "msg_len" of the answer */ + if (header[8] == ERQ_SEND) + { + while(1){ + if (child->state == CHILD_UDP) + { + struct sockaddr_in host_ip_addr; + + if (msglen < 6) + { + header[8] = ERQ_E_ARGLENGTH; + break; + } + memcpy(&host_ip_addr.sin_addr, + buf+sizeof(union ticket_u), 4); + host_ip_addr.sin_family = AF_INET; + memcpy(&host_ip_addr.sin_port, + buf+sizeof(union ticket_u)+4, 2); + num = sendto(child->socket, + buf+sizeof(union ticket_u)+6, + msglen - 6, 0, + (struct sockaddr *)&host_ip_addr, + sizeof(host_ip_addr)); + } + else + { + /* Program or TCP socket */ + num = write(child->socket, + buf+sizeof(union ticket_u)+4, + msglen-4); + } + + if (num != msglen-4) + { + /* Prepare the error message to send back */ + if (num < 0) + { + switch(errno) { + case EWOULDBLOCK: +#if EAGAIN != EWOULDBLOCK + case EAGAIN: +#endif + header[3] = 9; + header[8] = ERQ_E_WOULDBLOCK; + break; + case EPIPE: + header[3] = 9; + header[8] = ERQ_E_PIPE; + break; + case EINTR: + continue; + default: + header[3] = 10; + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + } + else + { + header[3] = 13; + header[8] = ERQ_E_INCOMPLETE; + write_32(header+9, num); + } + } + else + { + /* Send back "ok" */ + header[3] = 9; + header[8] = ERQ_OK; + } + break; + } + } + else + { + /* header[8] == ERQ_KILL */ + FD_CLR(child->socket,¤t_fds); + FD_CLR(child->socket,¤t_fds2); + if (child->state == CHILD_ACCEPT) + shutdown(child->socket,0); + close(child->socket); + free_socket_child(child,listp); + write_32(header,9); + header[8] = ERQ_OK; + } + write1(header, header[3]); + break; +#endif /* defined(ERQ_OPEN_UDP) || defined(ERQ_LISTEN) */ + } + break; /* end of ERQ_KILL / ERQ_SEND */ + } + + default: + { + /* Non-communication request: pass it on to + * a new subserver. + */ + subserver = get_subserver(); + if (subserver >= 0) + { + writen(subserver, header, 9); + writen(subserver, buf, msglen); + } + } + break; + +#ifdef ERQ_OPEN_UDP + case ERQ_OPEN_UDP: + { + /* Open a UDP socket */ + + subserver = 0; /* ready for new commands */ + write_32(header, 10); + do { + struct sockaddr_in host_ip_addr; + int tmp; + + if (msglen != 2) + { + header[8] = ERQ_E_ARGLENGTH; + header[9] = 0; + break; + } + host_ip_addr.sin_addr.s_addr = INADDR_ANY; + host_ip_addr.sin_family = AF_INET; + memcpy(&host_ip_addr.sin_port, buf, 2); + if (!free_socket_childs()) + { + header[8] = ERQ_E_NSLOTS; + header[9] = MAX_CHILDS; + break; + } + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + tmp = 1; + if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, + (char *) &tmp, sizeof (tmp)) < 0) + { + close(s); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + if (bind(s, (struct sockaddr *)&host_ip_addr, + sizeof host_ip_addr) == -1) + { + close(s); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + + /* We got the socket, now make the child */ + + child = get_socket_child(); + child->socket = s; + FD_SET(child->socket,¤t_fds); + child->state = CHILD_UDP; + child->next_all = udp_sockets; + udp_sockets = child; + ticket.s.seq += + ((get_seed() - ticket.s.seq) & 0x7fffffff) + 1; + ticket.s.rnd = get_ticket(); + if (s >= nfds) + nfds = s + 1; + memcpy(child->u.s.handle, header+4, 4); + memcpy(child->u.s.ticket.c, ticket.c, sizeof ticket); + + /* Compose the answer to the driver */ + + header[3] = 17 + sizeof ticket; + memcpy(header+8, header+4, 4); + write_32(header+4, ERQ_HANDLE_KEEP_HANDLE); + header[12] = ERQ_OK; + write_32(header+13, child - &childs[0]); + memcpy(header+17, ticket.c, sizeof ticket); + } while(0); + write1(header, header[3]); + } + break; +#endif /* ERQ_OPEN_UDP */ + +#ifdef ERQ_OPEN_TCP + case ERQ_OPEN_TCP: + { + /* Open a TCP socket and connect it to a given + * address. + */ + subserver = 0; /* ready for new commands */ + write_32(header, 10); + do { + struct sockaddr_in host_ip_addr; + int tmp; + + if (msglen != 6) + { + header[8] = ERQ_E_ARGLENGTH; + header[9] = 0; + break; + } + host_ip_addr.sin_family = AF_INET; + memcpy(&host_ip_addr.sin_port, buf+4, 2); + memcpy(&host_ip_addr.sin_addr.s_addr, buf, 4); + + if (!free_socket_childs()) + { + header[8] = ERQ_E_NSLOTS; + header[9] = MAX_CHILDS; + break; + } + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + { + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + if ((tmp = fcntl(s,F_GETFL,0)) < 0) + { + fprintf(stderr,"%s fnctl 1\n", time_stamp()); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + + if ((tmp = fcntl(s,F_SETFL,tmp | O_NDELAY)) < 0) + { + fprintf(stderr,"%s fnctl 2\n", time_stamp()); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + + tmp = 1; + if (connect(s, (struct sockaddr *)&host_ip_addr, + sizeof host_ip_addr) == -1) + { + if(errno != EINPROGRESS) + { + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + close(s); + break; + } + } + + /* Got the socket, now create the child */ + + child = get_socket_child(); + child->socket = s; + FD_SET(child->socket,¤t_fds); + FD_SET(child->socket,¤t_fds2); + child->state = CHILD_TCP; + child->u.s.bytes_recv = 0; + child->next_all = tcp_sockets; + tcp_sockets = child; + ticket.s.seq += + ((get_seed() - ticket.s.seq) & 0x7fffffff) + 1; + ticket.s.rnd = get_ticket(); + if (s >= nfds) + nfds = s + 1; + memcpy(child->u.s.handle, header+4, 4); + memcpy(child->u.s.ticket.c, ticket.c, sizeof ticket); + + /* Compose the answer to the driver */ + + header[3] = 17 + sizeof ticket; + memcpy(header+8, header+4, 4); + write_32(header+4, ERQ_HANDLE_KEEP_HANDLE); + header[12] = ERQ_OK; + header[3] = 0; + write_32(header+13, child - &childs[0]); + memcpy(header+17, ticket.c, sizeof ticket); + } while(0); + if(header[3]) write1(header, header[3]); + } + break; +#endif /* ERQ_OPEN_TCP */ + +#ifdef ERQ_LISTEN + case ERQ_LISTEN: + { + subserver = 0; /* ready for new commands */ + write_32(header, 10); + do { + struct sockaddr_in host_ip_addr; + int tmp; + + if (msglen != 2) + { + header[8] = ERQ_E_ARGLENGTH; + header[9] = 0; + break; + } + host_ip_addr.sin_family = AF_INET; + host_ip_addr.sin_addr.s_addr = INADDR_ANY; + memcpy(&host_ip_addr.sin_port, buf, 2); + + if (!free_socket_childs()) + { + header[8] = ERQ_E_NSLOTS; + header[9] = MAX_CHILDS; + break; + } + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + { + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + if((tmp = fcntl(s,F_GETFL,0)) < 0) + { + fprintf(stderr,"%s fnctl 1\n", time_stamp()); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + + if((tmp = fcntl(s,F_SETFL,tmp | O_NDELAY)) < 0) + { + fprintf(stderr,"%s fnctl 2\n", time_stamp()); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + tmp = 1; + if (bind(s, (struct sockaddr *)&host_ip_addr, + sizeof host_ip_addr) == -1) + { + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + if (listen(s,2) < 0) + { + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + + /* Got the socket, now create the child */ + + child = get_socket_child(); + child->socket = s; + FD_SET(child->socket,¤t_fds); + child->state = CHILD_ACCEPT; + child->next_all = accept_sockets; + accept_sockets = child; + ticket.s.seq += + ((get_seed() - ticket.s.seq) & 0x7fffffff) + 1; + ticket.s.rnd = get_ticket(); + if (s >= nfds) + nfds = s + 1; + memcpy(child->u.s.handle, header+4, 4); + memcpy(child->u.s.ticket.c, ticket.c, sizeof ticket); + + /* Compose the message for the driver */ + + header[3] = 17 + sizeof ticket; + memcpy(header+8, header+4, 4); + write_32(header+4, ERQ_HANDLE_KEEP_HANDLE); + header[12] = ERQ_OK; + write_32(header+13, child - &childs[0]); + memcpy(header+17, ticket.c, sizeof ticket); + } while(0); + if(header[3]) write1(header, header[3]); + } + break; +#endif /* ERQ_LISTEN */ + +#ifdef ERQ_ACCEPT + case ERQ_ACCEPT: + { + + /* Accept a connection from a socket */ + + subserver = 0; /* ready for new commands */ + write_32(header, 10); + do { + struct child_s *parent; + struct sockaddr_in host_ip_addr; + long tmp2; + int tmp; + length_t len; + int n; + + if (msglen != sizeof(union ticket_u) + 4) + { + header[8] = ERQ_E_ARGLENGTH; + header[9] = 0; + break; + } + + n = read_32(buf); + if((unsigned long) n >= (unsigned long) next_child_index) + { + fprintf(stderr,"%s given: %d, nxt: %d\n", time_stamp(), n,next_child_index); + goto accept_bad_ticket; + } + + parent = &childs[n]; + if(parent->state != CHILD_ACCEPT) + { + fprintf(stderr,"%s State is %d, should be %d!\n", time_stamp(), parent->state,CHILD_ACCEPT); + goto accept_bad_ticket; + } + + if(memcmp(buf+4,parent->u.s.ticket.c,sizeof(union ticket_u))) + { + accept_bad_ticket: + fprintf(stderr,"%s Accept: Ticket mismatch.\n", time_stamp()); + header[8] = ERQ_E_TICKET; + header[9] = 0; + break; + } + + if (!free_socket_childs()) + { + header[8] = ERQ_E_NSLOTS; + header[9] = MAX_CHILDS; + break; + } + + len = sizeof(host_ip_addr); + s = accept( parent->socket + , (struct sockaddr *) &host_ip_addr + , &len); + if (s < 0) + { + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + if((tmp = fcntl(s,F_GETFL,0)) < 0) + { + fprintf(stderr,"%s fnctl 1\n", time_stamp()); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + + if((tmp = fcntl(s,F_SETFL,tmp | O_NDELAY)) < 0) + { + fprintf(stderr,"%s fnctl 2\n", time_stamp()); + header[8] = ERQ_E_UNKNOWN; + header[9] = errno; + break; + } + tmp = 1; + child = get_socket_child(); + child->socket = s; + + /* Socket accepted, wait for more conns */ + + FD_SET(parent->socket, ¤t_fds); + FD_SET(child->socket, ¤t_fds); + child->state = CHILD_TCP; + child->u.s.bytes_recv = 0; + child->next_all = tcp_sockets; + tcp_sockets = child; + ticket.s.seq += + ((get_seed() - ticket.s.seq) & 0x7fffffff) + 1; + ticket.s.rnd = get_ticket(); + if (s >= nfds) + nfds = s + 1; + memcpy(child->u.s.handle, header+4, 4); + memcpy(child->u.s.ticket.c, ticket.c, sizeof ticket); + header[3] = 23 + sizeof ticket; + memcpy(header+8, header+4, 4); + write_32(header+4, ERQ_HANDLE_KEEP_HANDLE); + header[12] = ERQ_OK; + memcpy(header+13, &host_ip_addr.sin_addr.s_addr, 4); + memcpy(header+17, &host_ip_addr.sin_port, 2); + write_32(header+19, child - &childs[0]); + memcpy(header+23, ticket.c, sizeof ticket); + } while(0); + if (header[3]) + write1(header, header[3]); + } + break; +#endif /* ERQ_ACCEPT */ + + } /* switch() */ + } /* if (num_ready > 0 && FD_ISSET(1, &readfds)) */ + + if (subserver < 0) + FD_CLR(1, ¤t_fds); + else + FD_SET(1, ¤t_fds); + } /* main loop */ + +die: + fprintf(stderr, "%s Giving up.\n", time_stamp()); + return 1; +} /* main() */ + +/***************************************************************************/ + diff --git a/config/gentoo/ldmud/files/erq/erq.h b/config/gentoo/ldmud/files/erq/erq.h new file mode 100644 index 0000000..013f0ef --- /dev/null +++ b/config/gentoo/ldmud/files/erq/erq.h @@ -0,0 +1,60 @@ +/* external request demon interface definitions */ + +#ifndef ERQ_H__ +#define ERQ_H__ 1 + +/* servive request types */ + +#define ERQ_RLOOKUP 0 /* Lookup ip -> name */ +#define ERQ_EXECUTE 1 /* Execute a program */ +#define ERQ_FORK 2 /* Fork a program */ +#define ERQ_AUTH 3 /* Connect to a remote authd */ +#define ERQ_SPAWN 4 /* Spawn a program */ +#define ERQ_SEND 5 /* Send data to a program or connection */ +#define ERQ_KILL 6 /* Kill a program or connection */ +#define ERQ_OPEN_UDP 7 /* Open a UDP socket */ +#define ERQ_OPEN_TCP 8 /* Open a TCP connection */ +#define ERQ_LISTEN 9 /* Open a TCP accept-socket */ +#define ERQ_ACCEPT 10 /* Accept a connection from a accept-socket */ +#define ERQ_LOOKUP 11 /* Lookup name -> ip */ + +#ifdef __IPV6__ +#define ERQ_RLOOKUPV6 12 /* Lookup name/ip6 */ +#endif +#define ERQ_LOOKUP_SRV 13 + +/* Additional service request type flags evaluated by efun send_erq(). + * The ERQ itself won't get to see it. + */ + +#define ERQ_CB_STRING (1 << 31) /* Callback closure takes a string arg */ + + +/* answers from ERQ_EXECUTE / ERQ_FORK */ + +#define ERQ_OK 0 +#define ERQ_SIGNALED 1 +#define ERQ_E_NOTFOUND 2 /* process not found by wait */ +#define ERQ_E_UNKNOWN 3 /* unknown exit condition from wait() */ +#define ERQ_E_ARGLENGTH 4 +#define ERQ_E_ARGFORMAT 5 +#define ERQ_E_ARGNUMBER 6 +#define ERQ_E_ILLEGAL 7 +#define ERQ_E_PATHLEN 8 +#define ERQ_E_FORKFAIL 9 +#define ERQ_E_TICKET 11 +#define ERQ_E_INCOMPLETE 12 +#define ERQ_E_WOULDBLOCK 13 +#define ERQ_E_PIPE 14 +#define ERQ_STDOUT 15 /* Normal data received */ +#define ERQ_STDERR 16 +#define ERQ_EXITED 17 /* Connection closed on EOF */ +#define ERQ_E_NSLOTS 18 + +/* reserved handles */ + +#define ERQ_HANDLE_RLOOKUP (-1) +#define ERQ_HANDLE_KEEP_HANDLE (-2) +#define ERQ_HANDLE_RLOOKUPV6 (-3) + +#endif /* ERQ_H__ */ diff --git a/config/gentoo/ldmud/files/erq/srv.c b/config/gentoo/ldmud/files/erq/srv.c new file mode 100644 index 0000000..0fcf672 --- /dev/null +++ b/config/gentoo/ldmud/files/erq/srv.c @@ -0,0 +1,215 @@ +#include "srv.h" + + +#include +#include +#include +#include // For solaris +#include +#include + +#include + +/* the biggest packet we'll send and receive */ +#if PACKETSZ > 1024 +#define MAXPACKET PACKETSZ +#else +#define MAXPACKET 1024 +#endif + +/* and what we send and receive */ +typedef union { + HEADER hdr; + u_char buf[MAXPACKET]; +} querybuf; + +#ifndef T_SRV +#define T_SRV 33 +#endif + + +void freesrvhost ( struct srvhost * s ) +{ + struct srvhost * n; + while( s ) { + n = s->next; + /* hack to make double-free visible by causing null dereference */ + s->next = NULL; + free( (void *)s ); + s = n; + } +} + + +static int compare( const void * a, const void * b ) +{ + struct srvhost * aa, * bb; + + if ( !a ) + return 1; + if ( !b ) + return -1; + + aa = (struct srvhost *) *(int*)a; + bb = (struct srvhost *) *(int*)b; + + if ( aa->pref > bb->pref ) + return 1; + if ( aa->pref < bb->pref ) + return -1; + + if ( aa->rweight > bb->rweight ) + return -1; + if ( aa->rweight < bb->rweight ) + return 1; + + return 0; +} + + +struct srvhost * getsrv( const char * domain, + const char * service, const char * protocol ) { + querybuf answer; /* answer buffer from nameserver */ + int n; + char * zone; + int ancount, qdcount; /* answer count and query count */ + HEADER *hp; /* answer buffer header */ + struct srvhost **replyarray; + struct srvhost * firsthost; + int answerno; + u_char hostbuf[256]; + u_char *msg, *eom, *cp; /* answer buffer positions */ + int dlen, type, pref, weight, port; + + if ( !domain || !*domain || + !service || !*service || + !protocol || !*protocol ) + return NULL; + + zone = (char *)malloc( strlen( domain ) + + strlen( service ) + + strlen( protocol ) + 20 ); + if (zone == NULL) + return NULL; + + *zone = '\0'; + + if (*service != '_') // If service and protocol do not start with a + strcat(zone, "_"); // _, prepend the _ to them... + + strcat(zone, service); + strcat(zone, "."); + + if (*protocol != '_') + strcat(zone, "_"); + + strcat(zone, protocol); + strcat(zone, "."); + + strcat(zone, domain); + + n = res_query( zone, C_IN, T_SRV, (u_char *)&answer, sizeof( answer ) ); + + (void) free( zone ); + zone = NULL; + + if ( n < (int)sizeof(HEADER) ) + return NULL; + + /* valid answer received. skip the query record. */ + + hp = (HEADER *)&answer; + qdcount = ntohs(hp->qdcount); + ancount = ntohs(hp->ancount); + + msg = (u_char *)&answer; + eom = (u_char *)&answer + n; + cp = (u_char *)&answer + sizeof(HEADER); + + while ( qdcount-- > 0 && cp < eom ) { + n = dn_expand( msg, eom, cp, (char *)hostbuf, 256 ); + if (n < 0) + return NULL; + cp += n + QFIXEDSZ; + } + + /* make a big-enough (probably too big) reply array */ + + replyarray + = (struct srvhost **) malloc( ancount * sizeof(struct srvhost *) ); + for( n = 0; n < ancount; n++ ) + replyarray[n] = NULL; + answerno = 0; + + /* loop through the answer buffer and extract SRV records */ + while ( ancount-- > 0 && cp < eom ) { + n = dn_expand( msg, eom, cp, (char *)hostbuf, 256 ); + if ( n < 0 ) { + for( n = 0; n < answerno; n++ ) + (void) free( replyarray[n] ); + (void)free( replyarray ); + return NULL; + } + + cp += n; + + type = _getshort(cp); + cp += sizeof(u_short); + + /* class = _getshort(cp); */ + cp += sizeof(u_short); + + /* ttl = _getlong(cp); */ + cp += sizeof(u_long); + + dlen = _getshort(cp); + cp += sizeof(u_short); + + if ( type != T_SRV ) { + cp += dlen; + continue; + } + + pref = _getshort(cp); + cp += sizeof(u_short); + + weight = _getshort(cp); + cp += sizeof(u_short); + + port = _getshort(cp); + cp += sizeof(u_short); + + n = dn_expand( msg, eom, cp, (char *)hostbuf, 256 ); + if (n < 0) + break; + cp += n; + + replyarray[answerno] + = (struct srvhost *)malloc( sizeof( struct srvhost ) + + strlen( (char *)hostbuf ) ); + replyarray[answerno]->pref = pref; + replyarray[answerno]->weight = weight; + if ( weight ) + replyarray[answerno]->rweight = 1+random()%( 10000 * weight ); + else + replyarray[answerno]->rweight = 0; + replyarray[answerno]->port = port; + replyarray[answerno]->next = NULL; + strcpy( replyarray[answerno]->name, (char *)hostbuf ); + + answerno++; + } + if (answerno == 0) return NULL; + qsort( replyarray, answerno, sizeof( struct srvhost * ), + compare ); + + // Recreate a linked list from the sorted array... + for( n = 0; n < answerno; n++ ) + replyarray[n]->next = replyarray[n+1]; + replyarray[answerno-1]->next = NULL; + + firsthost = replyarray[0]; + (void) free( replyarray ); + return firsthost; +} + diff --git a/config/gentoo/ldmud/files/erq/srv.h b/config/gentoo/ldmud/files/erq/srv.h new file mode 100644 index 0000000..0fcf130 --- /dev/null +++ b/config/gentoo/ldmud/files/erq/srv.h @@ -0,0 +1,32 @@ +/**************************************************************************** +** $Id: srv.h,v 1.1 2006/05/22 11:18:10 lynx Exp $ +** +** Definition of something or other +** +** Created : 979899 +** +** Copyright (C) 1997 by Troll Tech AS. All rights reserved. +** +****************************************************************************/ + +#ifndef SRV_H +#define SRV_H + +struct srvhost { + unsigned int pref; + struct srvhost * next; + unsigned int port; + unsigned int weight; + unsigned int rweight; + char name[1]; +}; + + +extern void freesrvhost ( struct srvhost * ); + +extern struct srvhost * getsrv( const char * domain, + const char * service, + const char * protocol ); + +#endif + diff --git a/config/gentoo/ldmud/files/psyced.settings b/config/gentoo/ldmud/files/psyced.settings new file mode 100644 index 0000000..79d6cb9 --- /dev/null +++ b/config/gentoo/ldmud/files/psyced.settings @@ -0,0 +1,311 @@ +#!/bin/sh +# +# settings/psyced version 4.0 +# +# $Id: psyced.settings,v 1.10 2007/05/08 00:52:01 lynx Exp $ +# +# LDMUD compilation settings for psyced, the LPC server and client +# implementation of the protocol for synchronous conferencing. +# Please execute this instead of ./configure before compilation. +# +# The latest version of this file is in the psyced-CVS and usually also +# at http://www.psyced.org/dist/config/psyced.settings +# The version in the LDMUD distribution may be slightly outdated. +# +# Documents and download on: http://www.psyced.org +# Information about PSYC: http://www.psyc.eu +# +# the PSYC implementation and library is designed in a way that it should +# easily co-host in a running LPMUD, it just needs a few patches to the +# master object and #includes in simul_efun. +# +# how to multiplex InterMUD and PSYC on the same udp port: +# PSYC UDP packets always start with ".\n". +# +# configure will strip this part from the script. + +cp $0 settings/psyced-current +exec ./configure --prefix=/opt/psyced --bindir=/opt/psyced/bin --libdir=/opt/psyced/world --libexec=/opt/psyced/run --with-setting=psyced-current $* +exit + +#-- PSYCLPC EXTENSIONS + +# compile json c library in, if available +enable_use_json=yes + +# compile expat xml & xmpp parser in, if available +enable_use_expat=yes + +# enable this if you want use http://about.psyc.eu/psyclpc#Authlocal +enable_use_authlocal=yes +# requires procfs with readable /proc/net/tcp +# +# some kernel configurations show wrong port numbers however, +# in that case the correlation from peer port to user id fails. +# there is however no risk of error (the peer ports are unprivileged +# whereas the 'wrong' ones are privileged ones. could be related to +# iptables or some other port mapping magic). + +#-- FILE PATHES + +with_master_name=drivers/ldmud/master/master +# unused really +with_swap_file=log/psyced.swap + +#-- NETWORKING & DATA BASES + +# cool tool that does external name server resolution and more +enable_erq=erq + +# the "sorry" message of the built-in ACL isn't flexible enough: +# it cannot output a properly formatted HTTP or PSYC message +# and why shouldn't this be done in LPC anyway? +enable_access_control=no + +# zlib compression, really +enable_use_mccp=yes + +# Enable support for TLS (Transport Layer Security). +# +# 'no': TLS support is not compiled it +# 'gnu': TLS support using GnuTLS is compiled in if found. +# 'ssl': TLS support using OpenSSL is compiled in if found. +# 'yes': TLS support using either OpenSSL or GnuTLS is compiled in if found. +# The configuration script looks first for OpenSSL, then GnuTLS. +# If both are available, OpenSSL is used. +# +# gnutls does not offer features we need +enable_use_tls=ssl + +# SQL storage not implemented. Maybe PSYC synchronization is good enough? +# see http://about.psyc.eu/storage for info. +enable_use_mysql=no +enable_use_pgsql=no +enable_use_sqlite=no + +# maximum permitted tcp connections +with_max_players=900 + +# The maximum number of ports to listen for connections on. +with_maxnumports=33 + +# size of the buffer for incoming data of each socket +with_size_socket_buffer=32768 + +# The maximum number of simultaneous connect() calls +with_max_net_connects=33 + +# how can i set all the other port numbers? +# still using the historic dirty command line hack? +with_portno=4404 + +# Maximum size of a socket send buffer. +with_set_buffer_size_max=262144 + +# the PSYC port +with_udp_port=4404 + +# Enable support for IPv6 (this should better be autodetected!!) +#enable_use_ipv6=no + +# Enable the use of pthreads for background socket IO. +enable_use_pthreads=no + +# If PThreads are used, this is the max amount of data held pending +# for writing. If the amount is exceeded, the oldest data blocks +# are discarded. +# If 0, any amount of data is allowed. +with_pthread_write_max_size=333333 + +#-- RUNTIME LIMITS +# +# currently we consider a psyced a friendly environment +# where no coders will abuse cpu time and other limits +# you may want to change this if you have many room coders + +# you can lower this value for better debugging +with_max_cost=9999999 +#with_catch_reserved_cost=2000 +#with_master_reserved_cost=512 +enable_dynamic_costs=no + +# Maximum function call depth for normal program execution +#with_max_user_trace=60 +# Maximum function call depth during error handling. +# It must be bigger than MAX_USER_TRACE (check at compile time). +#with_max_trace=65 +# Maximum number of bits in a bit string. +# The limit is more based on considerations of speed than memory consumption. +#with_max_bits=6144 +# Allowed number of ed commands per backend cycle (for faster file upload). +#with_allowed_ed_cmds=20 + +# disabled "mud" limits +with_max_array_size=0 +with_max_mapping_size=0 +with_max_mapping_keys=0 +with_read_file_max_size=0 +with_max_byte_transfer=0 +with_max_callouts=0 +# new, was =100000 +with_pthread_write_max_size=0 + +# Compiler stack size. This value affects the complexity the compiler can +# parse. +#with_compiler_stack_size=400 + +# Maximum number of local variables +#with_max_local=50 + +# Maximum size of an expanded preprocessor definition. +#with_defmax=65000 + +#-- MEMORY ALLOCATION + +# slabmalloc seems to interact badly with net/jabber +#with_malloc=sysmalloc +# but other mallocs don't even compile & run + +enable_malloc_sbrk=yes +enable_malloc_check=no +enable_malloc_trace=no +enable_malloc_sbrk_trace=no +#enable_malloc_lpc_trace=no, MALLOC_slaballoc + +# disabled, sort of +with_reserved_user_size=100000 + +# hash table sizes. we have many chat strings (like lastlogs), but few objects +# (max: 65536) +with_htable_size=32768 +with_otable_size=256 +# Size of the hash table for defines, reserved words, identifiers, and +# efun names. This should be several times smaller than HTABLE_SIZE. +#with_itable_size=256 +# Size of the apply cache, expressed in the bitsize of its indices. +# The number of entries is 2**cache_bits. +#with_apply_cache_bits=12 + +# Select whether compiled regular expressions shall be cached, and +# how big the cache shall be (max: 65536). +enable_rxcache_table=yes +with_rxcache_table=4096 + +#-- TIMER MECHANISMS + +# short resets, not strictly necessary but useful +with_time_to_reset=1000 +with_time_to_clean_up=9000 +#with_time_to_swap=1500 +#with_time_to_swap_variables=2500 +with_time_to_swap=0 +with_time_to_swap_variables=0 + +#-- MUD FEATURES + +# should work in "native" too, after a tweak or two +# however, psyced expects create() to get called instead of reset(0) +enable_compat_mode=yes +enable_strict_euids=no + +# The Input escape character. +# If this character is typed as first on the line, the normal input_to()s +# are bypassed. +#with_input_escape="!" + +# Define ALLOW_FILENAME_SPACES if the driver should accept space characters +# in filenames. If it is not defined, a hardcoded test for space characters +# is activated. +enable_filename_spaces=yes + +with_wizlist_file=no +enable_use_set_light=no +enable_use_set_is_wizard=no + +#-- LANGUAGE + +enable_use_parse_command=no +enable_use_process_string=no +enable_lpc_nosave=yes +# if turned to yes this causes errors +# in textdb and room history: +enable_share_variables=no +# keep .o files crossplatform +enable_use_system_crypt=no + +# Define this to enable LPC structs. +enable_use_structs=no + +# new inline closure and function syntax, see doc/LPC/inline-closures +# ... We don't want to use them as yet, but current +# 3.3.5xx doesn't compile properly without! :-( +enable_use_new_inlines=yes + +enable_use_alists=no +# we use it for system shout +enable_lpc_array_calls=yes +# only enables the ancient transfer() efun we don't use +enable_use_deprecated=no + +# Enable PCRE instead of traditional regexps +# 'no': use traditional regexps by default +# 'no-builtin': use traditional regexps by default, or the builtin PCRE +# package if PCRE is requested +# 'builtin': use PCRE package by default, using the builtin package +# 'yes': use the system's PCRE package if available, otherwise the +# builtin package +enable_use_pcre=yes + +#-- COMPILATION + +# The optimization level in the generated Makefile +# Settings: no, low, med, high +with_optimize=med + +#-- DEBUGGING + +# Enable basic run time sanity checks. This will use more time +# and space, but nevertheless you are strongly encouraged to keep +# it defined. +enable_debug=yes +# LPC compiler debug +#enable_yydebug=no + +# Disable inlining. Use this to debug obscure crashes, or for +# speed tests. +#enable_no_inlines=no + +# Activate debug prints in the telnet machine (lots of output). +#enable_debug_telnet=no + +# Activate allocation debug prints in the malloc module (lots of output). +# Supported by: MALLOC_smalloc, MALLOC_slaballoc +#enable_debug_malloc_allocs=no + +# The DEBUG level for the ERQ daemon: +# 0 : no debug output +# 1 : standard debug output +# 2 : verbose debug output +with_erq_debug=0 + +# If TRACE_CODE is enable, the driver keeps a log of TOTAL_TRACE_LENGTH +# most recently execute bytecode instructions. +enable_trace_code=yes +with_total_trace_length=1024 + +# Enable these for runtime statistics: +# COMM_STAT: number and size of outgoing packets +# APPLY_CACHE_STAT: number of hits and misses in the apply cache +enable_comm_stat=yes +enable_apply_cache_stat=no + +# Enable usage statistics of VM instructions. +enable_opcprof=no +enable_verbose_opcprof=no + +# Lars' hardcore debug features +#enable_check_object_stat=no +#enable_check_mapping_total=no +#enable_check_object_ref=no +#enable_check_object_gc_ref=no +#enable_dump_gc_refs=no diff --git a/pike/net.pmod/psyc.pmod/common.pike b/pike/net.pmod/psyc.pmod/common.pike new file mode 100644 index 0000000..d73ac1d --- /dev/null +++ b/pike/net.pmod/psyc.pmod/common.pike @@ -0,0 +1,3 @@ +import net.library; + +#include "../../../world/net/psyc/common.c" diff --git a/utility/latency.pike b/utility/latency.pike new file mode 100644 index 0000000..fe23e97 --- /dev/null +++ b/utility/latency.pike @@ -0,0 +1,35 @@ +# contributed by fippo + +Stdio.File socket; +int count; +array(int) start, stop; + +void logon(int success) { + socket->set_nonblocking(read_cb); + socket->write(".\n"); +} + +void read_cb(int id, string data) { + //werror("read_cb:\n%s\n---\n", data); + count++; + if (count == 2) { + start = System.gettimeofday()[..1]; + socket->write(":_target\tpsyc://localhost/~fippo\n\n_request_version\n.\n"); + } + if (count > 2) { + stop = System.gettimeofday()[..1]; + werror("latency is %d\n", (stop[0] - start[0])*1000000 + (stop[1] - start[1])); + if (count < 500) { + socket->write(":_target\tpsyc://localhost/\n\n_request_version\n.\n"); + start = stop; + } + } + +} + +int main() { + socket = Stdio.File(); + socket->async_connect("localhost", 4404, logon); + + return -1; +} diff --git a/world/drivers/ldmud/master/accept.c b/world/drivers/ldmud/master/accept.c new file mode 100644 index 0000000..0ac3534 --- /dev/null +++ b/world/drivers/ldmud/master/accept.c @@ -0,0 +1,440 @@ +// $Id: accept.c,v 1.119 2008/08/03 14:21:59 lynx Exp $ // vim:syntax=lpc:ts=8 +// +// this file contains the glue between LDMUD and psyced to +// connect the socket ports to appropriately named objects. +// +#include "/local/config.h" + +#ifdef Dmaster +# undef DEBUG +# define DEBUG Dmaster +#endif + +#include NET_PATH "include/net.h" +#include NET_PATH "include/services.h" +#include DRIVER_PATH "include/driver.h" +#include CONFIG_PATH "config.h" + +#include DRIVER_PATH "sys/tls.h" + +#ifndef ERR_TLS_NOT_DETECTED +# define ERR_TLS_NOT_DETECTED -31337 +# ifdef SPYC_PATH +# echo Warning: TLS autodetect is not enabled in driver. +# endif +#endif + +volatile int shutdown_in_progress = 0; + +// not part of the master/driver API.. this is called from the library +void notify_shutdown_first(int progress) { + P3(("%O notify_shutdown_first(%O)\n", ME, progress)) + shutdown_in_progress = progress; +} + +#ifndef UID2NICK +# ifdef _userid_nick_mapping +# define UID2NICK(uid) ([ _userid_nick_mapping ])[uid] +# endif +#endif + +/* + * This function is called every time a TCP connection is established. + * It dispatches the ports to the protocol implementations. + * input_to() can't be called from here. + * + * uid is only passed if USE_AUTHLOCAL is built into the driver. + */ +object connect(int uid, int port, string service) { + int peerport; + mixed arg, t; + + // now that's a bit of preprocessor magic you don't need to understand.. ;) + D2( if (uid) D("master:connected on port "+ query_mud_port() +" by uid " + + uid +"\n"); + else) { + D3(D("master:connected on port "+query_mud_port() + +" by "+query_ip_name()+"\n");) + } +#ifndef H_DEFAULT_PROMPT + set_prompt(""); +#endif + + if (shutdown_in_progress) { + PT(("shutdown_in_progress(%O): putting connection from %O on hold\n", + shutdown_in_progress, query_ip_name(ME))) + // put the connection on hold to avoid further reconnects + return clone_object(NET_PATH "utility/onhold"); + } + + // we dont want the telnet machine most of the time + // but disabling and re-enabling it for telnet doesn't work + switch(port || query_mud_port()) { + +#if HAS_PORT(PSYCS_PORT, PSYC_PATH) + case PSYCS_PORT: // inofficial & temporary +# if __EFUN_DEFINED__(tls_want_peer_certificate) + tls_want_peer_certificate(ME); +# endif + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); +#endif // fall thru +#if HAS_PORT(PSYC_PORT, PSYC_PATH) + case PSYC_PORT: +#endif +#if HAS_PORT(PSYC_PORT, PSYC_PATH) || HAS_PORT(PSYCS_PORT, PSYC_PATH) +# ifdef DRIVER_HAS_CALL_BY_REFERENCE + arg = ME; + query_ip_number(&arg); + // this assumes network byte order provided by driver + peerport = pointerp(arg) ? (arg[2]*256 + arg[3]) : 0; + if (peerport < 0) peerport = 65536 + peerport; + // no support for non-AF_INET nets yet + if (peerport == PSYC_SERVICE) peerport = 0; +# else + // as long as the object names don't collide, this is okay too + peerport = 65536 + random(9999999); +# endif +# ifdef DRIVER_HAS_RENAMED_CLONES + unless (service) service = "psyc"; + t = "S:"+ service + ":"+ query_ip_number(); + // tcp peerports cannot be connected to, so we use minus + if (peerport) t += ":-"+peerport; +# else + t = clone_object(PSYC_PATH "server"); +# endif + // the psyc backend distinguishes listen ports from peers using minus + D3(D(S("%O -> load(%O, %O)\n", t, query_ip_number(), -peerport));) + return t -> load(query_ip_number(), -peerport); +#endif + +#if HAS_PORT(SPYCS_PORT, SPYC_PATH) + case SPYCS_PORT: // inofficial & temporary +# if __EFUN_DEFINED__(tls_want_peer_certificate) + tls_want_peer_certificate(ME); +# endif + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); +#endif // fall thru +#if HAS_PORT(SPYC_PORT, SPYC_PATH) + case SPYC_PORT: +#endif +#if HAS_PORT(SPYC_PORT, SPYC_PATH) || HAS_PORT(SPYCS_PORT, SPYC_PATH) +# ifdef DRIVER_HAS_CALL_BY_REFERENCE + arg = ME; + query_ip_number(&arg); + // this assumes network byte order provided by driver + peerport = pointerp(arg) ? (arg[2]*256 + arg[3]) : 0; + if (peerport < 0) peerport = 65536 + peerport; + // no support for non-AF_INET nets yet + if (peerport == SPYC_SERVICE) peerport = 0; +# else + // as long as the object names don't collide, this is okay too + peerport = 65536 + random(9999999); +# endif +# ifdef DRIVER_HAS_RENAMED_CLONES + t = "S:spyc:"+query_ip_number(); + // tcp peerports cannot be connected to, so we use minus + if (peerport) t += ":-"+peerport; +# else + t = clone_object(SPYC_PATH "server"); +# endif + // the psyc backend distinguishes listen ports from peers using minus + D3(D(S("%O -> load(%O, %O)\n", t, query_ip_number(), -peerport));) + return t -> load(query_ip_number(), -peerport); +#endif + +#if HAS_PORT(POP3S_PORT, POP3_PATH) + case POP3S_PORT: + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); + return clone_object(POP3_PATH "server"); +#endif +#if HAS_PORT(POP3_PORT, POP3_PATH) + case POP3_PORT: + return clone_object(POP3_PATH "server"); +#endif + +#if HAS_PORT(SMTPS_PORT, NNTP_PATH) + case SMTPS_PORT: + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); + return clone_object(SMTP_PATH "server"); +#endif +#if HAS_PORT(SMTP_PORT, SMTP_PATH) + case SMTP_PORT: + return clone_object(SMTP_PATH "server"); +#endif + +// heldensagas little http app +#if HAS_PORT(SHT_PORT, SHT_PATH) + case SHT_PORT: + return clone_object(SHT_PATH "server"); +#endif + +#if HAS_PORT(NNTPS_PORT, NNTP_PATH) + case NNTPS_PORT: + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); + return clone_object(NNTP_PATH "server"); +#endif +#if HAS_PORT(NNTP_PORT, NNTP_PATH) + case NNTP_PORT: + return clone_object(NNTP_PATH "server"); +#endif + +#if HAS_PORT(JABBERS_PORT, JABBER_PATH) + case JABBERS_PORT: + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); + return clone_object(JABBER_PATH "server"); +#endif +#if HAS_PORT(JABBER_PORT, JABBER_PATH) + case JABBER_PORT: +# if __EFUN_DEFINED__(enable_telnet) + enable_telnet(0); // are you sure!??? +# endif + return clone_object(JABBER_PATH "server"); +#endif + +#if HAS_PORT(JABBER_S2S_PORT, JABBER_PATH) + case JABBER_S2S_PORT: +# ifdef DRIVER_HAS_CALL_BY_REFERENCE + arg = ME; + query_ip_number(&arg); + // this assumes network byte order provided by driver + peerport = pointerp(arg) ? (arg[2]*256 + arg[3]) : 0; + if (peerport < 0) peerport = 65536 + peerport; + if (peerport == JABBER_S2S_SERVICE) peerport = 0; +# else + // as long as the object names don't collide, this is okay too + peerport = 65536 + random(9999999); +# endif +# if __EFUN_DEFINED__(enable_telnet) + enable_telnet(0); +# endif + t = "S:xmpp:"+query_ip_number(); + // it's just an object name, but let's be consequent minus peerport + if (peerport) t += ":-"+peerport; +# ifdef _flag_log_sockets_XMPP + SIMUL_EFUN_FILE -> log_file("RAW_XMPP", "\n\n%O: %O -> load(%O, %O)", + ME, t, +# ifdef _flag_log_hosts + query_ip_number(), +# else + "?", +# endif + -peerport); +# endif + P3(("%O -> load(%O, %O)\n", t, query_ip_number(), -peerport)) + return t -> load(query_ip_number(), -peerport); +#endif +#if 0 //__EFUN_DEFINED__(enable_binary) + // work in progress + case 8888: + enable_binary(); + enable_telnet(0); + return clone_object(NET_PATH "socks/protocol"); + case 1935: + enable_binary(); + enable_telnet(0); + return clone_object(NET_PATH "rtmp/protocol"); +#endif +#if HAS_PORT(IRCS_PORT, IRC_PATH) + case IRCS_PORT: + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); + return clone_object(IRC_PATH "server"); +#endif +#if HAS_PORT(IRC_PORT, IRC_PATH) + case IRC_PORT: +# if 0 // __EFUN_DEFINED__(enable_telnet) + enable_telnet(0); // shouldn't harm.. but it does!!! +# endif + return clone_object(IRC_PATH "server"); +#endif + +#if HAS_PORT(APPLET_PORT, APPLET_PATH) + case APPLET_PORT: +# if __EFUN_DEFINED__(enable_telnet) + // enable_telnet(0); +# endif + return clone_object(APPLET_PATH "server"); +#endif + +#if HAS_PORT(TELNETS_PORT, TELNET_PATH) + case TELNETS_PORT: + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); + // we could do the UID2NICK thing here, too, but why should we? + // what do you need tls for on a localhost tcp link? + return clone_object(TELNET_PATH "server"); +#endif +#if HAS_PORT(TELNET_PORT, TELNET_PATH) + case TELNET_PORT: +// set_prompt("> "); + // we can't do the usual autodetect here, as telnet users + // don't send first and rather expect the server to prompt + // the correct way to do this: implement telnet nego for tls. bah! + t = clone_object(TELNET_PATH "server"); +# ifdef UID2NICK + if (uid && (arg = UID2NICK(uid))) { t -> sName(arg); } +# endif + return t; +#endif + +#if HAS_PORT(HTTPS_PORT, HTTP_PATH) + case HTTPS_PORT: + t = tls_init_connection(this_object()); + if (t < 0) { + D1( if (t != ERR_TLS_NOT_DETECTED) PP(( "TLS(%O) on %O: %O\n", + t, query_mud_port(), tls_error(t) )); ) +#if !HAS_PORT(HTTP_PORT, HTTP_PATH) + // if we have no http port, it may be intentional + return (object)0; +#endif + } + D2( else if (t > 0) PP(( "Setting up TLS connection in the background.\n" )); ) + D2( else PP(( "Oh yeah, I'm initializing an https session!\n" )); ) + return clone_object(HTTP_PATH "server"); +#endif + /* don't fall thru. allow for https: to be available without http: */ +#if HAS_PORT(HTTP_PORT, HTTP_PATH) + case HTTP_PORT: + return clone_object(HTTP_PATH "server"); +#endif + +#if HAS_PORT(MUDS_PORT, MUD_PATH) + case MUDS_PORT: + t = tls_init_connection(this_object()); + if (t < 0 && t != ERR_TLS_NOT_DETECTED) PP(( "TLS on %O: %O\n", + query_mud_port(), tls_error(t) )); + return clone_object(MUD_PATH "login"); +#endif +#if HAS_PORT(MUD_PORT, MUD_PATH) + default: + // if you want to multiplex psyced with an LPMUD game +// set_prompt("> "); + return clone_object(MUD_PATH "login"); +#endif + } + + PP(("Received connection on port %O which isn't configured.\n", + query_mud_port())); + return (object)0; +} + + +#ifdef DRIVER_HAS_RENAMED_CLONES +// named clones -lynx +object compile_object(string file) { + string path, name; + object rob; + +# ifdef PSYC_PATH + if (abbrev("S:psyc:", file)) { + rob = clone_object(PSYC_PATH "server"); + D2(if (rob) PP(("NAMED CLONE: %O => %s\n", rob, file));) + return rob; + } + if (abbrev("psyc:", file)) { + rob = clone_object(PSYC_PATH "active"); + D2(if (rob) PP(("NAMED CLONE: %O => %s\n", rob, file));) + return rob; + } +# endif +# ifdef SPYC_PATH + if (abbrev("S:spyc:", file)) { + rob = clone_object(SPYC_PATH "server"); + D2(if (rob) PP(("NAMED CLONE: %O => %s\n", rob, file));) + return rob; + } + if (abbrev("spyc:", file)) { + rob = clone_object(SPYC_PATH "active"); + D2(if (rob) PP(("NAMED CLONE: %O => %s\n", rob, file));) + return rob; + } +# endif +# ifdef HTTP_PATH + // match both http:/ and https:/ objects ;D + if (abbrev("http", file)) { + rob = clone_object(HTTP_PATH "fetch"); + if (rob) rob->fetch(file[..<3]); + return rob; + } + if (abbrev("xmlrpc:", file)) { + rob = clone_object(HTTP_PATH "xmlrpc"); + if (rob) rob->fetch("http://" + file[7..<3]); + return rob; + } +# endif + if (sscanf(file, "%s#%s.c", path, name) && name != "") { + unless (name = SIMUL_EFUN_FILE->legal_name(name)) + return (object)0; + rob = clone_object(path); + rob -> sName(name); + D2(if (rob) PP(("NAMED CLONE: %O becomes %s of %s\n", + rob, name, path));) + return rob; + } + if (sscanf(file, "place/%s.c", name) && name != "") { +#ifdef SANDBOX + string t; +#endif + unless (name = SIMUL_EFUN_FILE->legal_name(name)) + return (object)0; + +#ifdef SANDBOX + if (file_size(t = USER_PATH + name + ".c") != -1) { + rob = t -> sName(name); + D2(if (rob) PP(("USER PLACE loaded: %O becomes %O\n", rob, file));) + } else { +#endif +#ifdef _flag_disable_places_arbitrary + P2(("WARN: cloned places disabled by #define %O\n", file)) + return (object)0; +#else +#ifdef _path_archetype_place_default + rob = clone_object(_path_archetype_place_default); +#else + rob = clone_object(NET_PATH "place/default"); +#endif + rob -> sName(name); + D2(if (rob) PP(("PLACE CLONED: %O becomes %O\n", rob, file));) +#endif +#ifdef SANDBOX + } +#endif + return rob; + } + if (sscanf(file, "%s/text.c", path) && path != "") { + rob = clone_object(NET_PATH "text"); + rob -> sPath(path); + D2(if (rob) PP(("DB CLONED: %O becomes %s/text\n", rob, path));) + return rob; + } +# ifdef JABBER_PATH + if (abbrev("S:xmpp:", file)) { + rob = clone_object(JABBER_PATH "gateway"); + D2(if (rob) PP(("NAMED CLONE: %O => %s\n", rob, file));) + return rob; + } + if (abbrev("C:xmpp:", file)) { + rob = clone_object(JABBER_PATH "active"); + D2(if (rob) PP(("NAMED CLONE: %O => %s\n", rob, file));) + return rob; + } +# endif + P3(("WARN: could not create %O\n", file)) + return (object)0; +} +#endif + diff --git a/world/net/include/trust.h b/world/net/include/trust.h new file mode 100644 index 0000000..8f69daa --- /dev/null +++ b/world/net/include/trust.h @@ -0,0 +1,32 @@ +// $Id: trust.h,v 1.2 2008/07/26 13:18:06 lynx Exp $ // vim:syntax=lpc:ts=8 +// +#ifndef _INCLUDE_TRUST_H +#define _INCLUDE_TRUST_H + +// all of these need new names to fit the tuning masterplan +#ifndef MAX_EXPOSE_GROUPS +# define MAX_EXPOSE_GROUPS 4 // just the top four ;) +#endif +#ifndef DEFAULT_EXPOSE_GROUPS // change this value in your local.h +# define DEFAULT_EXPOSE_GROUPS 4 // if you want groups to be exposed by +#endif // default like on irc +#ifndef MAX_EXPOSE_FRIENDS +# define MAX_EXPOSE_FRIENDS 8 // just the top eight ;) +#endif +#ifndef DEFAULT_EXPOSE_FRIENDS +# define DEFAULT_EXPOSE_FRIENDS 4 // do not expose yet, until we have + // profiles worth exposing for +#endif +#define TRUST_OVER_NOTIFY 3 // how much /trust counts more + // than notify. the normal value for + // a notify friendship is 8. if a + // medium trust is equivalent to + // that, 3 needs to be added to + // trust 5 to reach notify 8. +#define TRUST_MYSELF (9 + TRUST_OVER_NOTIFY) + // maximum trust +#ifndef EXPOSE_THRESHOLD +# define EXPOSE_THRESHOLD TRUST_MYSELF // at least show it to myself +#endif + +#endif diff --git a/world/net/spyc/dispatch.i b/world/net/spyc/dispatch.i new file mode 100644 index 0000000..fc7eb3c --- /dev/null +++ b/world/net/spyc/dispatch.i @@ -0,0 +1,239 @@ +// included by TCP circuit *and* UDP daemon // vim:syntax=lpc + +void dispatch(mixed header_vars, mixed varops, mixed method, mixed body) { + string vname; + mixed vop; // value operation + string t; + mapping vars; + string family; + int glyph; + + // check that method is a valid keyword + if (method && !legal_keyword(method)) { + DISPATCHERROR("non legal method"); + } +#ifdef PSYC_TCP + // copy() + occasional double modifier ops should be more + // efficient than merge at every packet --lynX + // no one cares about "efficiency" here. please proof your + // bold statements with benchmarks anyway + vars = header_vars + instate; +#else + vars = header_vars; +#endif + + // FIXME: this can happen earlier, e.g. in parse.c after + // process_header + // check _source/_context + // this check can be skipped if _source and _context are empty + if ((t = vars["_context"] || vars["_source"])) { + array(mixed) u; + unless (u = parse_uniform(t)) { + DISPATCHERROR("logical source is not an uniform\n") + } +#ifdef PSYC_TCP + unless (qAuthenticated(NAMEPREP(u[UHost]))) { + DISPATCHERROR("non-authenticated host\n") + } +#else + // TODO? +#endif + } + // check that _target is hosted by us + // this check can be skipped if _target is not set + if ((t = vars["_target"])) { + array(mixed) u; + unless (u = parse_uniform(t)) { + DISPATCHERROR("target is not an uniform\n") + } + // FIXME relaying support here? + if (!is_localhost(u[UHost])) { + DISPATCHERROR("target is not configured on this server\n") + } + } + // FIXME: i dont like this block... maybe we decode each variable + // when setting it? + // that would also fit with 0 as varname deletion + // below + foreach(vop : varops) { + vname = vop[0]; + + // psyc type conversion implementation ( http://about.psyc.eu/Type ) + // this does not support register_type() yet, but it is feasible + PSYC_TRY(vname) { + case "_uniform": + case "_page": + case "_entity": + // TODO: check legal uniform + break; + case "_nick": + // TODO: check legal nick + break; + case "_degree": + // only honour the first digit + if (strlen(vop[2]) && vop[2][0] >= '0' && vop[2][0] <= '9') + vop[2] = vop[2][0] - '0'; + else { + PT(("type parser _degree: could not handle value %O\n", + vop[2])) + vop[2] = 0; + } + break; + case "_time": + case "_amount": + vop[2] = to_int(vop[2]); + break; + case "_list": + mixed plist = list_parse(vop[2]); + if (plist == -1) { + DISPATCHERROR("could not parse list"); + } + vop[2] = plist; + break; + PSYC_SLICE_AND_REPEAT + } + } + + // FIXME deliver packet + // this should be a separate function + PT(("SPYC vars is %O\n", vars)) + PT(("SPYC method %O\nbody %O\n", method, body)) + // delivery rules as usual, but + if (vars["_context"]) { + mixed context; + mixed context_state; + mixed source, target; + + if (vars["_source"]) { + P0(("invalid _context %O with _source %O\n", + context, vars["_source"])) + DISPATCHERROR("invalid usage of context with _source"); + } + + context = find_context(vars["_context"]); + if (!objectp(context)) { + P0(("context %O not found?!\n", vars["_context"])) + return; + } + context_state = context->get_state(); + + // apply varops to context state + foreach(vop : varops) { + vname = vop[0]; + if (!legal_keyword(vname) || abbrev("_INTERNAL", vname)) { + DISPATCHERROR("illegal varname in psyc") + } + + switch(vop[1]) { // the glyph + case C_GLYPH_MODIFIER_SET: + vars[vname] = vop[2]; + break; + case C_GLYPH_MODIFIER_ASSIGN: + vars[vname] = context_state[vname] = vop[2]; + break; + case C_GLYPH_MODIFIER_AUGMENT: + if (!abbrev("_list", vname)) { + DISPATCHERROR("psyc modifier + with non-list arg") + } + // FIXME: duplicates? + context_state[vname] += vop[2]; + PT(("current state is %O, augment %O\n", context_state[vname], vop[2])) + break; + case C_GLYPH_MODIFIER_DIMINISH: + if (!abbrev("_list", vname)) { + DISPATCHERROR("psyc modifier + with non-list arg") + } + PT(("current state is %O, diminish %O\n", context_state[vname], vop[2])) + foreach(mixed item : vop[2]) + context_state[vname] -= ({ item }); + PT(("after dim: %O\n", context_state[vname])) + break; + case C_GLYPH_MODIFIER_QUERY: + DISPATCHERROR("psyc modifier ? not implemented") + break; + } + } + vars = vars + context_state; + // FIXME: is it legal to do this if this has _target? + // there should be no mods then anyway + context->commit_state(context_state); + + if (vars["_target"]) { + // FIXME: delivery copycat from below + // beware: source is set to 0 here as it may not be present + target = find_psyc_object(parse_uniform(vars["_target"])); + PT(("target is %O\n", target)) + // FIXME: net/entity can not yet deal with 0 method + // + if (objectp(context)) { + context->msg(0, method || "", body, vars, 0, target); + } else { + // FIXME: proper croak back to sender here + P0(("context %O for unicast to %O not found???\n", target)) + } + } else { + if (vars["_source_relay"]) { + mixed localrelay; + if ((localrelay = psyc_object(vars["_source_relay"]))) { + P0(("local relay %O\n", localrelay)) + vars["_source_relay"] = localrelay; + } else { // NORMALIZE UNIFORM + vars["_source_relay"] = lower_case(vars["_source_relay"]); + } + } + if (objectp(context)) { + // do we need more local object detection here? + context -> castmsg(source, method || "", body, vars); + } else { + // empty contexts are not bad currently + // in the current implementation it only means that no one + // interested in that context is online right now + // FIXME: lines above are about the old stuff where we did + // not have context state + } + } + } else { + if (!vars["_target"] && !vars["_source"]) { +#ifdef PSYC_TCP + circuit_msg(method, vars, body); +#else + P1(("Ignoring a rootMsg from UDP: %O,%O,%O\n", method, vars, body)) +#endif + } else { + string source; + mixed target; + if (!vars["_source"]) { + // FIXME: where to set netloc in active + if (!netloc) { // set in sender after _request_features + // FIXME: this is wrong + DISPATCHERROR("Did you forget to request circuit features?"); + } + source = netloc; + } else { + // FIXME: a macro NORMALIZE_UNIFORM that may do lower_case please + // not a simple lower_case + source = lower_case(vars["_source"]); + } + // source was checked either via x509 or dns before + // so it is 'safe' to do this + register_target(source); + + // deliver FIXME same code above + if (!vars["_target"]) { + target = find_object(NET_PATH "root"); + } else { + target = find_psyc_object(parse_uniform(vars["_target"])); + } + PT(("target is %O\n", target)) + // FIXME: net/entity can not yet deal with 0 method + if (objectp(target)) + target->msg(source, method || "", body, vars); + else { + // FIXME: proper croak back to sender here + P0(("target %O not found???\n", target)) + } + } + } + ::dispatch(header_vars, varops, method, body); +} +