summaryrefslogtreecommitdiffstats
path: root/xpa/xpans.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2017-10-26 16:45:12 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2017-10-26 16:45:12 (GMT)
commitdd1df8d8538f275b17e0d9d50dca19f4d44595fd (patch)
tree624bc07023b33668147eaa9097279c6fc92b4e48 /xpa/xpans.c
parent79d64f400391ce81b4eda73977cb40099256b348 (diff)
parent6054f36bb658916bd231b9002efb94444e2871c8 (diff)
downloadblt-dd1df8d8538f275b17e0d9d50dca19f4d44595fd.zip
blt-dd1df8d8538f275b17e0d9d50dca19f4d44595fd.tar.gz
blt-dd1df8d8538f275b17e0d9d50dca19f4d44595fd.tar.bz2
Merge commit '6054f36bb658916bd231b9002efb94444e2871c8' as 'xpa'
Diffstat (limited to 'xpa/xpans.c')
-rw-r--r--xpa/xpans.c1935
1 files changed, 1935 insertions, 0 deletions
diff --git a/xpa/xpans.c b/xpa/xpans.c
new file mode 100644
index 0000000..af727aa
--- /dev/null
+++ b/xpa/xpans.c
@@ -0,0 +1,1935 @@
+/*
+ * Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory
+ */
+
+#include <xpap.h>
+
+#if HAVE_LIBPTHREAD
+#include <pthread.h>
+static pthread_mutex_t xpans_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+#define MAX_ERRORS 5
+
+static int doproxy=0;
+static unsigned int localhost_ip=0;
+
+extern char *optarg;
+extern int optind;
+
+typedef struct entrec{
+ struct entrec *next;
+ char *method;
+ char *xclass;
+ char *name;
+ char *type;
+ char *user;
+ char *info;
+} *Entry, EntryRec;
+
+typedef struct reqrec{
+ struct reqrec *next;
+ int sock;
+ unsigned int ip;
+ int port;
+ Entry entry;
+} *Req, ReqRec;
+
+static char *helpbuf = "xpans commands:\nhelp\t\t\t\t# print this help message\nlist\t\t\t\t# list all entries\nlookup class:name type user\t# lookup entries of this type and user\n";
+
+static int keepalive=0;
+static int ksec=0;
+static int mtype = 0;
+static int nentry = 0;
+static int exconn=0;
+static int sock=-1;
+static char *logfile=NULL;
+static time_t lastt=(time_t)0;
+static time_t curt=(time_t)0;
+static FILE *securefp=NULL;
+static Req reqhead=NULL;
+
+static int LookupReq _PRx((Req xreq, char *lbuf, int flag));
+static int ListReq _PRx((Req xreq, int flag));
+static void HelpReq _PRx((Req xreq, int flag));
+#ifdef __STDC__
+static void SecureLog(char *format, ...);
+#else
+static void SecureLog();
+#endif
+
+#if HAVE_LIBPTHREAD
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: doxpaloop
+ *
+ * Purpose: start up XPAMainLoop in another thread
+ *
+ * Returns: NONE
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+void *doxpaloop(void *arg)
+#else
+void *doxpaloop(arg)
+ void *arg;
+#endif
+{
+ XPAMainLoop();
+ return (void *)NULL;
+}
+#endif
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: receive_proxy
+ *
+ * Purpose: receive callback for a proxy request
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+receive_proxy (void *client_data, void *call_data, char *paramlist,
+ char *buf, int len)
+#else
+int receive_proxy(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char *buf;
+ int len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ char *xtemplate;
+ char *mode;
+ char *err;
+ char *s;
+ char xmode[SZ_LINE];
+ char tbuf[SZ_LINE];
+ int fd=0;
+ int pfd=-1;
+ int got=0;
+ Req req=NULL;
+
+ xtemplate = xpa->comm->target;
+ mode = xpa->comm->info;
+ fd = xpa->comm->datafd;
+
+ /* get proxy fd and associated xpans request struct */
+ if( mode ){
+ strcpy(xmode, mode);
+ if( keyword(xmode, "nsproxy", tbuf, SZ_LINE) ){
+ pfd = strtol(tbuf, &s, 0);
+ if( s != tbuf ){
+ for(req=reqhead; req!=NULL; req=req->next){
+ if( req->sock == pfd ){
+ break;
+ }
+ }
+ }
+ }
+ }
+ if( pfd < 0 ){
+ snprintf(tbuf, SZ_LINE, "invalid or missing proxy fd");
+ XPAError(xpa, tbuf);
+ return(-1);
+ }
+ else if( req == NULL ){
+ snprintf(tbuf, SZ_LINE, "could not find xpans fd for proxy fd %d", pfd);
+ XPAError(xpa, tbuf);
+ return(-1);
+ }
+
+ FPRINTF((stderr, "%sreceive_proxy: fd=%d xtmpl=%s mode=%s paramlist=%s\n",
+ _sp, xpa->comm->cmdfd, xtemplate, mode,
+ paramlist?paramlist:"NONE"));
+
+ if( XPASetFd(xpa, xtemplate, paramlist, mode, fd, NULL, &err, 1) ){
+ /* display errors and free up strings */
+ if( err != NULL ){
+ XPAError(xpa, err);
+ xfree(err);
+ got = -1;
+ }
+ }
+ else{
+ got = -1;
+ }
+
+ FPRINTF((stderr, "%sreceive_proxy done\n", _sp));
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: send_proxy
+ *
+ * Purpose: send callback for a proxy request
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+send_proxy (void *client_data, void *call_data, char *paramlist,
+ char **buf, size_t *len)
+#else
+static int send_proxy(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char **buf;
+ size_t *len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ char *xtemplate;
+ char *mode;
+ char *err;
+ char *s;
+ char xmode[SZ_LINE];
+ char tbuf[SZ_LINE];
+ int fd=0;
+ int pfd=-1;
+ int got=0;
+ Req req=NULL;
+
+ xtemplate = xpa->comm->target;
+ mode = xpa->comm->info;
+ fd = xpa->comm->datafd;
+
+ /* get proxy fd and associated xpans request struct */
+ if( mode ){
+ strcpy(xmode, mode);
+ if( keyword(xmode, "nsproxy", tbuf, SZ_LINE) ){
+ pfd = strtol(tbuf, &s, 0);
+ if( s != tbuf ){
+ for(req=reqhead; req!=NULL; req=req->next){
+ if( req->sock == pfd ){
+ break;
+ }
+ }
+ }
+ }
+ }
+ if( pfd < 0 ){
+ snprintf(tbuf, SZ_LINE, "invalid or missing proxy fd");
+ XPAError(xpa, tbuf);
+ return(-1);
+ }
+ else if( req == NULL ){
+ snprintf(tbuf, SZ_LINE, "could not find xpans fd for proxy fd %d", pfd);
+ XPAError(xpa, tbuf);
+ return(-1);
+ }
+
+ FPRINTF((stderr, "%ssend_proxy: fd=%d xtemplate=%s mode=%s paramlist=%s\n",
+ _sp, xpa->comm->cmdfd, xtemplate, mode, paramlist?paramlist:"NONE"));
+
+ if( XPAGetFd(xpa, xtemplate, paramlist, mode, &fd, NULL, &err, 1) ){
+ /* display errors and free up strings */
+ if( err != NULL ){
+ XPAError(xpa, err);
+ xfree(err);
+ got = -1;
+ }
+ }
+ else{
+ got = -1;
+ }
+
+ FPRINTF((stderr, "%ssend_proxy done\n", _sp));
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: receive_cb
+ *
+ * Purpose: receive callback for XPA access point
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+receive_cb (void *client_data, void *call_data, char *paramlist,
+ char *buf, size_t len)
+#else
+int receive_cb(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char *buf;
+ size_t len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ char tbuf[SZ_LINE];
+
+ /* if target is not xpans, we have a proxy request */
+ snprintf(tbuf, SZ_LINE, "%s:%s", XPANS_CLASS, XPANS_NAME);
+ if( strcmp(xpa->comm->target, tbuf) ){
+ if( doproxy )
+ return(receive_proxy(client_data, call_data, paramlist, buf, len));
+ else{
+ XPAError(xpa, "proxy requests not enabled in this xpans");
+ return(-1);
+ }
+ }
+
+ /* nothing to do for normal receive callback */
+ XPAError(xpa, "no receive function defined for xpans");
+ return(-1);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: send_cb
+ *
+ * Purpose: send callback for XPA access point
+ *
+ * Returns: 0 for success, -1 for failure
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+send_cb (void *client_data, void *call_data, char *paramlist,
+ char **buf, size_t *len)
+#else
+static int send_cb(client_data, call_data, paramlist, buf, len)
+ void *client_data;
+ void *call_data;
+ char *paramlist;
+ char **buf;
+ size_t *len;
+#endif
+{
+ XPA xpa = (XPA)call_data;
+ Req req;
+ char tbuf[SZ_LINE];
+ char ebuf[SZ_LINE];
+ char tmpl[SZ_LINE];
+ char type[SZ_LINE];
+ char users[SZ_LINE];
+ int got=0;
+ int wp=0;
+
+ /* if target is not xpans, we have a proxy request */
+ snprintf(tbuf, SZ_LINE, "%s:%s", XPANS_CLASS, XPANS_NAME);
+ if( strcmp(xpa->comm->target, tbuf) ){
+ if( doproxy )
+ return(send_proxy(client_data, call_data, paramlist, buf, len));
+ else{
+ XPAError(xpa, "proxy requests not enabled in this xpans");
+ return(-1);
+ }
+ }
+
+ if( paramlist && *paramlist )
+ SecureLog("xpaget from host %x:%d (%s): %s",
+ xpa->comm->cmdip, xpa->comm->cmdport,
+ getiphost(xpa->comm->cmdip),
+ (paramlist && *paramlist)?paramlist:"<no params>");
+ else
+ SecureLog("xpaget from host %x:%d (%s)",
+ xpa->comm->cmdip, xpa->comm->cmdport,
+ getiphost(xpa->comm->cmdip));
+ if( (req = (Req)xcalloc(1, sizeof(ReqRec))) == NULL )
+ return(-1);
+ if( xpa_datafd(xpa) >= 0 ){
+ req->sock = xpa_datafd(xpa);
+ }
+
+#if HAVE_LIBPTHREAD
+ /* lock the mutex before processing a request */
+ if( doproxy >= 2 ) pthread_mutex_lock(&xpans_mutex);
+#endif
+
+ /* execute the appropriate routine */
+ if( paramlist && *paramlist && word(paramlist, tbuf, &wp) ){
+ if( !strcmp(tbuf, "list") ){
+ ListReq(req, 0);
+ }
+ else if( !strcmp(tbuf, "help") ){
+ HelpReq(req, 0);
+ }
+ else if( !strcmp(tbuf, "lookup") ){
+ if( word(paramlist, tmpl, &wp ) ){
+ /* look for type */
+ if( !word(paramlist, type, &wp ) )
+ strcpy(type, XPA_ACLS);
+ /* look for users */
+ if( !word(paramlist, users, &wp ) )
+ strcpy(users, "*");
+ snprintf(tbuf, SZ_LINE, "%s %s %s", tmpl, type, users);
+ LookupReq(req, tbuf, 0);
+ }
+ else{
+ strcpy(ebuf,
+ "XPA$ERROR 'lookup' requires class:name [type] [user]\n");
+ XPAPuts(NULL, req->sock, ebuf, XPALongTimeout());
+ got = -1;
+ }
+ }
+ else{
+ snprintf(ebuf, SZ_LINE, "XPA$ERROR unknown command '%s'\n", tbuf);
+ XPAPuts(NULL, req->sock, ebuf, XPALongTimeout());
+ got = -1;
+ }
+ }
+ else {
+ ListReq(req, 0);
+ }
+
+#if HAVE_LIBPTHREAD
+ /* unlock the mutex */
+ if( doproxy >= 2 ) pthread_mutex_unlock(&xpans_mutex);
+#endif
+
+ /* clean up */
+ if( req ) xfree(req);
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: Log
+ *
+ * Purpose: write all names to a backup log
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+Log (void)
+#else
+static void Log()
+#endif
+{
+ FILE *fp;
+ Req req;
+ Entry entry;
+
+ if( !logfile )
+ return;
+ if( !strcasecmp(logfile, "stdout") )
+ fp = stdout;
+ else if( (fp=fopen(logfile, "w")) == NULL )
+ return;
+ for(req=reqhead; req!=NULL; req=req->next){
+ for(entry=req->entry; entry!=NULL; entry=entry->next){
+ fprintf(fp, "# add %s %s %s %s %s\n",
+ entry->method, entry->xclass,
+ entry->name, entry->type, entry->user);
+ /* last one */
+ if( entry->next == NULL )
+ fprintf(fp, "xpaset -p %s -nsconnect\n", entry->method);
+ }
+ }
+ if( fp != stdout )
+ fclose(fp);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: SecureLog
+ *
+ * Purpose: write security info to a backup log
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef __STDC__
+void SecureLog(char *format, ...)
+{
+ char sbuf[SZ_LINE];
+ time_t t;
+ va_list args;
+ va_start(args, format);
+#else
+void SecureLog(va_alist) va_dcl
+{
+ char *format;
+ char sbuf[SZ_LINE];
+ time_t t;
+ va_list args;
+
+ va_start(args);
+ format = va_arg(args, char *);
+#endif
+ if( securefp == NULL )
+ return;
+ t = time(NULL);
+ vsnprintf(sbuf, SZ_LINE, format, args);
+ fprintf(securefp, "%s", sbuf);
+ fprintf(securefp, "\t%s", ctime(&t));
+ fflush(securefp);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: SplitArg
+ *
+ * Purpose: split the specified argument by changing a ":" to a space
+ * splitting is done in place
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+SplitArg (char *buf, int arg)
+#else
+static void SplitArg(buf, arg)
+ char *buf;
+ int arg;
+#endif
+{
+ int i;
+ char *s;
+
+ /* point to beginning of buffer */
+ s = buf;
+ /* skip over previous args */
+ for(i=0; i<arg; i++){
+ /* skip up to white space */
+ while( *s && !isspace((int)*s) )
+ s++;
+ /* skip over white space to next arg */
+ while( *s && isspace((int)*s) )
+ s++;
+ }
+ /* we now are pointing at the arg in question.
+ look for a ':' (up to next white space) and change to space */
+ while( *s && !isspace((int)*s) ){
+ if( *s == ':' ){
+ *s = ' ';
+ break;
+ }
+ else
+ s++;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: NewEntry
+ *
+ * Purpose: allocate a new XPA entry
+ *
+ * Returns: entry struct
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+NewEntry (Req req,
+ char *method, char *xclass, char *name,
+ char *type, char *user, char *info)
+#else
+static int NewEntry(req, method, xclass, name, type, user, info)
+ Req req;
+ char *method;
+ char *xclass;
+ char *name;
+ char *type;
+ char *user;
+ char *info;
+#endif
+{
+ Entry entry, cur;
+ Req xreq;
+
+ /* don't duplicate with any other entry */
+ for(xreq=reqhead; xreq!=NULL; xreq=xreq->next){
+ for(entry=xreq->entry; entry!=NULL; entry=entry->next){
+ if( !strcmp(entry->method, method) &&
+ !strcmp(entry->xclass, xclass) &&
+ !strcmp(entry->name, name) &&
+ !strcmp(entry->type, type) &&
+ !strcmp(entry->user, user) &&
+ !strcmp(entry->info, info) )
+ return(1);
+ }
+ }
+
+ /* allocate new entry */
+ if( (entry = (Entry)xcalloc(1, sizeof(EntryRec))) == NULL )
+ return(-1);
+
+ /* fill in the blanks */
+ entry->xclass = xstrdup(xclass);
+ entry->name = xstrdup(name);
+ entry->method = xstrdup(method);
+ entry->type = xstrdup(type);
+ entry->user = xstrdup(user);
+ entry->info = xstrdup(info);
+
+ FPRINTF((stderr, "%sNewEntry: %s %s %s %s %s %s\n", _sp,
+ xclass, name, method, type, user, info));
+
+ /* add this to end of the list */
+ if( req->entry == NULL ){
+ req->entry = entry;
+ }
+ else{
+ for(cur=req->entry; cur->next!=NULL; cur=cur->next)
+ ;
+ cur->next = entry;
+ }
+
+ /* inc the total number of entries */
+ nentry++;
+
+ /* return the news */
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: FreeEntry
+ *
+ * Purpose: free up an XPA entry
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+FreeEntry (Req req, Entry entry)
+#else
+static void FreeEntry(req, entry)
+ Req req;
+ Entry entry;
+#endif
+{
+ Entry cur;
+
+ /* remove this entry from the list so it can't be found */
+ if( entry == req->entry ){
+ req->entry = req->entry->next;
+ }
+ else{
+ for(cur=req->entry; cur!=NULL; cur=cur->next){
+ if( entry == cur->next ){
+ cur->next = entry->next;
+ break;
+ }
+ }
+ }
+
+ FPRINTF((stderr, "%sFreeEntry: %s %s\n", _sp, entry->xclass, entry->name));
+
+ /* now free this struct */
+ if( entry->method )
+ xfree(entry->method);
+ if( entry->xclass )
+ xfree(entry->xclass);
+ if( entry->name )
+ xfree(entry->name);
+ if( entry->type )
+ xfree(entry->type);
+ if( entry->user )
+ xfree(entry->user);
+ if( entry->info )
+ xfree(entry->info);
+ xfree(entry);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: DelEntry
+ *
+ * Purpose: Delete an XPA entry
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+DelEntry (Req req, char *method)
+#else
+static int DelEntry(req, method)
+ Req req;
+ char *method;
+#endif
+{
+ Entry cur, tcur;
+ int got=-1;
+
+ for(cur=req->entry; cur!=NULL; ){
+ tcur = cur->next;
+ if( ((method == NULL) || (*method == '\0')) ||
+ (!strcmp(method, "@") && (*cur->method == '@')) ||
+ !strcmp(cur->method, method) ){
+ FreeEntry(req, cur);
+ got = 0;
+ }
+ cur = tcur;
+ }
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: FirewallEntry
+ *
+ * Purpose: Correct the method to take a firewall into account
+ * we do this by taking the ip from the socket packet instead
+ * of the specified ip, if they differ
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+FirewallEntry (Req req, char *method)
+#else
+static void FirewallEntry(req, method)
+ Req req;
+ char *method;
+#endif
+{
+ unsigned int ip;
+ unsigned short port;
+
+ if( mtype != XPA_INET )
+ return;
+ if( XPAParseIpPort(method, &ip, &port) ){
+ if( (ip != req->ip) && (req->ip != localhost_ip) ){
+ SecureLog("firewall %d: changing ip from %x to %x",
+ req->sock, ip, req->ip);
+ snprintf(method, SZ_LINE, "%x:%d", req->ip, port);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: NewReq
+ *
+ * Purpose: allocate a new XPA request struct
+ *
+ * Returns: Req struct
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static Req
+NewReq (int sock, unsigned int ip, int port)
+#else
+static Req NewReq(sock, ip, port)
+ int sock;
+ unsigned int ip;
+ int port;
+#endif
+{
+ Req req;
+ Req cur;
+
+ if( (req = (Req)xcalloc(1, sizeof(ReqRec))) == NULL )
+ return(NULL);
+
+ /* fill in the blanks */
+ req->sock = sock;
+ req->ip = ip;
+ req->port = port;
+
+ /* add this to end of the list */
+ if( reqhead == NULL ){
+ reqhead = req;
+ }
+ else{
+ for(cur=reqhead; cur->next!=NULL; cur=cur->next)
+ ;
+ cur->next = req;
+ }
+ /* return the news */
+ return(req);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: FreeReq
+ *
+ * Purpose: free up an XPA request entry
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+FreeReq (Req req)
+#else
+static void FreeReq(req)
+ Req req;
+#endif
+{
+ Req cur;
+ /* remove this entry from the list so it can't be found */
+ if( req == reqhead ){
+ reqhead = req->next;
+ }
+ else{
+ for(cur=reqhead; cur!=NULL; cur=cur->next){
+ if( req == cur->next ){
+ cur->next = req->next;
+ break;
+ }
+ }
+ }
+ /* close the communication channel */
+ close(req->sock);
+ /* now free this struct */
+ xfree(req);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: AddReq
+ *
+ * Purpose: add an XPA entry to the list
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+AddReq (Req req, char *lbuf, int flag)
+#else
+static void AddReq(req, lbuf, flag)
+ Req req;
+ char *lbuf;
+ int flag;
+#endif
+{
+ int got;
+ char xclass[SZ_LINE];
+ char name[SZ_LINE];
+ char method[SZ_LINE];
+ char omethod[SZ_LINE];
+ char type[SZ_LINE];
+ char user[SZ_LINE];
+ char tbuf[SZ_LINE];
+
+ SplitArg(lbuf, 1);
+ got = sscanf(lbuf, "%s %s %s %s %s", method, xclass, name, type, user);
+ if( got == 5 ){
+ /* fix method if we can determine its been through a firewall */
+ strcpy(omethod, method);
+ FirewallEntry(req, method);
+ if( !strcmp(omethod, method) )
+ strcpy(tbuf, method);
+ else
+ snprintf(tbuf, SZ_LINE, "%s,%s", method, omethod);
+ /* add the new entry */
+ got = NewEntry(req, tbuf, xclass, name, type, user, XPA_DEF_CLIENT_INFO);
+ Log();
+ if( flag ){
+ switch(got){
+ case -1:
+ XPAPuts(NULL, req->sock, "XPA$ERROR could not add entry\n",
+ XPALongTimeout());
+ break;
+ case 0:
+ XPAPuts(NULL, req->sock, "XPA$OK\n", XPALongTimeout());
+ break;
+ case 1:
+ XPAPuts(NULL, req->sock, "XPA$EXISTS entry already exists\n",
+ XPALongTimeout());
+ break;
+ default:
+ XPAPuts(NULL, req->sock, "XPA$ERROR could not add entry\n",
+ XPALongTimeout());
+ break;
+ }
+ }
+ }
+ else{
+ strcpy(tbuf,
+ "XPA$ERROR 'add' requires 4 args: ip:port class:name type user\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: AddProxyReq
+ *
+ * Purpose: add an XPA proxy entry to the list
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+AddProxyReq (Req req, char *lbuf, int flag)
+#else
+static void AddProxyReq(req, lbuf, flag)
+ Req req;
+ char *lbuf;
+ int flag;
+#endif
+{
+ int got=0;
+ char xclass[SZ_LINE];
+ char name[SZ_LINE];
+ char method[SZ_LINE];
+ char omethod[SZ_LINE];
+ char type[SZ_LINE];
+ char user[SZ_LINE];
+ char info[SZ_LINE];
+ char tbuf[SZ_LINE];
+
+ /* make sure we are accepting proxy requests */
+ if( !doproxy ){
+ strcpy(tbuf,
+ "XPA$ERROR: proxy requests not enabled in this xpans\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ return;
+ }
+
+ SplitArg(lbuf, 1);
+ got = sscanf(lbuf, "%s %s %s %s %s", method, xclass, name, type, user);
+ if( got == 5 ){
+ if( XPAMethod(method) == XPA_INET ){
+ /* fix method if we can determine its been through a firewall */
+ strcpy(omethod, method);
+ FirewallEntry(req, method);
+ if( !strcmp(omethod, method) )
+ snprintf(tbuf, SZ_LINE, "@%s", method);
+ else
+ snprintf(tbuf, SZ_LINE, "@%s,%s", method, omethod);
+ }
+ else{
+ strcpy(tbuf,
+ "XPA$ERROR 'proxy' requires INET method\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ return;
+ }
+ /* save info */
+ snprintf(info, SZ_LINE, "nsproxy=%d,ns=(%s,%s,%s,%s),dofork=true",
+ req->sock, xclass, name, method, omethod);
+ /* add the new entry */
+ got = NewEntry(req, tbuf, xclass, name, type, user, info);
+ Log();
+ if( flag ){
+ switch(got){
+ case -1:
+ XPAPuts(NULL, req->sock, "XPA$ERROR could not add entry\n",
+ XPALongTimeout());
+ break;
+ case 0:
+ XPAPuts(NULL, req->sock, "XPA$OK\n", XPALongTimeout());
+ break;
+ case 1:
+ XPAPuts(NULL, req->sock, "XPA$EXISTS entry already exists\n",
+ XPALongTimeout());
+ break;
+ default:
+ XPAPuts(NULL, req->sock, "XPA$ERROR could not add entry\n",
+ XPALongTimeout());
+ break;
+ }
+ }
+ }
+ else{
+ strcpy(tbuf,
+ "XPA$ERROR 'proxy' requires 4 args: ip:port class:name type user\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: DelReq
+ *
+ * Purpose: delete an XPA entry from the list
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+DelReq (Req req, char *lbuf, int flag)
+#else
+static void DelReq(req, lbuf, flag)
+ Req req;
+ char *lbuf;
+ int flag;
+#endif
+{
+ int got=0;
+ char method[SZ_LINE];
+ char omethod[SZ_LINE];
+ char tbuf[SZ_LINE];
+
+ if( lbuf != NULL ){
+ if( sscanf(lbuf, "%s", method) == 1 ){
+ /* fix method if we can determine its been through a firewall */
+ strcpy(omethod, method);
+ FirewallEntry(req, method);
+ if( !strcmp(omethod, method) )
+ strcpy(tbuf, method);
+ else
+ snprintf(tbuf, SZ_LINE, "%s,%s", method, omethod);
+ got = DelEntry(req, tbuf);
+ Log();
+ if( flag ){
+ if( got == 0 )
+ XPAPuts(NULL, req->sock, "XPA$OK\n", XPALongTimeout());
+ else
+ XPAPuts(NULL, req->sock, "XPA$ERROR entry does not exist\n",
+ XPALongTimeout());
+ }
+ }
+ else{
+ strcpy(tbuf, "XPA$ERROR 'del' requires 1 arg: ip:port\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ }
+ }
+ else{
+ /* connection is closed -- free all entries for req, and delete req */
+ FPRINTF((stderr, "%sxpans request really died: %d\n", _sp, req->sock));
+ DelEntry(req, NULL);
+ FreeReq(req);
+ Log();
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: DelProxyReq
+ *
+ * Purpose: delete an XPA entry from the list
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+DelProxyReq (Req req, char *lbuf, int flag)
+#else
+static void DelProxyReq(req, lbuf, flag)
+ Req req;
+ char *lbuf;
+ int flag;
+#endif
+{
+ int got=0;
+ char method[SZ_LINE];
+ char omethod[SZ_LINE];
+ char tbuf[SZ_LINE];
+
+ /* make sure we are accepting proxy requests */
+ if( !doproxy ){
+ strcpy(tbuf,
+ "XPA$ERROR: proxy requests not enabled in this xpans\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ return;
+ }
+
+ if( lbuf != NULL ){
+ if( sscanf(lbuf, "%s", method) == 1 ){
+ /* fix method if we can determine its been through a firewall */
+ strcpy(omethod, method);
+ FirewallEntry(req, method);
+ if( !strcmp(omethod, method) )
+ strcpy(tbuf, method);
+ else
+ snprintf(tbuf, SZ_LINE, "@%s,%s", method, omethod);
+ /* free the specified entry */
+ got = DelEntry(req, tbuf);
+ Log();
+ if( flag ){
+ if( got == 0 )
+ XPAPuts(NULL, req->sock, "XPA$OK\n", XPALongTimeout());
+ else
+ XPAPuts(NULL, req->sock, "XPA$ERROR entry does not exist\n",
+ XPALongTimeout());
+ }
+ }
+ else{
+ strcpy(tbuf, "XPA$ERROR 'del' requires 1 arg: ip:port\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ }
+ }
+ else{
+ /* connection is closed -- free all entries for req, and delete req */
+ DelEntry(req, "@");
+ /* if this is the last entry, delete the request struct as well */
+ if( req->entry == NULL ){
+ FreeReq(req);
+ }
+ Log();
+ }
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: LookupReq
+ *
+ * Purpose: lookup a template in the XPA list
+ *
+ * Returns: number of matched lookups
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+LookupReq (Req xreq, char *lbuf, int flag)
+#else
+static int LookupReq(xreq, lbuf, flag)
+ Req xreq;
+ char *lbuf;
+ int flag;
+#endif
+{
+ int i;
+ int nrec;
+ int got=0;
+ int domethod=0;
+ int slen=1024;
+ int *lens;
+ char args[4][SZ_LINE];
+ char ctmpl[SZ_LINE];
+ char ntmpl[SZ_LINE];
+ char tbuf[SZ_LINE];
+ char type[SZ_LINE];
+ char users[SZ_LINE];
+ char method[SZ_LINE];
+ char *user;
+ char *usercopy;
+ char **strings;
+ char *tstring;
+ Req req;
+ Entry entry;
+
+ SplitArg(lbuf, 0);
+ nrec = sscanf(lbuf, "%s %s %s %s", args[0], args[1], args[2], args[3]);
+ switch(nrec){
+ case 0:
+ goto error;
+ case 1:
+ strcpy(ctmpl, "*");
+ strcpy(ntmpl, args[0]);
+ strcpy(type, "*");
+ strcpy(users, "*");
+ if( *args[0] == '@' ){
+ strcpy(method, args[0]);
+ domethod = 1;
+ }
+ break;
+ case 2:
+ strcpy(ctmpl, "*");
+ strcpy(ntmpl, args[0]);
+ strcpy(type, args[1]);
+ strcpy(users, "*");
+ if( *args[0] == '@' ){
+ snprintf(method, SZ_LINE, "%s:%s", args[0], args[1]);
+ domethod = 1;
+ }
+ break;
+ case 3:
+ strcpy(ctmpl, "*");
+ strcpy(ntmpl, args[0]);
+ strcpy(type, args[1]);
+ strcpy(users, args[2]);
+ if( *args[0] == '@' ){
+ strcpy(method, args[0]);
+ domethod = 1;
+ }
+ break;
+ case 4:
+ strcpy(ctmpl, args[0]);
+ strcpy(ntmpl, args[1]);
+ strcpy(type, args[2]);
+ strcpy(users, args[3]);
+ if( *args[0] == '@' ){
+ snprintf(method, SZ_LINE, "%s:%s", args[0], args[1]);
+ domethod = 1;
+ }
+ break;
+ case 5:
+ goto error;
+ }
+ strings = (char **)xmalloc(slen * sizeof(char *));
+ lens = (int *)xmalloc(slen * sizeof(int));
+ lens[0] = 0;
+ for(req=reqhead; req!=NULL; req=req->next){
+ for(entry=req->entry; entry!=NULL; entry=entry->next){
+ /* check method or class:name */
+ if( domethod ){
+ if( strcmp(entry->method, method) )
+ continue;
+ }
+ else{
+ if( !tmatch(entry->xclass, ctmpl) || !tmatch(entry->name, ntmpl) )
+ continue;
+ }
+ /* check type */
+ if( strcmp(type, "*") && !strpbrk(entry->type, type) )
+ continue;
+ /* check user */
+ if( !strcmp(users, "*") || !strcmp(users, entry->user) ){
+ user = entry->user;
+ }
+ else{
+ user = NULL;
+ usercopy = (char *)xstrdup(users);
+ for(user=(char *)strtok(usercopy, " :;,");
+ user!=NULL;
+ user=(char *)strtok(NULL," :;,")){
+ if ( !strcasecmp(user, entry->user) ){
+ break;
+ }
+ }
+ if( usercopy )
+ xfree(usercopy);
+ }
+ if( !user )
+ continue;
+ /* made it through all checks! */
+ if( domethod || (*entry->method == '@') ){
+ snprintf(tbuf, SZ_LINE, "%s %s %s %s %s %s\n",
+ entry->xclass, entry->name,
+ entry->type, XPANSMethod(NULL,1), entry->user, entry->info);
+ FPRINTF((stderr, "%sLookupReq: method lookup found:\n%s", _sp, tbuf));
+ }
+ else{
+ snprintf(tbuf, SZ_LINE, "%s %s %s %s %s %s\n",
+ entry->xclass, entry->name,
+ entry->type, entry->method, entry->user, entry->info);
+ FPRINTF((stderr, "%sLookupReq: class/name lookup got:\n%s",
+ _sp, tbuf));
+ }
+ if( got >= (slen-2) ){
+ slen *= 2;
+ strings = (char **)xrealloc(strings, slen * sizeof(char *));
+ lens = (int *)xrealloc(lens, slen * sizeof(int));
+ }
+ strings[got] = xstrdup(tbuf);
+ lens[got+1] = lens[got] + strlen(strings[got]);
+ got++;
+ }
+ }
+ if( flag ){
+ strings[got] = xstrdup("XPA$OK\n");
+ lens[got+1] = lens[got] + strlen(strings[got]);
+ got++;
+ }
+ /* write one buffer load: we have to avoid multiple writes in a row
+ because tcp_delay buffers these (i.e., the Nagle algorithm) and kills
+ the performance */
+ if( got > 0 ){
+ tstring = (char *)xcalloc(lens[got]+1, sizeof(char));
+ for(i=0; i<got; i++){
+ if( strings[i] ){
+ strcpy(&(tstring[lens[i]]), strings[i]);
+ }
+ }
+ XPAPutBuf(NULL, xreq->sock, tstring, lens[got], XPALongTimeout());
+ for(i=0; i<got; i++){
+ if( strings[i] )
+ xfree(strings[i]);
+ }
+ if( tstring )
+ xfree(tstring);
+ }
+ if( strings )
+ xfree(strings);
+ if( lens )
+ xfree(lens);
+ return(got);
+
+error:
+ strcpy(tbuf,
+ "XPA$ERROR xpans 'lookup' requires: class:name [type] [user]\n");
+ XPAPuts(NULL, xreq->sock, tbuf, XPALongTimeout());
+ return(0);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: HelpReq
+ *
+ * Purpose: send help message
+ *
+ * Returns: none
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+HelpReq (Req xreq, int flag)
+#else
+static void HelpReq(xreq, flag)
+ Req xreq;
+ int flag;
+#endif
+{
+ /* XPAPuts(NULL, xreq->sock, helpbuf, XPALongTimeout()); */
+ XPAPutBuf(NULL, xreq->sock, helpbuf, strlen(helpbuf), XPALongTimeout());
+ if( flag )
+ XPAPuts(NULL, xreq->sock, "XPA$OK\n", XPALongTimeout());
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: ListReq
+ *
+ * Purpose: list all entries in the XPA list
+ *
+ * Returns: number of entries listed
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+ListReq (Req xreq, int flag)
+#else
+static int ListReq(xreq, flag)
+ Req xreq;
+ int flag;
+#endif
+{
+ int got=0;
+ char tbuf[SZ_LINE];
+ Req req;
+ Entry entry;
+
+ for(req=reqhead; req!=NULL; req=req->next){
+ for(entry=req->entry; entry!=NULL; entry=entry->next){
+ snprintf(tbuf, SZ_LINE, "%s %s %s %s %s\n",
+ entry->xclass, entry->name,
+ entry->type, entry->method, entry->user);
+ XPAPuts(NULL, xreq->sock, tbuf, XPALongTimeout());
+ got++;
+ }
+ }
+ if( flag )
+ XPAPuts(NULL, xreq->sock, "XPA$OK\n", XPALongTimeout());
+ return(got);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: StatusReq
+ *
+ * Purpose: send short "alive" message to inquiring server
+ *
+ * Returns: NONE
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+StatusReq (Req xreq)
+#else
+static void StatusReq(xreq)
+ Req xreq;
+#endif
+{
+ XPAPuts(NULL, xreq->sock, "XPA$OK\n", XPALongTimeout());
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: VersionReq
+ *
+ * Purpose: send version info to inquiring server
+ *
+ * Returns: NONE
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static void
+VersionReq (Req xreq, char *vstr)
+#else
+static void VersionReq(xreq, vstr)
+ Req xreq;
+ char *vstr;
+#endif
+{
+ int ip=0;
+ int dowarn=0;
+ char tbuf[SZ_LINE];
+
+ /* version check: server version should be <= our version */
+ if( word(vstr, tbuf, &ip) ){
+ dowarn = (XPAVersionCheck(XPA_VERSION, tbuf)>0);
+ }
+ else{
+ strcpy(tbuf, "unknown/pre-2.1");
+ dowarn = 1;
+ }
+ if( dowarn )
+ XPAVersionWarn(tbuf, XPA_VERSION);
+ snprintf(tbuf, SZ_LINE, "XPA$VERSION %s\n", XPA_VERSION);
+ XPAPuts(NULL, xreq->sock, tbuf, XPALongTimeout());
+}
+
+#ifdef ANSI_FUNC
+void
+usage (char *s)
+#else
+void usage(s)
+ char *s;
+#endif
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "usage: xpans [-h] [-e] [-k msec] [-l log] [-p port] [-s slog] [-P n] \n");
+ fprintf(stderr, "switches:\n");
+ fprintf(stderr, "\t-h\tprint this message\n");
+ fprintf(stderr, "\t-e\texit when there are no more XPA connections\n");
+ fprintf(stderr, "\t-k msec\tsend keepalive messages every n sec\n");
+ fprintf(stderr, "\t-l file\tlog data base entries to specified file\n");
+ fprintf(stderr, "\t-p port\tlisten for connections on specified port\n");
+ fprintf(stderr, "\t-s file\tlog security info to specified file\n");
+ fprintf(stderr, "\t-P 1|2\taccept proxy requests (1) using separate thread (2) \n");
+ fprintf(stderr, "\t--version display version and exit\n");
+ fprintf(stderr, "\n(version: %s)\n", XPA_VERSION);
+ exit(1);
+}
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Routine: KeepAliveReq
+ *
+ * Purpose: send keep alive to all open connections
+ *
+ * Returns: number of entries processed
+ *
+ *----------------------------------------------------------------------------
+ */
+#ifdef ANSI_FUNC
+static int
+KeepAliveReq (void)
+#else
+static int KeepAliveReq()
+#endif
+{
+ int got=0;
+ Req req;
+
+ /* return if keepalive is turned off */
+ if( !keepalive )
+ return(0);
+ /* get current time */
+ curt = time(NULL);
+ /* if keep alive time has passed, send keep alive messages */
+ if( (curt - lastt) > ksec ){
+ for(req=reqhead; req!=NULL; req=req->next){
+ send(req->sock, " ", 1, MSG_OOB);
+ got++;
+ }
+ lastt = curt;
+ }
+ return(got);
+}
+
+#ifdef ANSI_FUNC
+int
+main (int argc, char **argv)
+#else
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+#endif
+{
+ char lbuf[SZ_LINE];
+ char tbuf[SZ_LINE];
+ char cmd[SZ_LINE];
+ char method[SZ_LINE];
+ int c;
+ int sock2;
+ int width;
+ int got;
+ int wp;
+ int cmdport;
+ int sval;
+ int nerr=0;
+ int oum;
+ int llen;
+ int reuse_addr=1;
+ int keep_alive=1;
+ unsigned int ip;
+ unsigned int cmdip;
+ unsigned short port=0;
+ socklen_t slen=sizeof(struct sockaddr_in);
+ struct sockaddr_in sock_in;
+ struct sockaddr_in sock_in2;
+#if HAVE_LIBPTHREAD
+ pthread_t tid;
+#endif
+#if HAVE_SYS_UN_H
+ struct sockaddr_un sock_un;
+ struct sockaddr_un sock_un2;
+ char lockfile[SZ_LINE];
+ int lockfd=-1;
+ struct flock lock;
+#endif
+ fd_set readfds;
+ struct timeval tv;
+ struct timeval *tvp;
+ Req req, treq;
+ XPA xpa;
+
+ /* display version and exit, if necessary */
+ if( (argc == 2) && !strcmp(argv[1], "--version") ){
+ fprintf(stderr, "%s\n", XPA_VERSION);
+ exit(0);
+ }
+
+ /* init the XPA environment */
+ XPAInitEnv();
+
+ /* Disable SIGPIPE so we do not die if the client dies.
+ * Rather, we will get an EOF on reading or writing.
+ */
+ xsignal_sigpipe();
+
+ /* we want the args in the same order in which they arrived, and
+ gnu getopt sometimes changes things without this */
+ putenv("POSIXLY_CORRECT=true");
+
+ /* process command line args */
+ while ((c = getopt(argc, argv, "ef:hk:l:p:P:s:")) != -1){
+ switch(c){
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ case 'e':
+ exconn = 1;
+ break;
+ case 'f':
+#if HAVE_SYS_UN_H
+ /* method is unix with specified file */
+ putenv(xstrdup("XPA_METHOD=unix"));
+ snprintf(tbuf, SZ_LINE, "XPA_NSUNIX=%s", optarg);
+ putenv(xstrdup(tbuf));
+ break;
+#else
+ fprintf(stderr, "XPA$ERROR: UNIX sockets not supported on this host\n");
+ exit(1);
+ break;
+#endif
+ case 'k':
+ fprintf(stderr,
+ "XPA$KEEPALIVE: URG TCP data is changed by some Cisco routers into in-band data.\n");
+ fprintf(stderr,
+ "Encountering such a router will break the keep-alive function and may break your\n");
+ fprintf(stderr, "XPA server as well. Proceed with caution!\n");
+ keepalive = 1;
+ ksec = atoi(optarg);
+ if( ksec <= 0 )
+ ksec = 1;
+ break;
+ case 'l':
+ logfile = optarg;
+ break;
+ case 'p':
+ /* method is inet with specified port */
+ putenv(xstrdup("XPA_METHOD=inet"));
+ snprintf(tbuf, SZ_LINE, "XPA_NSINET=$host:%d", atoi(optarg));
+ putenv(xstrdup(tbuf));
+ break;
+ case 'P':
+ if( istrue(optarg) )
+ doproxy = 1;
+ else if( isfalse(optarg) )
+ doproxy = 0;
+ else
+ doproxy = atoi(optarg);
+ if( doproxy < 0 )
+ doproxy = 0;
+#if HAVE_LIBPTHREAD==0
+ if( doproxy >= 2 ){
+ fprintf(stderr,
+ "XPA$ERROR: xpans not built with thread support on this host\n");
+ exit(1);
+ }
+#endif
+ break;
+ case 's':
+ if( !strcasecmp(optarg, "stdout") ){
+ securefp = stdout;
+ }
+ else if( (securefp=fopen(optarg, "w")) == NULL ){
+ perror("securefp");
+ exit(1);
+ }
+ break;
+ }
+ }
+
+ /* get default ip and port */
+ strcpy(method, XPANSMethod(NULL, 0));
+ mtype = XPAMethod(method);
+ localhost_ip = gethostip("$localhost");
+
+ /* start secure logging, if necessary */
+ SecureLog("Starting xpans: %s", method);
+ /* set up communication method */
+ switch(mtype){
+ case XPA_INET:
+ XPAParseIpPort(method, &ip, &port);
+ if( (sock = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ){
+ if( XPAVerbosity() > 1 )
+ perror("xpans socket()");
+ exit(1);
+ }
+ setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&reuse_addr, sizeof(reuse_addr));
+ memset((char *)&sock_in, 0, sizeof(sock_in));
+ sock_in.sin_family = AF_INET;
+ /* localhost only */
+ if( ip == localhost_ip )
+ sock_in.sin_addr.s_addr = htonl(ip);
+ /* any address will do */
+ else
+ sock_in.sin_addr.s_addr = htonl(INADDR_ANY);
+ sock_in.sin_port = htons(port);
+ /* bind to a port -- an error indicates that another xpans is running */
+ if( xbind(sock, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0 ){
+ if( XPAVerbosity() > 1 )
+ perror("xpans bind()");
+ exit(1);
+ }
+ SecureLog("Opening INET socket: %d", sock);
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ /* with unix sockets, we lock a special file to signal any new xpans
+ process that it is not needed. This behavior mimicks the bind()
+ error with inet sockets */
+ snprintf(lockfile, SZ_LINE, "%s-lock", method);
+ if( (lockfd=open(lockfile, O_CREAT|O_RDWR, 0666)) >=0 ){
+ lock.l_type = F_WRLCK; /* F_RDLCK, F_WRLCK, F_UNLCK */
+ lock.l_start = 0; /* byte offset, relative to l_whence */
+ lock.l_whence = SEEK_SET; /* SEEK_SET, SEE_CUR, SEEK_END */
+ lock.l_len = 1; /* #bytes (0 means to EOF) */
+ /* if we can't get the lock, there is an xpans already running */
+ if( xfcntl(lockfd, F_SETLK, &lock) < 0 ){
+ close(lockfd);
+ if( XPAVerbosity() > 1 )
+ fprintf(stderr, "XPA$ERROR: xpans already running\n");
+ exit(1);
+ }
+ }
+ unlink(method);
+ if( (sock = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ){
+ if( XPAVerbosity() > 1 )
+ perror("xpans socket()");
+ exit(1);
+ }
+ setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
+ (char *)&keep_alive, sizeof(keep_alive));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&reuse_addr, sizeof(reuse_addr));
+ memset((char *)&sock_un, 0, sizeof(sock_un));
+ sock_un.sun_family = AF_UNIX;
+ strcpy(sock_un.sun_path, method);
+ /* unset umask so that everyone can read and write */
+ oum = umask(0);
+ /* bind to a port */
+ if( xbind(sock, (struct sockaddr *)&sock_un, sizeof(sock_un)) < 0 ){
+ if( XPAVerbosity() > 1 )
+ perror("xpans bind()");
+ exit(1);
+ }
+ /* reset umask */
+ umask(oum);
+ SecureLog("opened UNIX socket: %d", sock);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ /* listen for connections */
+ if( listen(sock, XPA_MAXLISTEN) < 0 ){
+ if( XPAVerbosity() > 1 )
+ perror("xpans listen()");
+ exit(1);
+ }
+ /* make sure we close on exec */
+ xfcntl(sock, F_SETFD, FD_CLOEXEC);
+
+ /* add an XPA access point for external processing */
+ if( !(xpa=XPANew(XPANS_CLASS, XPANS_NAME, helpbuf,
+ send_cb, NULL, "fillbuf=false",
+ receive_cb, NULL, "fillbuf=false")) ){
+ if( XPAVerbosity() > 1 )
+ fprintf(stderr, "XPA$ERROR: failed to create access point for xpans\n");
+ exit(1);
+ }
+ SecureLog("XPA access point: %s", xpa->name);
+
+ /* init select parameters */
+ width = FD_SETSIZE;
+
+#if HAVE_LIBPTHREAD
+ /* start up new thread for XPA main loop, if necessary */
+ if( doproxy >= 2 ){
+ if( pthread_create(&tid, NULL, doxpaloop, NULL) != 0 ){
+ fprintf(stderr,
+ "XPA$ERROR: can't create new thread for XPA handler in xpans\n");
+ exit(1);
+ }
+ }
+#endif
+
+ /* enter processing loop */
+ while( 1 ){
+ /* reset flag */
+ FD_ZERO(&readfds);
+ /* add main listening socket */
+ FD_SET(sock, &readfds);
+ /* add request lines */
+ for(got=0, req=reqhead; req!=NULL; req=req->next){
+ FD_SET(req->sock, &readfds);
+ got++;
+ }
+ /* add XPA selections */
+ if( doproxy < 2 ) XPAAddSelect(NULL, &readfds);
+ /* if we once had entries but do not have them now, we might be done */
+ if( exconn && nentry && !got ){
+ goto done;
+ }
+ if( keepalive ){
+ tv.tv_sec = ksec;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+ else{
+ tvp = NULL;
+ }
+ /* wait for next request */
+ sval = xselect(width, &readfds, NULL, NULL, tvp);
+ if( sval > 0 ){
+#if HAVE_LIBPTHREAD
+ /* lock the mutex before processing a reqest */
+ if( doproxy >= 2 ) pthread_mutex_lock(&xpans_mutex);
+#endif
+ /* process a new major request */
+ if( FD_ISSET(sock, &readfds) ){
+ /* new request */
+ switch(mtype){
+ case XPA_INET:
+ while( 1 ){
+ slen = sizeof(struct sockaddr_in);
+ if((sock2=xaccept(sock, (struct sockaddr *)&sock_in2, &slen))>=0){
+ cmdip = ntohl(sock_in2.sin_addr.s_addr);
+ cmdport = ntohs(sock_in2.sin_port);
+ /* make sure we close on exec */
+ xfcntl(sock2, F_SETFD, FD_CLOEXEC);
+ NewReq(sock2, cmdip, cmdport);
+ SecureLog("accept %d: %x:%d (%s)",
+ sock2, cmdip, cmdport, getiphost(cmdip));
+ break;
+ }
+ else{
+ if( errno == EINTR )
+ continue;
+ else
+ break;
+ }
+ }
+ break;
+#if HAVE_SYS_UN_H
+ case XPA_UNIX:
+ while( 1 ){
+ slen = sizeof(struct sockaddr_un);
+ if( (sock2=xaccept(sock, (struct sockaddr *)&sock_un2, &slen))>=0){
+ /* make sure we close on exec */
+ xfcntl(sock2, F_SETFD, FD_CLOEXEC);
+ NewReq(sock2, 0, 0);
+ SecureLog("accept from local socket");
+ break;
+ }
+ else{
+ if( errno == EINTR )
+ continue;
+ else
+ break;
+ }
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ /* process an existing request line */
+ for(got=0, req=reqhead; req!=NULL; ){
+ treq = req->next;
+ if( FD_ISSET(req->sock, &readfds) ){
+ FPRINTF((stderr, "%sxpans existing request: %d\n", _sp, req->sock));
+ if( XPAGets(NULL, req->sock, lbuf, SZ_LINE, XPAShortTimeout()) >0 ){
+ llen = strlen(lbuf) - 1;
+ if( (lbuf[llen]) == '\n' ){
+ /* ignore a single new-line, its a keep-alive message */
+ if( llen == 0){
+ FPRINTF((stderr, "%sxpans ignoring keep-alive\n", _sp));
+ req = treq;
+ continue;
+ }
+ /* else remove new-line */
+ else{
+ lbuf[llen] = '\0';
+ }
+ }
+ FPRINTF((stderr, "%sxpans request: %s\n", _sp, lbuf));
+ SecureLog("cmd %d: %s", req->sock, lbuf);
+ /* process another request from this process */
+ wp = 0;
+ /* get first token: command */
+ if( !word(lbuf, cmd, &wp) ){
+ strcpy(tbuf, "XPA$ERROR no xpans command specified\n");
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ }
+ else{
+ /* skip white space */
+ while( isspace((int)lbuf[wp]) )
+ wp++;
+ /* lookup the command */
+ if( !strcmp(cmd, "status") ){
+ StatusReq(req);
+ }
+ else if( !strcmp(cmd, "version") ){
+ VersionReq(req, &(lbuf[wp]));
+ }
+ else if( !strcmp(cmd, "add") ){
+ AddReq(req, &(lbuf[wp]), 1);
+ }
+ else if( !strcmp(cmd, "addproxy") ){
+ AddProxyReq(req, &(lbuf[wp]), 1);
+ }
+ else if( !strcmp(cmd, "del") ){
+ DelReq(req, &(lbuf[wp]), 1);
+ }
+ else if( !strcmp(cmd, "delproxy") ){
+ DelProxyReq(req, &(lbuf[wp]), 1);
+ }
+ else if( !strcmp(cmd, "help") ){
+ HelpReq(req, 1);
+ }
+ else if( !strcmp(cmd, "list") ){
+ ListReq(req, 1);
+ }
+ else if( !strcmp(cmd, "lookup") ){
+ LookupReq(req, &(lbuf[wp]), 1);
+ }
+ else{
+ SecureLog("ignoring unknown command");
+ snprintf(tbuf, SZ_LINE,
+ "XPA$ERROR unknown xpans request: %s\n", lbuf);
+ XPAPuts(NULL, req->sock, tbuf, XPALongTimeout());
+ }
+ }
+ }
+ else{
+ /* process dies: delete all entries associated with this sock */
+ FPRINTF((stderr, "%sxpans request died: %d\n", _sp, req->sock));
+ DelReq(req, NULL, 1);
+ }
+ }
+ req = treq;
+ }
+
+ /* process xpa requests */
+ if( doproxy < 2 ) XPAProcessSelect(&readfds, 0);
+#if HAVE_LIBPTHREAD
+ /* unlock the mutex */
+ if( doproxy >= 2 ) pthread_mutex_unlock(&xpans_mutex);
+#endif
+ }
+ /* keep alive timer went off */
+ else if( sval == 0 ){
+ ;
+ }
+ /* error on select() */
+ else{
+ /* restart system call if interrupted */
+ if( errno != EINTR ){
+ /* all others are problematic */
+ if( XPAVerbosity() > 1 )
+ perror("xpans select()");
+ if( ++nerr >= MAX_ERRORS ){
+ if( XPAVerbosity() > 1 )
+ fprintf(stderr,
+ "XPA$ERROR: too many select() errors in xpans\n");
+ goto done;
+ }
+ }
+ }
+ /* see if its time to send keepalive probe */
+ if( keepalive )
+ KeepAliveReq();
+ }
+
+done:
+ if( sock >=0 ) close(sock);
+ if( securefp && (securefp != stdout) ) fclose(securefp);
+#if HAVE_SYS_UN_H
+ if( mtype == XPA_UNIX ){
+ unlink(method);
+ if( lockfd >= 0 ){
+ close(lockfd);
+ unlink(lockfile);
+ }
+ }
+#endif
+ XPAFree(xpa);
+ return(0);
+}