diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2017-10-26 16:45:12 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2017-10-26 16:45:12 (GMT) |
commit | dd1df8d8538f275b17e0d9d50dca19f4d44595fd (patch) | |
tree | 624bc07023b33668147eaa9097279c6fc92b4e48 /xpa/xpans.c | |
parent | 79d64f400391ce81b4eda73977cb40099256b348 (diff) | |
parent | 6054f36bb658916bd231b9002efb94444e2871c8 (diff) | |
download | blt-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.c | 1935 |
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); +} |