/* * Copyright (c) 1999-2003 Smithsonian Astrophysical Observatory */ #include /* *---------------------------------------------------------------------------- * * * Private Routines and Data * * *---------------------------------------------------------------------------- */ /* this is the head of the global list -- too lazy to do anything more */ static XPA xpahead=NULL; /* globals for the xpa environment, controlled by environment variables */ static int stimeout=XPA_SHORT_TIMEOUT; /* select, gets timeout in secs */ static int ltimeout=XPA_LONG_TIMEOUT; /* fillbuf timeout in secs */ static int ctimeout=XPA_CONNECT_TIMEOUT;/* local xpans connect */ static int verbosity=XPA_VERBOSITY; /* 0=quiet, 1=normal, 2=full */ static int nsdelay=XPA_NSDELAY; /* delay increment waiting for ns to start */ static int retries=XPA_RETRIES; /* retries when retrying */ static int guseacl=1; /* 0=don't use acls, 1=enable acls */ static int etimestamp=0; /* 0=don't timestamp errors, 1=do timestamp */ static int nsregister=1; /* 0=don't register with xpans, 1=register */ static int sigusr1=1; /* 0=don't use sigusr1, 1=enable sigusr1 */ static int vercheck=1; /* 0=don't check version, 1=check version */ static char *tmpdir=NULL; /* temporary dir for logs. etc. */ #if HAVE_ATEXIT static int atexitinit=0; /* whether atexit has been registered */ #endif /* variables used by all XPAs in this process */ static char activefds[FD_SETSIZE]; static char nsmethod[SZ_LINE]; static char nsusers[SZ_LINE]; /* global method type: unix or inet */ static int mtype=0; /* width of select channel flag */ static int swidth=FD_SETSIZE; /* defined in tcp.c */ extern int use_localhost; /* erro code strings -- must match the error codes in xpap.h */ char *xpaMessbuf[] = { "oh, what a mess we've made!", "authentication failed", "xpa connection refused", "can't resolve host name for xpa", "can't read initialization info for xpa", "invalid xpa command in initialization string", "no receive function defined for this xpa", "no send function defined for this xpa", "no info function defined for this xpa", "undefined command for this xpa", "missing command for this xpa", "name does not match target template", "can't create data channel socket", "could not read data buf (possible timeout)", "illegal command or command switch" }; /* temp static time buffer */ static char ctimebuf[SZ_LINE]; /* *---------------------------------------------------------------------------- * * Routine: XPAProxyConnect * * Purpose: connect to a client (in proxy mode) * * Returns: 0 if successfull, otherwise -1 * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int XPAProxyConnect(XPA xpa, char *method, unsigned int *rip, unsigned short *rport, char *rname) #else static int XPAProxyConnect(xpa, method, rip, rport, rname) XPA xpa; char *method; unsigned int *rip; unsigned short *rport; char *rname; #endif { int ofd; int tries=0; int keep_alive=1; unsigned int ip; unsigned short port; struct sockaddr_in sock_in; #if HAVE_SYS_UN_H struct sockaddr_un sock_un; #endif FPRINTF((stderr, "%sXPAProxyConnect to %s\n", _sp, method)); /* initialize results */ if( rip ) *rip = 0; if( rport ) *rport = 0; if( rname ) *rname = '\0'; switch(XPAMethod(method)){ case XPA_INET: again1: if( !XPAParseIpPort(method, &ip, &port) ){ goto error; } /* use $localhost over $host (we don't trust host to be correct) */ if( (ip == gethostip("$host")) && (tries == 0) ){ ip = gethostip("$localhost"); } /* connect to the server before we go further */ if( (ofd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ){ PERROR(("XPAProxyConnect: socket()")); goto error; } setsockopt(ofd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keep_alive, sizeof(keep_alive)); memset((char *)&sock_in, 0, sizeof(sock_in)); sock_in.sin_family = AF_INET; sock_in.sin_addr.s_addr = htonl(ip); sock_in.sin_port = htons(port); /* make the connection with the server */ if(connect(ofd, (struct sockaddr *)&sock_in, sizeof(sock_in))<0){ xclose(ofd); /* if localhost doesn't work, make one try with the host ip */ /* we also try again just in case there was an odd error such as "permission denied", which we have seen once or twice */ if( tries < 2 ){ tries++; goto again1; } /* give up */ else{ PERROR(("XPAProxyConnect: connect()")); goto error; } } break; #if HAVE_SYS_UN_H case XPA_UNIX: again2: ip = 0; port = 0; /* open a socket and fill in socket information */ if( (ofd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ){ goto error; } setsockopt(ofd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keep_alive, sizeof(keep_alive)); memset((char *)&sock_un, 0, sizeof(sock_un)); sock_un.sun_family = AF_UNIX; strcpy(sock_un.sun_path, method); /* make the connection with the server */ if(connect(ofd, (struct sockaddr *)&sock_un, sizeof(sock_un))<0){ xclose(ofd); /* Unix sockets get ECONNREFUSED when the listen queue is full, so we try a few times to give the server a chance to recover */ if( (xerrno == ECONNREFUSED) && (tries < retries) ){ tries++; XPASleep(10); goto again2; } /* give up */ else{ goto error; } } break; #endif default: goto error; } /* fill in blansk */ if( rip ) *rip = ip; if( rport ) *rport = port; if( rname ){ strncpy(rname, method, SZ_LINE-1); rname[SZ_LINE-1] = '\0'; } return(ofd); error: return -1; } /* *---------------------------------------------------------------------------- * * Routine: CommNew * * Purpose: allocate a new comm record and add to end of list * * Returns: allocated comm record * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static XPAComm CommNew (XPA xpa, int fd, unsigned int ip, int port, char *name, NS ns) #else static XPAComm CommNew(xpa, fd, ip, port, name, ns) XPA xpa; int fd; unsigned int ip; int port; char *name; NS ns; #endif { XPAComm comm, cur; int i; socklen_t slen; struct sockaddr_in sock_in; #if HAVE_SYS_UN_H struct sockaddr_un sock_un; #endif /* create the comm struct */ if( (comm = (XPAComm)xcalloc(1, sizeof(XPACommRec))) == NULL ) return(NULL); /* if fd < 0, we accept a connection to get the fd */ if( fd < 0 ){ /* accept the connection */ switch(mtype){ case XPA_INET: while( 1 ){ slen = sizeof(struct sockaddr_in); if((comm->cmdfd=xaccept(xpa->fd,(struct sockaddr *)&sock_in,&slen))>=0){ comm->cmdip = ntohl(sock_in.sin_addr.s_addr); comm->cmdport = ntohs(sock_in.sin_port); break; } else{ if( xerrno == EINTR ) continue; else{ xfree(comm); return(NULL); } } } break; #if HAVE_SYS_UN_H case XPA_UNIX: while( 1 ){ slen = sizeof(struct sockaddr_un); if((comm->cmdfd=xaccept(xpa->fd,(struct sockaddr *)&sock_un,&slen))>=0){ comm->cmdname = xstrdup(sock_un.sun_path); break; } else{ if( xerrno == EINTR ) continue; else{ xfree(comm); return(NULL); } } } break; #endif default: xfree(comm); return(NULL); } } /* all info is supplied */ else{ switch(mtype){ case XPA_INET: comm->cmdip = ip; comm->cmdport = port; break; case XPA_UNIX: comm->cmdname = xstrdup(name); break; } comm->cmdfd = fd; /* store name server record */ comm->ns = ns; } /* set back pointer */ /* make sure we close on exec */ xfcntl(comm->cmdfd, F_SETFD, FD_CLOEXEC); /* mark data socket with impossible value */ comm->datafd = -1; /* default is to ack */ comm->ack = 1; /* set default cendian flag */ comm->cendian = "?"; /* clear the acl flags */ for(i=0; iacl[i] = -1; } /* add this comm to end of list of comms */ if( xpa->commhead == NULL ){ xpa->commhead = comm; } else{ for(cur=xpa->commhead; cur->next!=NULL; cur=cur->next){ ; } cur->next = comm; } /* we might have to add this fd specially to a non-select event loop */ if( xpa->seladd ) comm->selcptr = (xpa->seladd)(xpa, comm->cmdfd); /* make this fd active */ XPAActive(xpa, comm, 1); FPRINTF((stderr, "%sCommNew: ip=%x port=%d fd=%d\n", _sp, comm->cmdip, comm->cmdport, comm->cmdfd)); /* return the good news */ return(comm); } /* *---------------------------------------------------------------------------- * * Routine: CommFree * * Purpose: free a comm record and remove from list * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static void CommFree (XPA xpa, XPAComm comm, int flag) #else static void CommFree(xpa, comm, flag) XPA xpa; XPAComm comm; int flag; #endif { XPAComm cur; if( !comm ) return; FPRINTF((stderr, "%sCommFree: ip=%x port=%d fd=%d dfd=%d\n", _sp, comm->cmdip, comm->cmdport, comm->cmdfd, comm->datafd)); /* remove from list of this xpa's comms */ if( xpa ){ if( xpa->commhead ){ if( xpa->commhead == comm ){ xpa->commhead = comm->next; } else{ for(cur=xpa->commhead; cur!=NULL; cur=cur->next){ if( cur->next == comm ){ cur->next = comm->next; break; } } } } } /* must check all xpas */ else{ for(xpa=xpahead; xpa!=NULL; xpa=xpa->next){ if( xpa->commhead ){ if( xpa->commhead == comm ){ xpa->commhead = comm->next; } else{ for(cur=xpa->commhead; cur!=NULL; cur=cur->next){ if( cur->next == comm ){ cur->next = comm->next; break; } } } } } } /* close socket connections */ if( flag && (comm->cmdfd >= 0) ){ FPRINTF((stderr, "%sCommFree closing cmd fd: %d\n", _sp, comm->cmdfd)); /* remove from active */ if( comm->cmdfd < FD_SETSIZE ) activefds[comm->cmdfd] = 0; /* delete the comm cmd fd specially from a non-select event loop */ if( xpa && xpa->seldel && comm->selcptr ){ (xpa->seldel)(comm->selcptr); comm->selcptr = NULL; } /* close file */ xclose(comm->cmdfd); } /* close data channel */ XPACloseData(xpa, comm); /* if we have a file name (unix sockets), free it */ if( comm->cmdname != NULL ){ unlink(comm->cmdname); xfree(comm->cmdname); } if( comm->dataname != NULL ){ unlink(comm->dataname); xfree(comm->dataname); } /* free up the space */ if( comm->id != NULL ) xfree(comm->id); if( comm->info != NULL ) xfree(comm->info); if( comm->target != NULL ) xfree(comm->target); if( comm->paramlist != NULL ) xfree(comm->paramlist); /* this comm is no longer associated with an ns */ if( comm->ns ){ comm->ns->nproxy -= 1; } /* disassociate from parent xpa */ if( xpa ){ xpa->comm = NULL; } /* free up structure */ xfree(comm); } /* *---------------------------------------------------------------------------- * * Routine: XPANSOpen * * Purpose: open a connection to the name service * * Returns: connection fd on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static NS XPANSOpen (XPA xpa, char *host, int force) #else static NS XPANSOpen(xpa, host, force) XPA xpa; char *host; int force; #endif { int i; int status; int xnsfd=0; int keep_alive=1; int tries=0; int ip=0; int dowarn=0; int contmode=XPA_CONNECT_TIMEOUT_MODE; unsigned short xnsport=0; unsigned int xnsip=0; static int errinit=0; char *s; char *path; char *method; char nscmd[SZ_LINE]; char tbuf[SZ_LINE]; char tbuf2[SZ_LINE]; struct sockaddr_in sock_in; struct passwd *pw; #if HAVE_SYS_UN_H struct sockaddr_un sock_un; #endif char *findname=NULL; NS ns, cur; /* get name server method */ method = XPANSMethod(host, 0); /* if the name service already is open, just return fd */ if( xpa ){ for(ns=xpa->nshead; ns!=NULL; ns=ns->next){ if( !strcmp(ns->method, method) ){ /* if forcing, see if the connection is valid */ if( force >= 0 ){ if( (XPAPuts(xpa, ns->fd, "status\n", stimeout) >0) && (XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0) && !strncmp(tbuf, "XPA$OK", 6) ){ FPRINTF((stderr, "%sXPANSOpen: found existing ns: %s\n", _sp, ns->method)); return ns; } /* found the ns, but its not open */ else{ FPRINTF((stderr, "%sXPANSOpen: existing ns is dead: %s\n", _sp, ns->method)); XPANSClose(xpa, ns); break; } } /* just return whatever we have if we are not forcing */ else{ return ns; } } } } /* if no existing ns and flag is negative, we are done */ if( force == -1 ) return(NULL); /* we always make up the command afresh */ *nscmd = '\0'; /* get users for this user */ if( (s=(char *)getenv("XPA_NSUSERS")) != NULL ) strncpy(nsusers, s, SZ_LINE-1); /* default is just this use's userrname, from the environment */ else if( (s=(char *)getenv("LOGNAME")) != NULL ) strncpy(nsusers, s, SZ_LINE-1); #if HAVE_MINGW32==0 /* this is a last resort */ else if( (pw=getpwuid(geteuid())) ) strncpy(nsusers, pw->pw_name, SZ_LINE-1); #endif /* if nothing good has happened, make it "all" */ if( *nsusers == '\0' ) strcpy(nsusers, "*"); /* null-terminate string */ nsusers[SZ_LINE-1] = '\0'; /* set up communications socket for this method */ switch(mtype){ case XPA_INET: again1: XPAParseIpPort(method, &xnsip, &xnsport); /* use $localhost over $host (we do not trust host to be correct) */ if( (xnsip == gethostip("$host")) && (tries == 0) ){ xnsip = gethostip("$localhost"); } if( xnsip == 0 ){ fprintf(stderr, "XPA$ERROR: invalid host name specified: %s.\n", method); return(NULL); } if( (xnsfd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ) goto nons; memset((char *)&sock_in, 0, sizeof(sock_in)); sock_in.sin_family = AF_INET; sock_in.sin_addr.s_addr = htonl(xnsip); sock_in.sin_port = htons(xnsport); /* try connecting to the name server */ if( ctimeout <= 0 ) contmode = 0; switch(contmode){ case 1: #if HAVE_MINGW32==0 status=alrmconnect(xnsfd, (void *)&sock_in, sizeof(sock_in), ctimeout); #else status=noblkconnect(xnsfd, (void *)&sock_in, sizeof(sock_in), ctimeout); #endif break; case 2: status=noblkconnect(xnsfd, (void *)&sock_in, sizeof(sock_in), ctimeout); break; default: status=connect(xnsfd, (struct sockaddr *)&sock_in, sizeof(sock_in)); break; } if( status == 0 ){ FPRINTF((stderr, "%sXPANSOpen: connect to xpans\n", _sp)); goto okns; } else{ xclose(xnsfd); /* if localhost doesn't work, make one try with the host ip */ if( (xerrno == ECONNREFUSED) && (tries < 1) ){ tries++; goto again1; } } break; #if HAVE_SYS_UN_H case XPA_UNIX: again2: /* open a socket and fill in socket information */ if( (xnsfd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ) goto nons; memset((char *)&sock_un, 0, sizeof(sock_un)); sock_un.sun_family = AF_UNIX; strcpy(sock_un.sun_path, method); xnsip = 0; /* try connecting to the name server */ status=connect(xnsfd, (struct sockaddr *)&sock_un, sizeof(sock_un)); if( status == 0 ){ FPRINTF((stderr, "%sXPANSOpen: connect to xpans\n", _sp)); goto okns; } else{ xclose(xnsfd); /* Unix sockets get ECONNREFUSED when the listen queue is full, so we try a few times to give the server a chance to recover */ if( (xerrno == ECONNREFUSED) && (tries < retries) ){ tries++; XPASleep(10); goto again2; } } break; #endif } /* if force is set, we try to start up the name server */ if( force == 0 ) goto noforce; /* make up the xpans command we will use */ if( *nscmd == '\0' ){ if( (mtype == XPA_UNIX) || LOCALIP(xnsip) ){ path = (char *)getenv("PATH"); findname = (char *)Find(XPANSNAME, "x", NULL, path); #if HAVE_CYGWIN /* this will help start up xpans under Windows */ if( !findname ) findname = (char *)Find(XPANSNAME, "x", NULL, "."); #endif } if( findname != NULL ){ switch(mtype){ case XPA_INET: #if USE_LAUNCH /* change spaces to special launch space */ for(i=0; imethod = xstrdup(method); ns->host = xstrdup(host); ns->fd = xnsfd; FPRINTF((stderr, "%sXPANSOpen: host %s opened on fd %d\n", _sp, host?host:"", xnsfd)); switch(mtype){ case XPA_INET: ns->ip = xnsip; ns->port = xnsport; break; #if HAVE_SYS_UN_H case XPA_UNIX: ns->name = xstrdup(method); break; #endif } if( xpa ){ /* add to list of name servers */ if( xpa->nshead == NULL ){ xpa->nshead = ns; } else{ for(cur=xpa->nshead; cur->next!=NULL; cur=cur->next) ; cur->next = ns; } } /* check version with xpans */ snprintf(tbuf, SZ_LINE, "version %s\n", XPA_VERSION); if( (XPAPuts(xpa, ns->fd, tbuf, stimeout) >0) && (XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0) ){ if( word(tbuf, tbuf2, &ip) ){ if( !strcmp(tbuf2, "XPA$VERSION") ){ if( word(tbuf, tbuf2, &ip) ){ /* version check: our server version should be <= xpans version */ dowarn = (XPAVersionCheck(XPA_VERSION, tbuf2)>0); } else{ strcpy(tbuf2, "unknown/pre-2.1 (noversion)"); dowarn = 1; } } else{ strcpy(tbuf2, "unknown/pre-2.1 (badformat)"); dowarn = 1; } } FPRINTF((stderr, "%sXPANSOpen: version info: %s\n", _sp, tbuf)); if( dowarn ){ XPAVersionWarn(XPA_VERSION, tbuf2); } } /* clean up */ if( findname != NULL ) xfree(findname); return(ns); nons: /* if we specified an explicit port, we don't need the name server, so don't bother with any warning or error messages */ if( XPAPort(xpa) >0 ) return NULL; switch(mtype){ case XPA_INET: if( !errinit && verbosity ){ if( LOCALIP(xnsip) ){ fprintf(stderr, "XPA$WARNING: xpans needs to be running on this machine.\n"); } else{ fprintf(stderr, "XPA$WARNING: xpans needs to be running on machine: "); fprintf(stderr, "%s\n", getiphost(xnsip)); } } break; #if HAVE_SYS_UN_H case XPA_UNIX: if( !errinit && verbosity ){ fprintf(stderr, "XPA$WARNING: xpans needs to be running on this machine.\n"); } break; #endif default: break; } if( xpa && verbosity ){ if( !errinit ){ /* make up the command users will need to start xpans */ if( *nscmd == '\0' ){ switch(mtype){ case XPA_INET: snprintf(nscmd, SZ_LINE, "xpans -e -p %d -l %s/xpans_%d.log", xnsport, tmpdir, xnsport); break; #if HAVE_SYS_UN_H case XPA_UNIX: snprintf(nscmd, SZ_LINE, "xpans -e -f %s -l %s.log", method, method); break; #endif } } fprintf(stderr, "Please start xpans using the command:\n\t%s\n", nscmd); fprintf(stderr, "Once xpans is running, register all xpas in this process using:\n"); fprintf(stderr, "\txpaset -p %s -nsconnect\n", xpa->method); } fprintf(stderr, "For now, contact %s using:", xpa->name); fprintf(stderr, " xpaset %s .. or xpaget %s ..\n", xpa->method, xpa->method); } errinit++; noforce: if( findname != NULL ) xfree(findname); return(NULL); } /* *--------------------------------------------------------------------------- * * Routine: _XPAFree * * Purpose: free up memory in the XPA record structure * (internal version) * * Results: 0 on success, -1 for failure * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static int _XPAFree (XPA xpa) #else static int _XPAFree(xpa) XPA xpa; #endif { char tbuf[SZ_LINE]; XPACmd cmd, tcmd; XPAComm comm, tcomm; XPAClip clip, tclip; NS ns, tns; /* make sure we have something to do */ if( xpa == NULL ) return(-1); FPRINTF((stderr, "%s_XPAFree: freeing xpa struct\n", _sp)); /* remove this xpa from public availability */ if( xpa->type != NULL ) XPANSDel(xpa, NULL, NULL); /* free all sub-commands */ for(cmd=xpa->commands; cmd!=NULL; ){ tcmd = cmd->next; XPACmdDel(xpa, cmd); cmd = tcmd; } /* remove from list of xpas */ XPAListDel(&xpahead, xpa); /* close down listening socket */ if( xpa->fd >= 0 ) xclose(xpa->fd); /* perform method-specific cleanup */ switch(mtype){ #if HAVE_SYS_UN_H case XPA_UNIX: /* delete the unix socket files */ unlink(xpa->method); snprintf(tbuf, SZ_LINE, "%s_data", xpa->method); unlink(tbuf); break; #endif default: break; } /* free up space */ if( xpa->version ) xfree(xpa->version); if( xpa->type ) xfree(xpa->type); if( xpa->method ) xfree(xpa->method); if( xpa->xclass ) xfree(xpa->xclass); if( xpa->name ) xfree(xpa->name); if( xpa->help ) xfree(xpa->help); if( xpa->sendian ) xfree(xpa->sendian); /* call the select free routine for the listening socket and loop type. we use an indirect routine to avoid having to link Xt, Tcl, etc. */ if( xpa->seldel && xpa->selptr ){ (xpa->seldel)(xpa->selptr); xpa->selptr = NULL; } /* close communication channels */ for(comm=xpa->commhead; comm!=NULL; ){ tcomm = comm->next; CommFree(xpa, comm, 1); comm = tcomm; } /* free up clipboards */ for(clip=xpa->cliphead; clip!=NULL; ){ tclip = clip->next; ClipBoardFree(xpa, clip); clip = tclip; } /* close down the name server and all of the remotes for this xpa */ for(ns=xpa->nshead; ns!=NULL; ){ tns = ns->next; XPANSClose(xpa, ns); ns = tns; } /* free up record struct */ xfree((char *)xpa); return(0); } #if HAVE_ATEXIT /* *--------------------------------------------------------------------------- * * Routine: _XPAAtExit * * Purpose: XPA cleanup on exit * The main purpose of this routine is to try to delete the * unix socket files when an XPA server exits. * * Results: none * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC static void _XPAAtExit (void) #else static void _XPAAtExit() #endif { XPA xpa, txpa; static int done=0; if( !done ){ /* return if we were not initialized */ if( !atexitinit ) return; /* return if I am not the process who initialized (I'm a child) */ if( atexitinit != getpid() ) return; FPRINTF((stderr, "calling XPAAtExit\n")); for(xpa=xpahead; xpa!=NULL; ){ /* use temp in case we destroy structure in the cleanup */ txpa = xpa->next; _XPAFree(xpa); xpa = txpa; } /* platform-dependent cleanup */ xsocketcleanup(); /* done with cleanup */ done++; } } /* *--------------------------------------------------------------------------- * * Routine: XPAAtExit * * Purpose: set up XPA cleanup on exit * * Results: none * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPAAtExit (void) #else void XPAAtExit() #endif { if( !atexitinit ){ atexit(_XPAAtExit); atexitinit = getpid(); } } #endif /* *---------------------------------------------------------------------------- * * * Semi-Public Routines * (mainly used by xpaset and xpaget) * * *---------------------------------------------------------------------------- */ /* *--------------------------------------------------------------------------- * * Routine: _XPAValid * * Purpose: see if the xpa struct is valid * * Results: 1 on success, 0 for failure * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int _XPAValid (XPA head, XPA xpa, char *type) #else int _XPAValid(head, xpa, type) XPA head; XPA xpa; char *type; #endif { XPA cur; if( xpa == NULL ) return(0); for(cur=head; cur!=NULL; cur=cur->next){ if( (cur == xpa) && !strcspn(cur->type, type) ){ return(1); } } return(0); } /* *--------------------------------------------------------------------------- * * Routine: XPAValid * * Purpose: see if the xpa struct is valid * * Results: 1 on success, 0 for failure * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAValid (XPA xpa) #else int XPAValid(xpa) XPA xpa; #endif { if( _XPAValid(xpahead, xpa, XPA_ACLS) ) return(1); else return(0); } /* *---------------------------------------------------------------------------- * * Routine: XPAEndian * * Purpose: semi-public routine to return the endian-ness of this * machine * * Results: 0 if little endian, 1 if bigendian * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAEndian(void) #else int XPAEndian() #endif { union { long l; char c[sizeof (long)]; } u; u.l = 1; return(u.c[sizeof (long) - 1] == 1); } /* *---------------------------------------------------------------------------- * * Routine: XPAListHead * * Purpose: semi-public routine to return the head of the xpa list * * Results: XPA list pointer on success * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC XPA XPAListHead (void) #else XPA XPAListHead() #endif { return(xpahead); } /* *--------------------------------------------------------------------------- * * Routine: XPAListAdd * * Purpose: add a member of an xpa list * * Results: 1 on success, 0 for failure * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPAListAdd (XPA *head, XPA xpa) #else void XPAListAdd(head, xpa) XPA *head; XPA xpa; #endif { XPA cur; if( *head == NULL ){ *head = xpa; } else{ for(cur=*head; cur->next!=NULL; cur=cur->next) ; cur->next = xpa; } } /* *--------------------------------------------------------------------------- * * Routine: XPAListDel * * Purpose: remove a member of an xpa list * * Results: 1 on success, 0 for failure * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPAListDel (XPA *head, XPA xpa) #else void XPAListDel(head, xpa) XPA *head; XPA xpa; #endif { XPA cur; /* remove from list of xpas */ if( *head ){ if( *head == xpa ){ *head = xpa->next; } else{ for(cur=*head; cur!=NULL; cur=cur->next){ if( cur->next == xpa ){ cur->next = xpa->next; break; } } } } } /* *---------------------------------------------------------------------------- * * Routine: XPAActive * * Purpose: make the xpa, cmd and data fds active or inactive for select * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAActive (XPA xpa, XPAComm comm, int flag) #else int XPAActive(xpa, comm, flag) XPA xpa; XPAComm comm; int flag; #endif { int prev=0; /* sanity check */ if( !xpa ) return(0); switch(flag){ /* remove this xpa from the active list */ case 0: if( (xpa->fd >= 0) && (xpa->fd < FD_SETSIZE) ){ FPRINTF((stderr, "%sXPAActive: clearing fd %d\n", _sp, xpa->fd)); prev = activefds[xpa->fd]; activefds[xpa->fd] = 0; if( xpa->seloff && xpa->selptr ) (xpa->seloff)(xpa->selptr); } if( comm ){ if( (comm->cmdfd >= 0) && (comm->cmdfd < FD_SETSIZE) ){ activefds[comm->cmdfd] = 0; if( xpa->seloff && comm->selcptr ) (xpa->seloff)(comm->selcptr); } if( (comm->datafd >= 0) && (comm->datafd < FD_SETSIZE) ){ activefds[comm->datafd] = 0; if( xpa->seloff && comm->seldptr ) (xpa->seloff)(comm->seldptr); } } break; /* add this xpa/comm to the active list */ case 1: if( (xpa->fd >= 0) && (xpa->fd < FD_SETSIZE) ){ FPRINTF((stderr, "%sXPAActive: activating fd %d\n", _sp, xpa->fd)); prev = activefds[xpa->fd]; activefds[xpa->fd] = 1; if( xpa->selon && xpa->selptr ) (xpa->selon)(xpa->selptr); } if( comm ){ if( (comm->cmdfd >= 0) && (comm->cmdfd < FD_SETSIZE) ){ activefds[comm->cmdfd] = 1; if( xpa->selon && comm->selcptr ) (xpa->selon)(comm->selcptr); } if( (comm->datafd >= 0) && (comm->datafd < FD_SETSIZE) ){ activefds[comm->datafd] = 1; if( xpa->selon && comm->seldptr ) (xpa->selon)(comm->seldptr); } } break; default: break; } return(prev); } /* *---------------------------------------------------------------------------- * * Routine: XPAActiveFd * * Purpose: semi-public routine to return flag if fd is active * * Results: 1 is active, 0 is inactive * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAActiveFd (int fd) #else int XPAActiveFd(fd) int fd; #endif { if( (fd >= 0) && (fd < FD_SETSIZE) && (activefds[fd] > 0) ) return(1); else return(0); } /* *---------------------------------------------------------------------------- * * Routine: XPAAddSelect * * Purpose: add one or more xpa sockets to the select flags * * Return: number of xpas that were added to the select flags * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAAddSelect (XPA xpa, fd_set *readfdsptr) #else int XPAAddSelect(xpa, readfdsptr) XPA xpa; fd_set *readfdsptr; #endif { XPA cur; XPAComm comm; int got=0; /* better have some place to set the flags */ if( readfdsptr == NULL ) return(0); /* if a specific xpa was specified, just set its select flags */ if( xpa != NULL ){ if( XPAActiveFd(xpa->fd) ){ FD_SET(xpa->fd, readfdsptr); got++; /* note that we only select on coms if the main fd is active */ for(comm=xpa->commhead; comm!=NULL; comm=comm->next){ if( XPAActiveFd(comm->cmdfd) ){ FD_SET(comm->cmdfd, readfdsptr); got++; } if( XPAActiveFd(comm->datafd) && (comm->datafd != comm->cmdfd) ){ FD_SET(comm->datafd, readfdsptr); got++; } } } } /* otherwise set select flags for all xpas */ else{ for(cur=xpahead; cur!=NULL; cur=cur->next){ if( XPAActiveFd(cur->fd) ){ FPRINTF((stderr, "%sXPAAddSelect: adding fd %d\n", _sp, cur->fd)); FD_SET(cur->fd, readfdsptr); got++; /* note that we only select on coms if the main fd is active */ for(comm=cur->commhead; comm!=NULL; comm=comm->next){ if( XPAActiveFd(comm->cmdfd) ){ FPRINTF((stderr, "%sXPAAddSelect: adding cmdfd %d\n", _sp, comm->cmdfd)); FD_SET(comm->cmdfd, readfdsptr); got++; } if( XPAActiveFd(comm->datafd) && (comm->datafd != comm->cmdfd) ){ FPRINTF((stderr, "%sXPAAddSelect: adding datafd %d\n", _sp, comm->datafd)); FD_SET(comm->datafd, readfdsptr); got++; } } } } } return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPAProcessSelect * * Purpose: process xpas that have pending reads or writes * * Return: number of xpas processed * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAProcessSelect (fd_set *readfdsptr, int maxreq) #else int XPAProcessSelect(readfdsptr, maxreq) fd_set *readfdsptr; int maxreq; #endif { int got=0; XPA xpa; XPAComm comm; #ifdef OLD XPA txpa; XPAComm tcomm; #endif /* <= 0 means do all of them */ if( maxreq < 0 ){ maxreq = 0; } again: for(xpa=xpahead; xpa!=NULL; xpa=xpa->next){ /* handle command requests */ for(comm=xpa->commhead; comm!=NULL; comm=comm->next){ if( (comm->cmdfd >=0) && FD_ISSET(comm->cmdfd, readfdsptr) ){ FD_CLR(comm->cmdfd, readfdsptr); XPAHandler(xpa, comm->cmdfd); got++; if( maxreq && (got >= maxreq) ) return(got); goto again; } } /* handle data requests */ for(comm=xpa->commhead; comm!=NULL; comm=comm->next){ if( (comm->datafd >=0) && FD_ISSET(comm->datafd, readfdsptr) ){ FD_CLR(comm->datafd, readfdsptr); XPAHandler(xpa, comm->datafd); got++; if( maxreq && (got >= maxreq) ) return(got); goto again; } } /* handle new requests */ if( (xpa->fd >= 0) && FD_ISSET(xpa->fd, readfdsptr) ){ FD_CLR(xpa->fd, readfdsptr); XPAHandler(xpa, xpa->fd); got++; if( maxreq && (got >= maxreq) ) return(got); goto again; } } #ifdef OLD for(xpa=xpahead; xpa!=NULL; ){ txpa = xpa->next; /* handle command requests */ for(comm=xpa->commhead; comm!=NULL; ){ tcomm = comm->next; if( (comm->cmdfd >=0) && FD_ISSET(comm->cmdfd, readfdsptr) ){ FD_CLR(comm->cmdfd, readfdsptr); FPRINTF((stderr, "%sXPAProcessSelect: cmd %d\n", _sp, comm->cmdfd)); /* if we got an error on this xpa, skip processing rest of it */ if( XPAHandler(xpa, comm->cmdfd) != XPA_RTN_OK ){ goto nextxpa; } got++; /* if we freed this xpa, skip processing rest of it */ if( !_XPAValid(xpahead, xpa, XPA_ACLS) ){ goto nextxpa; } /* if we are have processed the max reqests, we are done */ if( maxreq && (got >= maxreq) ){ return(got); } } comm = tcomm; } /* handle data requests */ for(comm=xpa->commhead; comm!=NULL; ){ tcomm = comm->next; if( (comm->datafd >=0) && FD_ISSET(comm->datafd, readfdsptr) ){ FD_CLR(comm->datafd, readfdsptr); FPRINTF((stderr, "%sXPAProcessSelect: data %d\n", _sp, comm->datafd)); /* if we got an error on this xpa, skip processing rest of it */ if( XPAHandler(xpa, comm->datafd) != XPA_RTN_OK ){ goto nextxpa; } got++; /* if we freed this xpa, skip processing rest of it */ if( !_XPAValid(xpahead, xpa, XPA_ACLS) ){ goto nextxpa; } /* if we are have processed the max reqests, we are done */ if( maxreq && (got >= maxreq) ){ return(got); } } comm = tcomm; } /* handle new requests */ if( (xpa->fd >= 0) && FD_ISSET(xpa->fd, readfdsptr) ){ FD_CLR(xpa->fd, readfdsptr); FPRINTF((stderr, "%sXPAProcessSelect: xpa %d\n", _sp, xpa->fd)); /* if we got an error on this xpa, skip processing rest of it */ if( XPAHandler(xpa, xpa->fd) != XPA_RTN_OK ){ goto nextxpa; } got++; /* if we freed this xpa, skip processing rest of it */ if( !_XPAValid(xpahead, xpa, XPA_ACLS) ){ goto nextxpa; } /* if we are have processed the max reqests, we are done */ if( maxreq && (got >= maxreq) ){ return(got); } } nextxpa: /* must check to see if last xpa freed the next xpa */ if( _XPAValid(xpahead, txpa, XPA_ACLS) ) xpa = txpa; else break; } #endif FPRINTF((stderr, "%sXPAProcessSelect: returns %d\n", _sp, got)); return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPACloseData * * Purpose: close data fd if its not the cmd fd * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPACloseData (XPA xpa, XPAComm comm) #else void XPACloseData(xpa, comm) XPA xpa; XPAComm comm; #endif { /* we close the data channel if its not the command channel */ if( comm && (comm->datafd >=0) ){ if( comm->cmdfd != comm->datafd ){ FPRINTF((stderr, "%sXPACloseData: close fd %d for cmd fd %d\n", _sp, comm->datafd, comm->cmdfd)); /* remove from active */ if( comm->datafd < FD_SETSIZE ) activefds[comm->datafd] = 0; /* delete the comm data fd specially from a non-select event loop */ if( xpa && xpa->seldel && comm->seldptr ){ (xpa->seldel)(comm->seldptr); comm->seldptr = NULL; } /* close file */ xclose(comm->datafd); } /* reset data channel to impossible value */ comm->datafd = -1; } } /* *---------------------------------------------------------------------------- * * Routine: XPAHandler * * Purpose: handle one request for an xpaset or xpaget * * Return: 0 on success, xpa error code on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAHandler (XPA xpa, int fd) #else int XPAHandler(xpa, fd) XPA xpa; int fd; #endif { char tbuf[SZ_LINE]; char tbuf2[SZ_LINE]; char lbuf[SZ_LINE]; char cmd[SZ_LINE]; char target[SZ_LINE]; char method[SZ_LINE]; char ctmpl[SZ_LINE]; char ntmpl[SZ_LINE]; char *paramlist=NULL; char *acl; int save_ack; int tcmd; int got=0; int lp=0; int cmdfd=-1; unsigned short port=0; unsigned int ip=0; struct timeval tv; XPAComm comm=NULL, tcomm=NULL, xcomm=NULL, ocomm=NULL; XPA txpa=NULL; NS ns=NULL; fd_set readfds; FPRINTF((stderr, "%sXPAHandler: entering with fd %d\n", _sp, fd)); /* this is a defensive measure: we have to guard against an external loop calling the XPA handler with a bogus XPA record. This has been seen with the Tcl event loop. */ for(txpa=xpahead; txpa!=NULL; txpa=txpa->next){ if( txpa == xpa ) break; } if( txpa == NULL ){ FPRINTF((stderr, "%sXPAHandler: xpa record %p is not in list\n", _sp, xpa)); return(XPA_RTN_NOCMD); } /* this is a defensive measure: we have to ensure that there really * is a request read. It is possible that a user-defined select loop * might call us to handle a request that we had already handled * (i.e. we handle a request but can't reset someone else's select flags) */ FD_ZERO(&readfds); FD_SET(fd, &readfds); tv.tv_sec = 0; tv.tv_usec = 0; while( xselect(fd+1, &readfds, NULL, NULL, &tv) <=0 ){ if( xerrno == EINTR ) continue; FPRINTF((stderr, "%sXPAHandler: xpa fd %d is not ready\n", _sp, fd)); return(XPA_RTN_NOCMD); } /* if this is a first connection, we create a new comm channel and exit */ if( fd == xpa->fd ){ if( (comm = CommNew(xpa, -1, 0, 0, NULL, NULL)) == NULL ) return(XPA_RTN_NOCONN); /* Return to prevent xpa from finishing before other xpa's are started. Otherwise, each xpa's associated with a target template will be processed serially, which will defeat all the hard work done on the client side to send data to servers when they ask for it. */ else{ FPRINTF((stderr, "%sXPAHandler: returning after CommNew on fd %d\n", _sp, fd)); got = XPA_RTN_OK; goto end; } } /* look for a currently active comm channel: cmd or data */ for(comm=xpa->commhead; comm!= NULL; comm=comm->next){ if( (fd == comm->cmdfd) || (fd == comm->datafd) ){ break; } } /* make sure we have something */ if( comm == NULL ){ /* read extraenous message */ if( XPAGets(xpa, fd, tbuf, SZ_LINE, 1) >0 ){ got = XPA_RTN_UNCMD; FPRINTF((stderr, "%sXPAHandler: fd %d received extra message:\n%s\n", _sp, fd, tbuf)); } else{ FPRINTF((stderr, "%sXPAHandler: no active comm record for fd %d\n", _sp, fd)); got = XPA_RTN_NOCMD2; } goto error; } /* but don't recurse */ if( comm->status & XPA_STATUS_ACTIVE ){ FPRINTF((stderr, "%sXPAHandler: fd %d returning to avoid recursion\n", _sp, fd)); got = 0; goto end; } /* set current comm for this xpa */ ocomm = xpa->comm; xpa->comm = comm; /* no message sent yet */ comm->message = 0; /* data ready on data channel: go right to data handling section */ if( fd == comm->datafd ){ FPRINTF((stderr, "%sXPAHandler: jumping to cb for %d\n", _sp, fd)); goto cb; } /* cmd channel: we are processing a new command */ retry: /* reset line buffer pointer for parsing */ lp = 0; /* read next command */ if( XPAGets(xpa, comm->cmdfd, lbuf, SZ_LINE, stimeout) <=0 ){ FPRINTF((stderr, "%sXPAHandler: fd %d read EOF\n", _sp, comm->cmdfd)); got = XPA_RTN_OK; goto eof; } /* new-lines imply we entered telnet mode on local host */ else if( (*lbuf == '\n') || (*lbuf == '\r') || !strcmp(lbuf, "telnet") ){ if( (mtype == XPA_UNIX) || LOCALIP(comm->cmdip) ){ if( comm->telnet == 0 ) XPAPuts(xpa, comm->cmdfd, "Entering telnet mode ...\n", stimeout); comm->telnet = 1; comm->ack = 0; comm->datafd = comm->cmdfd; stimeout = -1; goto retry; } else{ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]); got = XPA_RTN_NOCMD; goto error; } } FPRINTF((stderr, "%sXPAHandler: fd %d read command: %s", _sp, comm->cmdfd, lbuf)); /* validate the command */ if( !word(lbuf, cmd, &lp) ){ FPRINTF((stderr, "%sXPAHandler: missing target for fd %d\n", _sp, comm->cmd)); XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]); got = XPA_RTN_NOCMD; goto error; } /* send help message (local support only) */ if( !strcmp(cmd, "help") ){ if( (mtype == XPA_UNIX) || LOCALIP(comm->cmdip) ){ XPAPuts(xpa, comm->cmdfd, "xpaset|xpaget|xpainfo|xpaaccess [switches] class:name [params]\n", stimeout); XPAPuts(xpa, comm->cmdfd, "switches:\n", stimeout); XPAPuts(xpa, comm->cmdfd, "\t-a\t\tclient wants to accept() data connection\n", stimeout); XPAPuts(xpa, comm->cmdfd, "\t-e \tclient endian-ness: big(b) or little(l)\n", stimeout); XPAPuts(xpa, comm->cmdfd, "\t-i id\t\tclient id string\n", stimeout); XPAPuts(xpa, comm->cmdfd, "\t-p \t\tfrom xpans (e.g., for proxy processing)\n", stimeout); XPAPuts(xpa, comm->cmdfd, "\t-n\t\tdon't ack back to client\n", stimeout); XPAPuts(xpa, comm->cmdfd, "\t-t\t\tenter telnet mode (local only)\n", stimeout); /* we must be in telnet mode */ comm->telnet = 1; comm->ack = 0; goto retry; } else{ XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]); got = XPA_RTN_NOCMD; goto error; } } /* determine which command was specified */ if( !strcmp(cmd, "xpaset") ) tcmd = XPA_SET; else if( !strcmp(cmd, "xpaget") ) tcmd = XPA_GET; else if( !strcmp(cmd, "xpainfo") ) tcmd = XPA_INFO; else if( !strcmp(cmd, "xpaaccess") ) tcmd = XPA_ACCESS; else if( !strcmp(cmd, "xpadata") ) tcmd = XPA_DATA; else if( !strcmp(cmd, "xpaaccept") ) tcmd = XPA_ACCEPT; else if( !strcmp(cmd, "xpanagle") || !strcmp(cmd, "XPA$OK") ) tcmd = XPA_NAGLE; else{ FPRINTF((stderr, "%sXPAHandler: unknown command '%s' for fd %d\n", _sp, cmd, comm->cmdfd)); XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]); got = XPA_RTN_NOCMD; goto error; } /* process switches */ while( word(lbuf, tbuf, &lp) ){ if( *tbuf != '-' ) break; if( !strcmp(tbuf, "-a") ){ comm->mode |= COMM_CONNECT; } else if( !strcmp(tbuf, "-e") ){ if( word(lbuf, tbuf, &lp) ){ if( *tbuf == 'b' ) comm->cendian = "big"; else if( *tbuf == 'l' ) comm->cendian = "little"; } else{ got = XPA_RTN_ILLCMD; goto error; } } else if( !strcmp(tbuf, "-f") ){ /* B.Schoenhammer@bit-field.de 2009-09-21 */ #if HAVE_MINGW32==0 if( !word(lbuf, tbuf, &lp) || (sscanf(tbuf, "%p", &xcomm) != 1) ){ #else if( !word(lbuf, tbuf, &lp) || (sscanf(tbuf, "%x", &xcomm) != 1) ){ #endif got = XPA_RTN_ILLCMD; goto done; } if( !word(lbuf, tbuf, &lp) || ((cmdfd = atoi(tbuf)) < 0) ){ got = XPA_RTN_ILLCMD; goto done; } } else if( !strcmp(tbuf, "-i") ){ if( word(lbuf, tbuf, &lp) ){ if( comm->id != NULL ) xfree(comm->id); comm->id = xstrdup(tbuf); } else{ XPAError(xpa, xpaMessbuf[XPA_RTN_ILLCMD]); got = XPA_RTN_ILLCMD; goto error; } } else if( !strcmp(tbuf, "-n") ){ comm->ack = 0; } else if( !strcmp(tbuf, "-p") ){ if( word(lbuf, tbuf, &lp) ){ if( comm->info != NULL ) xfree(comm->info); comm->info = xstrdup(tbuf); } else{ XPAError(xpa, xpaMessbuf[XPA_RTN_ILLCMD]); got = XPA_RTN_ILLCMD; goto error; } } else if( !strcmp(tbuf, "-t") ){ comm->telnet = 1; } else{ break; } } /* make sure we have some sort of id */ if( comm->id == NULL ){ comm->id = xstrdup("?"); } /* process nagle ack */ if( tcmd == XPA_NAGLE ){ /* if data is forthcoming, exit and wait for data */ if( comm->usebuf ){ got = XPA_RTN_OK; goto end; } /* go process callback */ else{ goto cb; } } /* connect (proxy) request: connect back to client */ if( tcmd == XPA_ACCEPT ){ FPRINTF((stderr, "%scmd is xpaaccept from client %d: %s", _sp, fd, lbuf)); /* syntax: xpaaccept ... */ lp = 0; if( !word(lbuf, tbuf, &lp) || strcmp(tbuf, "xpaaccept") || !word(lbuf, method, &lp) ){ got = -1; goto error; } if( (cmdfd=XPAProxyConnect(xpa, method, &ip, &port, tbuf)) <0 ){ FPRINTF((stderr, "%sXPAProxyConnect failed for: %d\n", _sp, fd)); got = -1; goto error; } if( (tcomm = CommNew(xpa, cmdfd, ip, port, method, NULL)) == NULL ){ got = XPA_RTN_NOCONN; goto error; } else{ /* now exit and wait for client to send command */ FPRINTF((stderr, "%sXPAHandler: fd %d exiting after proxy connect\n", _sp, tcomm->cmdfd)); got = XPA_RTN_OK; goto end; } } /* data request: find associated command request, and process the command */ if( tcmd == XPA_DATA ){ FPRINTF((stderr, "%sXPAHandler: processing data fd %d\n", _sp, comm->cmdfd)); /* find the cmd record with which this data is associated */ for(tcomm=xpa->commhead; tcomm!= NULL; tcomm=tcomm->next){ if( (tcomm == xcomm) && (tcomm->cmdfd == cmdfd) ){ break; } } /* if we found an associated command, copy in data info and process */ if( tcomm ){ /* fill in command comm */ tcomm->datafd = comm->cmdfd; tcomm->seldptr = comm->selcptr; if( tcomm->dataname ) xfree(tcomm->dataname); tcomm->dataname = xstrdup(comm->cmdname); /* done with data comm */ CommFree(xpa, comm, 0); /* reset comm pointers */ comm = tcomm; xpa->comm = comm; FPRINTF((stderr, "%sXPAHandler: found cmd fd %d for this data fd %d\n", _sp, comm->cmdfd, comm->datafd)); if( comm->cmd == XPA_SET ){ FPRINTF((stderr, "%sXPAHandler: data %d returning await xpaset\n", _sp, comm->datafd)); got = XPA_RTN_OK; goto end; } else{ FPRINTF((stderr, "%sXPAHandler: data %d going on to process data\n", _sp, comm->datafd)); goto cb; } } else{ FPRINTF((stderr, "%sXPAHandler: data fd has no corresponding cmd %d\n", _sp, comm->cmdfd)); XPAError(xpa, xpaMessbuf[XPA_RTN_ILLCMD]); got = XPA_RTN_ILLCMD; goto error; } } /* for a command, the first non-switch word we found was the target */ if( *tbuf == '\0' ){ FPRINTF((stderr, "%sXPAHandler: missing target for fd %d\n", _sp, comm->cmdfd)); XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]); got = XPA_RTN_NOCMD; goto error; } else{ strcpy(target, tbuf); } /* validate the target (except for xpans for use as proxy) */ if( strcmp(xpa->xclass, XPANS_CLASS) || strcmp(xpa->name, XPANS_NAME) ){ XPAParseName(target, ctmpl, ntmpl, SZ_LINE); if( (strcmp(ctmpl,"?") && !tmatch(xpa->xclass, ctmpl)) || (strcmp(ntmpl,"?") && !tmatch(xpa->name, ntmpl)) ){ got = XPA_RTN_NOTARG; goto error; } } if( comm->target != NULL ) xfree(comm->target); comm->target = xstrdup(target); /* the rest of the input string is paramlist */ paramlist = &(lbuf[lp]); nowhite(paramlist, paramlist); if( comm->paramlist != NULL ) xfree(comm->paramlist); comm->paramlist = xstrdup(paramlist); /* save command */ comm->cmd = tcmd; FPRINTF((stderr, "%sXPAHandler: processing command fd %d for target %s (%d)\n", _sp, comm->cmdfd, target, comm->cmd)); /* a reserved command can only be called from the same host as the server, or from local (unix) sockets */ lp = 0; if( XPACmdLookupReserved(xpa, comm->paramlist, &lp) ){ comm->mode |= COMM_RESERVED; } /* set up command-specific info */ switch(comm->cmd){ case XPA_SET: if( comm->mode & COMM_RESERVED ){ comm->usebuf = 1; comm->useacl = guseacl && (mtype != XPA_UNIX); acl = "s"; } else if( xpa->receive_callback ){ comm->usebuf = (xpa->receive_mode & XPA_MODE_BUF); comm->useacl = guseacl && (xpa->receive_mode & XPA_MODE_ACL) && (mtype != XPA_UNIX); acl = "s"; } else{ XPAError(xpa, xpaMessbuf[XPA_RTN_NOREC]); got = XPA_RTN_NOREC; goto error; } break; case XPA_GET: if( comm->mode & COMM_RESERVED ){ comm->usebuf = 1; comm->useacl = guseacl && (mtype != XPA_UNIX); acl = "g"; } else if( xpa->send_callback ){ comm->usebuf = (xpa->send_mode & XPA_MODE_BUF); comm->useacl = guseacl && (xpa->send_mode & XPA_MODE_ACL) && (mtype != XPA_UNIX); acl = "g"; } else{ XPAError(xpa, xpaMessbuf[XPA_RTN_NOSEND]); got = XPA_RTN_NOSEND; goto error; } break; case XPA_INFO: if( xpa->info_callback ){ comm->usebuf = 0; comm->useacl = guseacl && (xpa->info_mode & XPA_MODE_ACL) && (mtype != XPA_UNIX); acl = "i"; } else{ XPAError(xpa, xpaMessbuf[XPA_RTN_NOINFO]); got = XPA_RTN_NOINFO; goto error; } break; case XPA_ACCESS: comm->usebuf = 0; comm->useacl = guseacl && (mtype != XPA_UNIX); /* may as well check access mode as well */ snprintf(tbuf2, SZ_LINE, "%sa%s", _sp, comm->paramlist?comm->paramlist:""); acl = tbuf2; break; default: FPRINTF((stderr, "%sXPAHandler: invalid access control check for fd %d\n", _sp, comm->cmdfd)); XPAError(xpa, xpaMessbuf[XPA_RTN_NOCMD]); got = XPA_RTN_NOCMD; goto error; } /* perform access authentication */ if( comm->useacl ){ /* determine acl for this ip, if necessary */ if( comm->acl[comm->cmd] < 0 ){ comm->acl[comm->cmd] = XPAAclCheck(xpa, comm->cmdip, acl); } /* check acl */ if( comm->acl[comm->cmd] <= 0 ){ FPRINTF((stderr, "%sXPAHandler: fd %d FAILED acl check\n", _sp, comm->cmdfd)); XPAError(xpa, xpaMessbuf[XPA_RTN_NOAUTH]); got = XPA_RTN_NOAUTH; goto error; } FPRINTF((stderr, "%sXPAHandler: fd %d passed acl check\n", _sp, comm->cmdfd)); } /* for telnet mode, just use cmdfd for data */ if( comm->telnet ){ comm->datafd = comm->cmdfd; } /* for xpainfo and no ack, just go to the vallback */ else if( (comm->cmd == XPA_INFO) && !comm->ack ){ goto cb; } /* in general, we must tell the client how we will handle data */ else{ if( comm->usebuf ){ if( comm->mode & COMM_CONNECT ){ snprintf(lbuf, SZ_LINE, "%s XPA$DATA accept %p %d (%s:%s %s)\n", comm->id, comm, comm->cmdfd, xpa->xclass, xpa->name, xpa->method); } else{ snprintf(lbuf, SZ_LINE, "%s XPA$DATA connect %p %d (%s:%s %s)\n", comm->id, comm, comm->cmdfd, xpa->xclass, xpa->name, xpa->method); } } /* no data channel being set up */ else{ snprintf(lbuf, SZ_LINE, "%s XPA$NODATA (%s:%s %s)\n", comm->id, xpa->xclass, xpa->name, xpa->method); } FPRINTF((stderr, "%sXPAHandler: fd %d sends string: %s", _sp, comm->cmdfd, lbuf)); if( XPAPuts(xpa, comm->cmdfd, lbuf, stimeout) <= 0 ){ FPRINTF((stderr, "%sXPAHandler: fd %d couldn't send string: %s", _sp, comm->cmdfd, lbuf)); got = -1; goto error; } /* now exit and wait for nagle ack and for client to send data */ FPRINTF((stderr, "%sXPAHandler: fd %d exiting to wait for nagle or connect req\n", _sp, comm->cmdfd)); got = XPA_RTN_OK; goto end; } /* data channel complete (or no data): ready to execute the user callback */ cb: /* we are now active */ comm->status |= XPA_STATUS_ACTIVE; /* remove the current comm from the list of active fds, in case the server callback re-enters the event loop */ XPAActive(xpa, comm, 0); /* zero out buf and len, just to make sure (don't free buf, in case application is using previous) */ comm->buf = NULL; comm->len = 0; /* if we are not ack'ing after callback, do it now so client can exit */ /* but don't do this for xpainfo/noack */ if( !comm->ack && (comm->cmd != XPA_INFO) ){ FPRINTF((stderr, "%sXPAHandler: sending OK to non-acking client %d %d\n", _sp, comm->cmdfd, comm->datafd)); comm->ack = 1; XPAOK(xpa); comm->ack = 0; } /* process command */ switch(comm->cmd){ case XPA_GET: FPRINTF((stderr, "%sXPAHandler: processing xpaget for %d %d\n", _sp, comm->cmdfd, comm->datafd)); /* check for a reserved command */ if( comm->mode & COMM_RESERVED ){ got = XPASendCommands(xpa->send_data, xpa, comm->paramlist, &(comm->buf), &(comm->len)); } else{ got = (xpa->send_callback)(xpa->send_data, xpa, comm->paramlist, &(comm->buf), &(comm->len)); } if( (got == 0) && (comm->buf != NULL) && (comm->len > 0) ){ if( XPAPutBuf(xpa, comm->datafd, comm->buf, comm->len, ltimeout) < 0 ){ PERROR(("XPAHandler write buf")); XPAError(xpa, "XPA could not write data to client"); goto done; } } if( (xpa->send_mode & XPA_MODE_FREEBUF) && (comm->buf != NULL) ){ if( xpa->comm->myfree != NULL ){ if( xpa->comm->myfree_ptr != NULL ){ xpa->comm->myfree(xpa->comm->myfree_ptr); } else{ xpa->comm->myfree(comm->buf); } } else{ xfree(comm->buf); } } /* send client a message, unless its already been done */ if( !comm->message ){ if( got ) XPAError(xpa, "error detected in send callback routine"); else XPAOK(xpa); } FPRINTF((stderr, "%sXPAHandler: finished xpaget for %d %d\n", _sp, comm->cmdfd, comm->datafd)); break; case XPA_SET: FPRINTF((stderr, "%sXPAHandler: processing xpaset for %d %d\n", _sp, comm->cmdfd, comm->datafd)); /* check for a reserved command */ if( comm->mode & COMM_RESERVED ){ got=XPAReceiveCommands(xpa->receive_data, xpa, comm->paramlist, NULL, 0); } else{ /* fill buf if necessary */ if( (comm->datafd >= 0) && comm->usebuf && (xpa->receive_mode & XPA_MODE_FILLBUF) ){ if(XPAGetBuf(xpa, comm->datafd, &(comm->buf), &(comm->len), -1) <0){ XPAError(xpa, xpaMessbuf[XPA_RTN_NODATA]); FPRINTF((stderr, "%sXPAHandler: no data for XPAGetBuf on %d\n", _sp, comm->datafd)); got = -1; goto done; } else{ /* close the data fd now that we are done with it */ XPACloseData(xpa, comm); } } /* execute the receive callback */ got = (xpa->receive_callback)(xpa->receive_data, xpa, comm->paramlist, comm->buf, comm->len); } if( (xpa->receive_mode & XPA_MODE_FREEBUF) && (comm->buf != NULL) ){ if( xpa->comm->myfree != NULL ){ if( xpa->comm->myfree_ptr != NULL ){ xpa->comm->myfree(xpa->comm->myfree_ptr); } else{ xpa->comm->myfree(comm->buf); } } else{ xfree(comm->buf); } } /* send client an error message, unless its already been done */ if( !comm->message ){ if( got ){ XPAError(xpa, "error detected in receive callback routine"); } else{ XPAOK(xpa); } } FPRINTF((stderr, "%sXPAHandler: finished xpaset for %d %d\n", _sp, comm->cmdfd, comm->datafd)); break; case XPA_INFO: /* send OK before callback because we do not want the client waiting */ if( comm->ack ) XPAOK(xpa); save_ack = comm->ack; /* don't ever ack in callback */ comm->ack = 0; /* execute the info callback -- don't bother checking for errors */ (xpa->info_callback)(xpa->info_data, xpa, comm->paramlist); comm->ack = save_ack; break; case XPA_ACCESS: /* return errors -- that how we know if we have access */ comm->ack = 1; /* type is in paramlist */ if( comm->paramlist && *comm->paramlist ){ char *s; for(s=comm->paramlist; *s; s++){ switch(*s){ case 'g': if( !xpa->send_callback ){ XPAError(xpa, "no send callback (i.e., can't xpaget)"); goto done; } break; case 'i': if( !xpa->info_callback ){ XPAError(xpa, "no info callback (i.e., can't xpainfo)"); goto done; } break; case 's': if( !xpa->receive_callback ){ XPAError(xpa, "no receive callback (i.e., can't xpaset)"); goto done; } break; case 'a': break; default: XPAError(xpa, "unknown xpa access type"); goto done; } } /* if we got here, its OK */ XPAOK(xpa); } /* no type, anything is OK */ else XPAOK(xpa); break; default: /* something is really wrong if we have no command */ FPRINTF((stderr, "%sXPAHandler: invalid command #%d for fd %d\n", _sp, comm->cmd, comm->cmdfd)); got = XPA_RTN_NOCMD; goto error; break; } done: FPRINTF((stderr, "%sXPAHandler: finished processing %d with status %d\n", _sp, fd, got)); /* add xpa back to list of active ones */ XPAActive(xpa, comm, 1); /* finalize comm record */ if( comm ){ /* close data channel */ XPACloseData(xpa, comm); /* xpa is no longer active */ comm->status &= ~XPA_STATUS_ACTIVE; } /* join common code */ goto end; eof: /* on eof, free up comm */ if( comm ){ ns = comm->ns; CommFree(xpa, comm, 1); comm = NULL; if( ns && !ns->nproxy && !ns->nxpa ){ FPRINTF((stderr, "%sXPAHandler: closing ns %s\n", _sp, ns->name)); XPANSClose(xpa, ns); } } /* join common code */ goto end; error: FPRINTF((stderr, "%sXPAHandler ERROR: return code %d on %d\n", _sp, got, fd)); /* add xpa back to list of active ones (but not comm) */ XPAActive(xpa, comm, 1); /* xpa is no longer using this comm */ xpa->comm = NULL; /* don't accidentally close ns on error */ if( comm ){ if( comm->ns ) CommFree(xpa, comm, 0); else CommFree(xpa, comm, 1); comm = NULL; } /* join common code */ goto end; end: /* if a free was requested by the callback, do it now when its safe */ if( xpa->status & XPA_STATUS_FREE ){ XPAFree(xpa); } /* restore old value of comm */ else{ xpa->comm = ocomm; } /* return the status */ return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPAMode * * Purpose: parse the mode string and set flags * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPAMode (char *mode, int *flag, char *name, int mask, int def) #else void XPAMode(mode, flag, name, mask, def) char *mode; int *flag; char *name; int mask; int def; #endif { char tbuf[SZ_LINE]; char s[SZ_LINE]; /* keyword routine requires an input buffer that can be modified */ if( mode && *mode ){ strncpy(s, mode, SZ_LINE-1); s[SZ_LINE-1] = '\0'; } else goto error; /* look for the keyword= string */ if( keyword(s, name, tbuf, SZ_LINE) ){ if( istrue(tbuf) ) *flag |= mask; else *flag &= ~mask; return; } else goto error; error: if( def ) *flag |= mask; else *flag &= ~mask; } /* *---------------------------------------------------------------------------- * * Routine: XPAShortTimeout * * Purpose: return short (select, gets) timeout value * * Return: timeout in seconds * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAShortTimeout (void) #else int XPAShortTimeout() #endif { return(stimeout); } /* *---------------------------------------------------------------------------- * * Routine: XPALongTimeout * * Purpose: return long (fillbuf) timeout value * * Return: timeout in seconds * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPALongTimeout (void) #else int XPALongTimeout() #endif { return(ltimeout); } /* *---------------------------------------------------------------------------- * * Routine: XPAReceiveLTimeout * * Purpose: modify long timeout value for this process * * Returns: 0 for success, -1 for failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAReceiveLTimeout (void *client_data, void *call_data, char *paramlist, char *buf, size_t len) #else int XPAReceiveLTimeout(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]; char *s; if( paramlist && *paramlist ){ strncpy(tbuf, paramlist, SZ_LINE-1); tbuf[SZ_LINE-1] = '\0'; nocr(tbuf); culc(tbuf); if( !strcmp(tbuf, "reset") ){ ltimeout = XPA_LONG_TIMEOUT; if( (s=(char *)getenv("XPA_LONG_TIMEOUT")) != NULL ) ltimeout = atoi(s); } else{ ltimeout = atoi(tbuf); } return(0); } else{ XPAError(xpa, "missing long timeout value"); return(-1); } } /* *---------------------------------------------------------------------------- * * Routine: XPASendLTimeout * * Purpose: return the long timeout for this process * * Returns: 0 for success, -1 for failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPASendLTimeout (void *client_data, void *call_data, char *paramlist, char **buf, size_t *len) #else int XPASendLTimeout(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]; snprintf(tbuf, SZ_LINE, "%d\n", ltimeout); send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0); return(0); } /* *---------------------------------------------------------------------------- * * Routine: XPAReceiveSTimeout * * Purpose: modify short timeout value for this process * * Returns: 0 for success, -1 for failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAReceiveSTimeout (void *client_data, void *call_data, char *paramlist, char *buf, size_t len) #else int XPAReceiveSTimeout(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]; char *s; if( paramlist && *paramlist ){ strncpy(tbuf, paramlist, SZ_LINE-1); tbuf[SZ_LINE-1] = '\0'; nocr(tbuf); culc(tbuf); if( !strcmp(tbuf, "reset") ){ stimeout = XPA_SHORT_TIMEOUT; if( (s=(char *)getenv("XPA_SHORT_TIMEOUT")) != NULL ) stimeout = atoi(s); } else{ stimeout = atoi(tbuf); } return(0); } else{ XPAError(xpa, "missing short timeout value"); return(-1); } } /* *---------------------------------------------------------------------------- * * Routine: XPASendSTimeout * * Purpose: return the short timeout for this process * * Returns: 0 for success, -1 for failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPASendSTimeout (void *client_data, void *call_data, char *paramlist, char **buf, size_t *len) #else int XPASendSTimeout(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]; snprintf(tbuf, SZ_LINE, "%d\n", stimeout); send(xpa_datafd(xpa), tbuf, strlen(tbuf), 0); return(0); } /* *---------------------------------------------------------------------------- * * Routine: XPAVerbosity * * Purpose: return verbosity value * * Return: verbosity value (0, 1, 2) * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAVerbosity (void) #else int XPAVerbosity() #endif { return(verbosity); } /* *---------------------------------------------------------------------------- * * Routine: XPASigusr1 * * Purpose: return flag for using sigusr1 * * Return: 0 or 1 * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPASigusr1 (void) #else int XPASigusr1() #endif { return(sigusr1); } /* *---------------------------------------------------------------------------- * * Routine: XPAInitEnv * * Purpose: initialize the xpa environment * * Return: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPAInitEnv (void) #else void XPAInitEnv() #endif { char *s; if( !tmpdir ){ /* determine the communication method */ mtype=XPAMethod(NULL); /* enable access controls and port management */ if( mtype != XPA_UNIX ){ XPAAclNew(NULL, 0); XPAPortNew(NULL, 0); } /* set short (select,gets) timeout, if necessary */ if( (s=(char *)getenv("XPA_SHORT_TIMEOUT")) != NULL ) stimeout = atoi(s); /* set long (fillbuf) timeout, if necessary */ if( (s=(char *)getenv("XPA_LONG_TIMEOUT")) != NULL ) ltimeout = atoi(s); /* set xpans connect timeout, if necessary */ if( (s=(char *)getenv("XPA_CONNECT_TIMEOUT")) != NULL ) ctimeout = atoi(s); /* set nsdelay timeout, if necessary */ if( (s=(char *)getenv("XPA_NSDELAY")) != NULL ) nsdelay = atoi(s); /* set retries, if necessary */ if( (s=(char *)getenv("XPA_RETRIES")) != NULL ) retries = atoi(s); /* set verbosity level, if necessary */ if( (s=(char *)getenv("XPA_VERBOSITY")) != NULL ){ if( istrue(s) ) verbosity = XPA_VERBOSITY; else if( isfalse(s) ) verbosity = 0; else verbosity = atoi(s); if( verbosity < 0 ) verbosity = 0; } /* check for acl enable/disable */ if( (s=(char *)getenv("XPA_ACL")) != NULL ) guseacl = istrue(s); /* check for timestamp on errors */ if( (s=(char *)getenv("XPA_TIMESTAMP_ERRORS")) != NULL ) etimestamp = istrue(s); /* check for xpans register flag */ if( (s=(char *)getenv("XPA_NSREGISTER")) != NULL ) nsregister = istrue(s); /* check for use of siguser1 */ if( (s=(char *)getenv("XPA_SIGUSR1")) != NULL ) sigusr1 = istrue(s); /* check for version checking */ if( (s=(char *)getenv("XPA_VERSIONCHECK")) != NULL ){ if( istrue(s) ) vercheck = 1; else if( isfalse(s) ) vercheck = 0; else vercheck = atoi(s); } /* check for io loop calling xpa */ if( (s=(char *)getenv("XPA_IOCALLSXPA")) != NULL ){ if( istrue(s) ){ XPAIOCallsXPA(1); } else if( isfalse(s) ){ XPAIOCallsXPA(0); } } /* make sure we have a temp dir */ if( tmpdir == NULL ){ if( (s=(char *)getenv("XPA_TMPDIR")) != NULL ) tmpdir = xstrdup(s); else if( (s=(char *)getenv("TMPDIR")) != NULL ) tmpdir = xstrdup(s); else if( (s=(char *)getenv("TMP")) != NULL ) tmpdir = xstrdup(s); else tmpdir = xstrdup(XPA_TMPDIR); } /* create directory, if necessary */ xmkdir(tmpdir, 0777); xchmod(tmpdir, 0777); #if HAVE_MINGW32==0 /* Disable SIGPIPE so we do not die if the client dies. * Rather, we will get an EOF on reading or writing. */ xsignal_sigpipe(); #endif /* platform-dependent startup */ xsocketstartup(); } } /* *---------------------------------------------------------------------------- * * Routine: XPANSAdd * * Purpose: add this XPA to the name service * * Returns: 0 on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPANSAdd (XPA xpa, char *host, char *mode) #else int XPANSAdd(xpa, host, mode) XPA xpa; char *host; char *mode; #endif { char username[SZ_LINE]; char tbuf[SZ_LINE]; char xmode[SZ_LINE]; char *cmd="add"; char *s; NS ns; struct passwd *pw; /* sanity check */ if( !xpa ) return(0); /* handle special case of the name server itself -- its a known entry */ if( !strcmp(xpa->name, XPANS_NAME) ){ return(0); } /* look for the proxy= string */ if( mode ){ strncpy(xmode, mode, SZ_LINE-1); xmode[SZ_LINE-1] = '\0'; if( keyword(xmode, "proxy", tbuf, SZ_LINE) && istrue(tbuf) ){ cmd="addproxy"; } } /* open a connection to the name service */ if( (ns=XPANSOpen(xpa, host, 1)) != NULL ){ /* get the user name, from the environment */ if( (s=(char *)getenv("XPA_LOGNAME")) != NULL ) strncpy(username, s, SZ_LINE-1); else if( (s=(char *)getenv("LOGNAME")) != NULL ) strncpy(username, s, SZ_LINE-1); #if HAVE_MINGW32==0 /* this is a last resort */ else if( (pw=getpwuid(geteuid())) ) strncpy(username, pw->pw_name, SZ_LINE-1); #endif /* if nothing good has happened, make it "unknown" */ if( *username == '\0' ) strcpy(username, "unknown"); /* null-terminate string */ username[SZ_LINE-1] = '\0'; /* write the command to add this xpa */ snprintf(tbuf, SZ_LINE, "%s %s %s:%s %s %s\n", cmd, xpa->method, xpa->xclass, xpa->name, xpa->type, username); if( XPAPuts(xpa, ns->fd, tbuf, stimeout) < 0 ){ return(-1); } /* get result */ if( XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0 ){ if( !strncmp(tbuf, "XPA$OK", 6) ){ /* for proxy, we now must listen for xpa requests on this ns */ if( !strcmp(cmd, "addproxy") && xpa ){ if( CommNew(xpa, ns->fd, ns->ip, ns->port, ns->name, ns) ){ /* one more proxy is using this name server */ ns->nproxy += 1; } } else{ /* one more access point is using this name server */ ns->nxpa += 1; } return(0); } else if( !strncmp(tbuf, "XPA$EXISTS", 10) ) return(0); else return(-1); } else{ return(-1); } } else{ return(-1); } } /* *---------------------------------------------------------------------------- * * Routine: XPANSDel * * Purpose: remove public knowledge of this XPA from the name service * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPANSDel (XPA xpa, char *host, char *mode) #else int XPANSDel(xpa, host, mode) XPA xpa; char *host; char *mode; #endif { int got=0; char tbuf[SZ_LINE]; char xmode[SZ_LINE]; char *cmd="del"; NS ns; /* sanity check */ if( !xpa ) return(0); /* handle special case of the name server itself -- its a known entry */ if( xpa->name && !strcmp(xpa->name, XPANS_NAME) ){ return(0); } /* if there is no method, just return */ if( (xpa->method == NULL) || (*xpa->method == '\0') ){ return(0); } /* look for the proxy= string */ if( mode ){ strncpy(xmode, mode, SZ_LINE-1); xmode[SZ_LINE-1] = '\0'; if( keyword(xmode, "proxy", tbuf, SZ_LINE) && istrue(tbuf) ){ cmd="delproxy"; } } /* open a connection to the name service */ if( (ns=XPANSOpen(xpa, host, -1)) != NULL ){ /* write the command to delete this xpa */ snprintf(tbuf, SZ_LINE, "%s %s\n", cmd, xpa->method); XPAPuts(xpa, ns->fd, tbuf, stimeout); /* get result */ if( XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) >0 ){ if( !strncmp(tbuf, "XPA$OK", 6) ){ /* one less access point is using this name server */ ns->nxpa -= 1; /* if there are no more access points using this xpans, close it */ if( !ns->nxpa && !ns->nproxy ){ XPANSClose(xpa, ns); } } else{ got = -1; } } else{ got = -1; } } else got = -1; return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPAVersionCheck * * Purpose: check our version vs. xpans version * * Returns: -1,0,1 for our<=>xpans * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAVersionCheck (char *serv, char *nsv) #else int XPAVersionCheck(serv, nsv) char *serv; char *nsv; #endif { int i; int ip1=0; int ip2=0; int v1=0; int v2=0; int got=0; int vsize=2; char s1[SZ_LINE]; char s2[SZ_LINE]; /* return if not checking version */ if( vercheck <=0 ) return(0); /* if either does not exist, its a mismatch */ if( !word(serv, s1, &ip1) || !word(nsv, s2, &ip2) ) return(1); /* if strings are equal, versions are equal */ if( !strcasecmp(s1, s2) ) return(0); /* format for version is maj.min.patch[be]beta */ newdtable(".be"); /* we check only the major and minor version for incompatibilities */ for(i=0; i v2 ){ got = 1; break; } if( v1 < v2 ){ got = -1; break; } } freedtable(); return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPAVersionWarn * * Purpose: warn about mismatched versions * * Returns: NONE * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPAVersionWarn (char *serv, char *nsv) #else void XPAVersionWarn(serv, nsv) char *serv; char *nsv; #endif { /* return if not checking version */ if( vercheck <=0 ) return; /* output warning */ fprintf(stderr, "XPA$WARNING: version mismatch detected between XPA-enabled server (%s)\n", serv?serv:"unknown"); fprintf(stderr, "and xpans (%s).", nsv?nsv:"unknown"); fprintf(stderr, " You probably will get bad results.\n"); fprintf(stderr, "Please consider updating XPA to match the XPA-enabled server you are running.\n"); /* we did it */ vercheck--; } /* *---------------------------------------------------------------------------- * * * Public Routines * * *---------------------------------------------------------------------------- */ /* *---------------------------------------------------------------------------- * * Routine: XPAMethod * * Purpose: return communication method type * * Returns: XPA__INET, XPA_UNIX * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAMethod (char *method) #else int XPAMethod(method) char *method; #endif { char *s1; /* if no method is passed, we return the default method type */ if( method == NULL ){ if( mtype == 0 ){ s1 = (char *)getenv("XPA_METHOD"); if( s1 == NULL ) mtype = XPA_INET; else if( !strcasecmp(s1, "inet") ) mtype = XPA_INET; else if( !strcasecmp(s1, "localhost") ){ mtype = XPA_INET; use_localhost = 1; } else if( !strcasecmp(s1, "unix") ){ #if HAVE_SYS_UN_H mtype = XPA_UNIX; #else mtype = XPA_INET; use_localhost = 1; #endif } else if( !strcasecmp(s1, "local") ){ #if HAVE_SYS_UN_H mtype = XPA_UNIX; #else mtype = XPA_INET; use_localhost = 1; #endif } else mtype = XPA_INET; } return(mtype); } /* otherwise, we analyze the input method to get the type */ else{ /* inet is ip:port, else unix filename */ if( strchr(method, ':') != NULL ) return(XPA_INET); else #if HAVE_SYS_UN_H return(XPA_UNIX); #else return(XPA_INET); #endif } } /* *---------------------------------------------------------------------------- * * Routine: XPANSMethod * * Purpose: return string containing name server method * * Returns: name server method string * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC char * XPANSMethod (char *host, int flag) #else char *XPANSMethod(host, flag) char *host; int flag; #endif { char *s, *t; char tbuf[SZ_LINE]; int i; int ip; int port; unsigned int bip; unsigned short bport; switch( XPAMethod(host) ){ case XPA_INET: if( (host != NULL) && (*host != '\0') ) strncpy(nsmethod, host, SZ_LINE-1); else if( (s=(char *)getenv("XPA_NSINET")) != NULL ) strncpy(nsmethod, s, SZ_LINE-1); else strncpy(nsmethod, XPA_NSINET, SZ_LINE-1); /* always null-terminate */ nsmethod[SZ_LINE-1] = '\0'; /* if flag, we want the XPA access ip and port, not the communication channel between xpa servers and the name service */ if( flag ){ if( (s=strrchr(nsmethod, ':')) != NULL ){ /* this is where we will overwrite the port */ t = s+1; /* get base port for default */ XPAParseIpPort(nsmethod, &bip, &bport); newdtable(","); for(ip=0, i=0; i<=flag; i++){ if( !word(t, tbuf, &ip) ){ *tbuf = '\0'; break; } } freedtable(); if( *tbuf ) port = atoi(tbuf); else port = bport + flag; snprintf(t, SZ_LINE, "%d", port); } } break; #if HAVE_SYS_UN_H case XPA_UNIX: if( host != NULL ) strncpy(nsmethod, host, SZ_LINE-1); else if( (s=(char *)getenv("XPA_NSUNIX")) != NULL ) strncpy(nsmethod, s, SZ_LINE-1); else snprintf(nsmethod, SZ_LINE, "%s/%s", tmpdir, XPA_NSUNIX); /* always null-terminate */ nsmethod[SZ_LINE-1] = '\0'; /* if flag is set, we are getting the XPA access file, not the socket file and we have to change the name slightly */ if( flag ){ /* replace the ending, if possible */ s = strrchr(nsmethod, '.'); t = strrchr(nsmethod, '/'); if( (s != NULL) && (s > t) ) *s = '\0'; snprintf(tbuf, SZ_LINE, ".xpa-%d", flag); strcat(nsmethod, tbuf); } break; #endif default: if( (s=(char *)getenv("XPA_NSINET")) != NULL ) strncpy(nsmethod, s, SZ_LINE-1); else strncpy(nsmethod, XPA_NSINET, SZ_LINE-1); /* always null-terminate */ nsmethod[SZ_LINE-1] = '\0'; break; } /* return the static method string */ return(nsmethod); } /* *---------------------------------------------------------------------------- * * Routine: XPAParseName * * Purpose: split the xpaname into a class and name string * * Results: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPAParseName (char *xpaname, char *xclass, char *name, int len) #else void XPAParseName(xpaname, xclass, name, len) char *xpaname; char *xclass; char *name; int len; #endif { char *s; char *t; char *cptr=NULL; char *nptr=NULL; /* if nothing is passed to us, allow everything */ if( (xpaname == NULL) || (*xpaname == '\0') ){ strncpy(xclass, "*", len-1); strncpy(name, "*", len-1); return; } /* split the xpaname into class and name */ s = xstrdup(xpaname); if( (t=(char *)strchr(s, ':')) != NULL ){ cptr = s; *t = '\0'; nptr = t+1; } else{ nptr = s; cptr = "*"; } if( *cptr == '\0' ) cptr = "*"; if( *nptr == '\0' ) nptr = "*"; strncpy(xclass, cptr, len-1); strncpy(name, nptr, len-1); xfree(s); } /* *---------------------------------------------------------------------------- * * Routine: XPAParseIpPort * * Purpose: split the host into ip and port * * Results: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAParseIpPort (char *host, unsigned int *ip, unsigned short *port) #else int XPAParseIpPort(host, ip, port) char *host; unsigned int *ip; unsigned short *port; #endif { char *s1, *s2, *s3, *p1, *p2; int got; /* make sure we have something to work with */ if( (host == NULL) || (*host == '\0') ) return(0); /* parse up the string and look for a port number (which is essential) */ s1 = xstrdup(host); /* if we have a ",", null it out since what comes after is aux info */ if( (p1 = (char *)strchr(s1, ',')) != NULL ){ *p1 = '\0'; } /* if we have a ":", we will null it out (so that what comes before is the host name) and bump past it to point to the port */ if( (p1 = (char *)strchr(s1, ':')) == NULL ){ /* there is no ":", so the whole string is the port */ p1 = s1; s2 = NULL; } else { /* null out ':' and bump port pointer */ *p1 = '\0'; p1++; s2 = s1; } /* get port */ p2 = NULL; if( p1 && !strcmp(p1, "$port") ) *port = XPA_NSPORT; /* NB: port number might be followed by other stuff */ else *port = (unsigned short)strtol(p1, &p2, 0); /* check for bad port number -- we lose */ if( *port <=0 || (p1 == p2) || (p2 && (*p2 != '\0')) ){ *ip = 0; *port = 0; got = 0; goto done; } /* get ip */ if( s2 && *s2 ){ /* see if this already is a hex address in network byte order */ *ip = strtoul16(s2, &s3); if( *s3 == '\0' ){ got = 1; goto done; } } /* not hex or proxy -- convert ip string to an ip address */ if( (*ip = gethostip(s2)) == 0 ){ *port = 0; got = 0; } else{ got = 1; } done: xfree(s1); return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPAParseUnixSocket * * Purpose: see if host is actually a unix socket file * * Results: 1 if its a socket, 0 otherwise * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAParseUnixSocket (char *host) #else int XPAParseUnixSocket(host) char *host; #endif { struct stat buf; /* see if its a file in the right directory */ if( !strncmp(host, tmpdir, strlen(tmpdir)) && !stat(host, &buf) ){ return(1); } else{ return(0); } } /* *---------------------------------------------------------------------------- * * Routine: XPAOK * * Purpose: send an XPA OK message to the client * * Returns: 0 on success, -1 on error * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAOK (XPA xpa) #else int XPAOK(xpa) XPA xpa; #endif { int len; int status=0; char tbuf[SZ_LINE]; /* make sure we have a valid struct */ if( (xpa == NULL) || (xpa_cmdfd(xpa) <0) ) return(-1); /* send message, if necessary */ if( !(xpa_status(xpa) & XPA_STATUS_ACTIVE) || (xpa_ack(xpa) == 1) ){ snprintf(tbuf, SZ_LINE, "%s XPA$OK (%s:%s %s)\n", xpa_id(xpa), xpa_class(xpa), xpa_name(xpa), xpa_method(xpa)); len = strlen(tbuf); if( XPAPuts(xpa, xpa_cmdfd(xpa), tbuf, stimeout) != len ){ status = -1; } } /* flag that there was a message sent for this xpa */ xpa->comm->message = 1; /* return status */ return(status); } /* *---------------------------------------------------------------------------- * * Routine: XPATimestamp * * Purpose: generate string with current date/time * * Returns: time string (in static buffer) * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC char * XPATimestamp(void) #else char *XPATimestamp() #endif { time_t t; struct tm *lt; *ctimebuf = '\0'; if( etimestamp ){ if( (t = time(NULL)) != (time_t)-1 ){ if( (lt = localtime(&t)) != NULL ){ snprintf(ctimebuf, SZ_LINE, " %02d/%02d/%d:%d:%d:%d", lt->tm_mday, lt->tm_mon+1, lt->tm_year+1900, lt->tm_hour, lt->tm_min, lt->tm_sec); } } } return ctimebuf; } /* *---------------------------------------------------------------------------- * * Routine: XPAError * * Purpose: send an XPA error message to the client * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAError (XPA xpa, char *s) #else int XPAError(xpa, s) XPA xpa; char *s; #endif { int status=0; int ip=0; char tbuf[SZ_LINE]; char *t; char *u; /* make sure we have a valid struct */ if( (xpa == NULL) || (xpa_cmdfd(xpa) <0) ) return(-1); /* send message, if necessary */ if( !(xpa_status(xpa) & XPA_STATUS_ACTIVE) || (xpa_ack(xpa) == 1) ){ t = xstrdup(s); /* get rid of CR in message -- we add one at the end */ nowhite(t, t); if( !strncmp(t, "XPA$", 4) ) word(t, tbuf, &ip); u = (char *)xcalloc(strlen(t)+SZ_LINE, sizeof(char)); /* package up and write the message */ snprintf(u, SZ_LINE, "%s XPA$ERROR %s (%s:%s %s%s)\n", xpa_id(xpa), &t[ip], xpa_class(xpa), xpa_name(xpa), xpa_method(xpa), XPATimestamp()); if( XPAPuts(xpa, xpa_cmdfd(xpa), u, stimeout) != (int)strlen(u) ){ status = -1; } if( t ) xfree(t); if( u ) xfree(u); } /* flag that there was a message sent for this xpa */ xpa->comm->message = 1; /* return status */ return(status); } /* *---------------------------------------------------------------------------- * * Routine: XPAMessage * * Purpose: send an XPA message to the client * * Returns: 0 on success, -1 on error * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAMessage (XPA xpa, char *s) #else int XPAMessage(xpa, s) XPA xpa; char *s; #endif { int status=0; int ip=0; char tbuf[SZ_LINE]; char *t; char *u; /* make sure we have a valid struct */ if( (xpa == NULL) || (xpa_cmdfd(xpa) <0) ) return(-1); /* send message, if necessary */ if( !(xpa_status(xpa) & XPA_STATUS_ACTIVE) || (xpa_ack(xpa) == 1) ){ t = xstrdup(s); /* get rid of CR in message -- we add one at the end */ nowhite(t, t); if( !strncmp(t, "XPA$", 4) ) word(t, tbuf, &ip); u = (char *)xcalloc(strlen(t)+SZ_LINE, sizeof(char)); /* package up and write the message */ snprintf(u, SZ_LINE, "%s XPA$MESSAGE %s (%s:%s %s%s)\n", xpa_id(xpa), &t[ip], xpa_class(xpa), xpa_name(xpa), xpa_method(xpa), XPATimestamp()); if( XPAPuts(xpa, xpa_cmdfd(xpa), u, stimeout) != (int)strlen(u) ){ status = -1; } if( t ) xfree(t); if( u ) xfree(u); } /* flag that there was a message sent for this xpa */ xpa->comm->message = 1; /* return status */ return(status); } /* *---------------------------------------------------------------------------- * * Routine: XPAArgvParamlist * * Purpose: generate a paramlist string from an argv * * Results: allocated paramlist string * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC char * XPAArgvParamlist (int argc, char **argv, int start) #else char *XPAArgvParamlist(argc, argv, start) int argc; char **argv; int start; #endif { int plen; int i; char *paramlist; /* get length of paramlist */ for(plen=0, i=start; ifd, tbuf, stimeout); /* read matches from the name server */ while( 1 ){ if( XPAGets(xpa, ns->fd, tbuf, SZ_LINE, stimeout) <=0 ){ FPRINTF((stderr, "%sXPANSLookup: unexpected EOF from xpans\n", _sp)); break; } FPRINTF((stderr, "%sXPANSLookup: receiving %s", _sp, tbuf)); /* XPA$ signals end of input */ if( !strncmp(tbuf, "XPA$", 4) ) break; /* otherwise scan next line */ if( sscanf(tbuf, "%s %s %s %s %s %s\n", xclass, name, type, method, user, info) != EOF ){ /* make sure this entry is not in the current process (i.e., we can't ever xpa ourselves!) */ for(own=0, txpa=xpahead; txpa!=NULL; txpa=txpa->next){ if( !strcmp(txpa->xclass, xclass) && !strcmp(txpa->name, name) && !strcmp(txpa->method, method) ){ own = 1; break; } } /* if this xpa is in the current process, skip it */ if( own ) continue; /* make sure we have enough space */ if( got >= nentry ){ nentry *= 2; *xclasses = (char **)xrealloc(*xclasses, nentry * sizeof(char *)); *names = (char **)xrealloc(*names, nentry * sizeof(char *)); *methods = (char **)xrealloc(*methods, nentry * sizeof(char *)); *infos = (char **)xrealloc(*infos, nentry * sizeof(char *)); } /* add this entry to the list */ (*xclasses)[got] = xstrdup(xclass); (*names)[got] = xstrdup(name); (*methods)[got] = xstrdup(method); (*infos)[got] = xstrdup(info); got++; } } } /* if we did not add to an xpa record, close up here */ if( xpa == NULL ){ XPANSClose(NULL, ns); } } /* reallocate the exact number of buffers we have */ if( got > 0 ){ *xclasses = (char **)xrealloc(*xclasses, got * sizeof(char *)); *names = (char **)xrealloc(*names, got * sizeof(char *)); *methods = (char **)xrealloc(*methods, got * sizeof(char *)); *infos = (char **)xrealloc(*infos, got * sizeof(char *)); } else{ xfree(*xclasses); xfree(*names); xfree(*methods); xfree(*infos); } FPRINTF((stderr, "%sXPANSLookup: found %d entries\n", _sp, got)); return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPANSClose * * Purpose: close connection to the name service * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPANSClose (XPA xpa, NS ns) #else int XPANSClose(xpa, ns) XPA xpa; NS ns; #endif { NS cur; XPAComm comm, tcomm; if( !ns ) return(-1); /* remove from xpa list */ if( xpa ){ if( xpa->nshead ){ if( xpa->nshead == ns ){ xpa->nshead = ns->next; } else{ for(cur=xpa->nshead; cur!=NULL; cur=cur->next){ if( cur->next == ns ){ cur->next = ns->next; break; } } } } /* close comms associated with this ns */ for(comm=xpa->commhead; comm!=NULL; ){ tcomm = comm->next; if( comm->ns == ns ){ CommFree(xpa, comm, 0); } comm = tcomm; } } /* close socket */ if( ns->fd >=0 ){ xclose(ns->fd); } /* free up space */ if( ns->method) xfree(ns->method); if( ns->host ) xfree(ns->host); if( ns->name ) xfree(ns->name); if( ns ) xfree(ns); return(0); } /* *---------------------------------------------------------------------------- * * Routine: XPANSKeepAlive * * Purpose: send a 1-byte keep-alive packet to each name server * * Returns: -1 on error, else 1 * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPANSKeepAlive (XPA xpa, int type) #else int XPANSKeepAlive(xpa, type) XPA xpa; int type; #endif { NS ns; int got=0; /* sanity check */ if( !xpa ) return(-1); /* use default type, if none specified */ if( !type ) type=DEF_KA_TYPE; /* send keep-alive to deserving xpans instances */ for(ns=xpa->nshead; ns!=NULL; ns=ns->next){ if( ((type&1) && (ns->nxpa>0)) || ((type&2) && (ns->nproxy>0)) ){ FPRINTF((stderr, "%sXPANSKeepAlive: sending ka to %d\n", _sp, ns->fd)); #if USE_KA_OOB /* send as out of band data to avoid mixing with normal data stream */ got = send(ns->fd, "\n", 1, MSG_OOB); #else /* cisco routers can clear the URG flag by default, so use in-band */ got = send(ns->fd, "\n", 1, 0); #endif } } return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPANew * * Purpose: add a new xpa name to a process to allow external * process to read/write data associated with this name. * * If the send callback is defined, it can send to an external process. * If the receive callback is defined, it can receive from an external process. * * Returns: xpa handle associated with this class.name or NULL * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC XPA XPANew (char *xclass, char *name, char *help, SendCb send_callback, void *send_data, char *send_mode, ReceiveCb rec_callback, void *rec_data, char *rec_mode) #else XPA XPANew(xclass, name, help, send_callback, send_data, send_mode, rec_callback, rec_data, rec_mode) char *xclass; char *name; char *help; SendCb send_callback; void *send_data; char *send_mode; ReceiveCb rec_callback; void *rec_data; char *rec_mode; #endif { unsigned short port; unsigned int ip; int got; int oum; int keep_alive=1; int reuse_addr=1; char tbuf[SZ_LINE]; char tbuf2[SZ_LINE]; char tfile[SZ_LINE]; char *tfptr; XPA xpa; socklen_t slen = sizeof(struct sockaddr_in); struct sockaddr_in sock_in; #if HAVE_SYS_UN_H struct sockaddr_un sock_un; #endif /* do some initialization */ XPAInitEnv(); /* init the list of reserved commands */ XPAInitReserved(); /* we need a name, but no ":" allowed in the name */ if( (name == NULL) || (*name == '\0') || strchr(name, ':') ) return(NULL); /* limit the size of the xclass and name designation */ if( xclass && *xclass && (strlen(xclass) > XPA_NAMELEN) ){ if( verbosity ) fprintf(stderr, "XPA$ERROR: class designator too long\n"); return(NULL); } if( strlen(name) > XPA_NAMELEN ){ if( verbosity ) fprintf(stderr, "XPA$ERROR: name designator too long\n"); return(NULL); } /* we need either a send or a receive or both */ if( (send_callback == NULL) && (rec_callback == NULL ) ){ if( verbosity ) fprintf(stderr, "XPA$ERROR: requires send and/or receive callback\n"); return(NULL); } /* allocate xpa struct */ if( (xpa = (XPA)xcalloc(1, sizeof(XPARec))) == NULL ) return(NULL); /* fill in the blanks */ xpa->version = xstrdup(XPA_VERSION); xpa->type = (char *)xcalloc(10, sizeof(char)); if( xclass && *xclass ) xpa->xclass = xstrdup(xclass); else xpa->xclass = xstrdup("*"); xpa->name = xstrdup(name); xpa->help = xstrdup(help); /* set the value of the server big-endian-ness */ xpa->sendian = XPAEndian() ? xstrdup("big") : xstrdup("little"); /* fill in send information */ if( send_callback != NULL ){ xpa->send_callback = send_callback; xpa->send_data = send_data; strcat(xpa->type, "g"); /* process the mode string */ xpa->send_mode = XPA_DEF_MODE_SEND; XPAMode(send_mode, &(xpa->send_mode), "freebuf", XPA_MODE_FREEBUF,1); XPAMode(send_mode, &(xpa->send_mode), "acl", XPA_MODE_ACL, 1); } /* fill in receive information */ if( rec_callback != NULL ){ xpa->receive_callback = rec_callback; xpa->receive_data = rec_data; strcat(xpa->type, "s"); /* process the mode string */ xpa->receive_mode = XPA_DEF_MODE_REC; XPAMode(rec_mode, &(xpa->receive_mode), "buf", XPA_MODE_BUF, 1); XPAMode(rec_mode, &(xpa->receive_mode), "fillbuf", XPA_MODE_FILLBUF, 1); XPAMode(rec_mode, &(xpa->receive_mode), "freebuf", XPA_MODE_FREEBUF, 1); XPAMode(rec_mode, &(xpa->receive_mode), "acl", XPA_MODE_ACL, 1); } /* set up communication method */ switch(mtype){ case XPA_INET: /* open a socket and fill in socket information */ if( (xpa->fd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ) goto error; setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keep_alive, sizeof(keep_alive)); setsockopt(xpa->fd, 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( use_localhost ) sock_in.sin_addr.s_addr = htonl(gethostip("$localhost")); /* any address will do */ else sock_in.sin_addr.s_addr = htonl(INADDR_ANY); /* handle special case of xpans port */ if( !strcmp(xpa->name, XPANS_NAME) ){ XPAParseIpPort(XPANSMethod(NULL, 1), &ip, &port); sock_in.sin_port = htons(port); } else{ sock_in.sin_port = htons(XPAPort(xpa)); } /* bind to the ip:port */ if( xbind(xpa->fd, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0 ) goto error; /* we now can determine which port the system assigned */ if( getsockname(xpa->fd, (struct sockaddr *)&sock_in, &slen) < 0 ) goto error; else{ /* ip:port is the method */ gethost(tbuf2, SZ_LINE); snprintf(tbuf, SZ_LINE, "%x:%d", gethostip(tbuf2), ntohs(sock_in.sin_port)); xpa->method = xstrdup(tbuf); } break; #if HAVE_SYS_UN_H case XPA_UNIX: /* open a socket and fill in socket information */ if( (xpa->fd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ) goto error; setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keep_alive, sizeof(keep_alive)); setsockopt(xpa->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse_addr, sizeof(reuse_addr)); memset((char *)&sock_in, 0, sizeof(sock_in)); sock_un.sun_family = AF_UNIX; /* handle special case of xpans */ if( !strcmp(xpa->name, XPANS_NAME) ){ strcpy(tbuf, XPANSMethod(NULL, 1)); } else{ /* get filename part, composed of class and name and unique id */ snprintf(tfile, SZ_LINE, "%s_%s.%d", xpa->xclass, xpa->name, (int)getpid()); /* change "/" to "_" for filename */ for(tfptr = tfile; *tfptr != '\0'; tfptr++){ if( *tfptr == '/' ) *tfptr = '_'; } /* create full pathname */ snprintf(tbuf, SZ_LINE, "%s/%s", tmpdir, tfile); } /* delete old copy */ unlink (tbuf); strcpy(sock_un.sun_path, tbuf); /* unset umask so that everyone can read and write */ oum = umask(0); /* bind to the file */ got = xbind(xpa->fd, (struct sockaddr *)&sock_un, sizeof(sock_un)); /* reset umask */ umask(oum); /* now check for bind error */ if( got < 0 ) goto error; /* path is the method */ xpa->method = xstrdup(tbuf); break; #endif default: goto error; } /* listen for connections */ if( listen(xpa->fd, XPA_MAXLISTEN) < 0 ) goto error; /* make sure we close on exec */ xfcntl(xpa->fd, F_SETFD, FD_CLOEXEC); /* add this xpa to end of list of xpas */ XPAListAdd(&xpahead, xpa); /* publish this entry to the world */ if( nsregister ) XPANSAdd(xpa, NULL, NULL); /* make it active */ XPAActive(xpa, NULL, 1); #if NO_AUTOMATIC_HAVE_ATEXIT /* register XPA atexit funtion */ if( !atexitinit ){ atexit(_XPAAtExit); atexitinit = getpid(); } #endif /* return good news */ return(xpa); error: if( verbosity ){ perror("XPANew"); } _XPAFree(xpa); return(NULL); } /* *--------------------------------------------------------------------------- * * Routine: XPAFree * * Purpose: free up alloc'ed memory in the XPA record structure * * Results: 0 on success, -1 for failure * *--------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAFree (XPA xpa) #else int XPAFree(xpa) XPA xpa; #endif { /* if status is active, just flag eventual need to free and exit */ if( _XPAValid(xpahead, xpa, XPA_ACLS) ){ if( xpa_status(xpa) & XPA_STATUS_ACTIVE ){ xpa->status |= XPA_STATUS_FREE; FPRINTF((stderr, "%sXPAFree: marking xpa struct for later free'ing\n", _sp)); return(0); } else{ /* call the primitive routine */ FPRINTF((stderr, "%sXPAFree: freeing xpa struct\n", _sp)); return(_XPAFree(xpa)); } } else{ return(-1); } } /* *---------------------------------------------------------------------------- * * Routine: XPAInfoNew * * Purpose: add a new xpa name to a process to allow external * process to send info messages * * Returns: xpa handle associated with this class.name or NULL * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC XPA XPAInfoNew (char *xclass, char *name, InfoCb info_callback, void *info_data, char *info_mode) #else XPA XPAInfoNew(xclass, name, info_callback, info_data, info_mode) char *xclass; char *name; InfoCb info_callback; void *info_data; char *info_mode; #endif { int got; int oum; int keep_alive=1; int reuse_addr=1; char tbuf[SZ_LINE]; char tbuf2[SZ_LINE]; char tfile[SZ_LINE]; char *tfptr; XPA xpa; socklen_t slen = sizeof(struct sockaddr_in); struct sockaddr_in sock_in; #if HAVE_SYS_UN_H struct sockaddr_un sock_un; #endif /* do some initialization */ XPAInitEnv(); /* init the list of reserved commands */ XPAInitReserved(); /* we need a name, but no ":" allowed in the name */ if( (name == NULL) || (*name == '\0') || strchr(name, ':') ) return(NULL); /* we need an info callback */ if( info_callback == NULL ){ if( verbosity ){ fprintf(stderr, "XPA$ERROR: requires info callback\n"); } return(NULL); } /* allocate xpa struct */ if( (xpa = (XPA)xcalloc(1, sizeof(XPARec))) == NULL ) return(NULL); xpa->version = xstrdup(XPA_VERSION); xpa->type = (char *)xcalloc(10, sizeof(char)); /* fill in the blanks */ if( xclass != NULL ) xpa->xclass = xstrdup(xclass); else xpa->xclass = xstrdup("*"); xpa->name = xstrdup(name); /* fill in send information */ xpa->info_callback = info_callback; xpa->info_data = info_data; strcat(xpa->type, "i"); /* process the mode string */ xpa->info_mode = XPA_DEF_MODE_INFO; XPAMode(info_mode, &(xpa->info_mode), "acl", XPA_MODE_ACL, 1); /* set up communication method */ switch(mtype){ case XPA_INET: /* open a socket and fill in socket information */ if( (xpa->fd = xsocket(AF_INET, SOCK_STREAM, 0)) < 0 ) goto error; setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keep_alive, sizeof(keep_alive)); setsockopt(xpa->fd, 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( use_localhost ) sock_in.sin_addr.s_addr = htonl(gethostip("$localhost")); /* any address will do */ else sock_in.sin_addr.s_addr = htonl(INADDR_ANY); sock_in.sin_port = htons(XPAPort(xpa)); /* bind to the ip:port */ if( xbind(xpa->fd, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0 ) goto error; /* we now can determine which port the system assigned */ if( getsockname(xpa->fd, (struct sockaddr *)&sock_in, &slen) < 0 ) goto error; else{ /* ip:port is the method */ gethost(tbuf2, SZ_LINE); snprintf(tbuf, SZ_LINE, "%x:%d", gethostip(tbuf2), ntohs(sock_in.sin_port)); xpa->method = xstrdup(tbuf); } break; #if HAVE_SYS_UN_H case XPA_UNIX: /* get filename part, composed of class and name and unique id */ snprintf(tfile, SZ_LINE, "%s_%s.%d", xpa->xclass, xpa->name, (int)getpid()); /* change "/" to "_" for filename */ for(tfptr = tfile; *tfptr != '\0'; tfptr++){ if( *tfptr == '/' ) *tfptr = '_'; } /* create full pathname */ snprintf(tbuf, SZ_LINE, "%s/%s", tmpdir, tfile); /* delete old copy */ unlink (tbuf); /* open a socket and fill in socket information */ if( (xpa->fd = xsocket(AF_UNIX, SOCK_STREAM, 0)) < 0 ) goto error; setsockopt(xpa->fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&keep_alive, sizeof(keep_alive)); memset((char *)&sock_in, 0, sizeof(sock_in)); sock_un.sun_family = AF_UNIX; strcpy(sock_un.sun_path, tbuf); /* unset umask so that everyone can read and write */ oum = umask(0); /* bind to the file */ got = xbind(xpa->fd, (struct sockaddr *)&sock_un, sizeof(sock_un)); /* reset umask */ umask(oum); /* now check for bind error */ if( got < 0 ) goto error; /* path is the method */ xpa->method = xstrdup(tbuf); break; #endif default: goto error; } /* listen for connections */ if( listen(xpa->fd, XPA_MAXLISTEN) < 0 ) goto error; /* make sure we close on exec */ xfcntl(xpa->fd, F_SETFD, FD_CLOEXEC); /* add this xpa to end of list of xpas */ XPAListAdd(&xpahead, xpa); /* publish this entry to the world */ if( nsregister ) XPANSAdd(xpa, NULL, NULL); /* make it active */ XPAActive(xpa, NULL, 1); #if NO_AUTOMATIC_HAVE_ATEXIT /* register XPA atexit funtion */ if( !atexitinit ){ atexit(_XPAAtExit); atexitinit = getpid(); } #endif /* return good news */ return(xpa); error: XPAFree(xpa); return(NULL); } /* *---------------------------------------------------------------------------- * * Routine: XPAPoll * * Purpose: non-blocking handling of XPA access points * timeout in millisecs, but if negative, no timeout is used * * Returns: number of requests processed (if maxreq >=0) * number of requests pending (if maxreq <0) * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAPoll (int msec, int maxreq) #else int XPAPoll(msec, maxreq) int msec; int maxreq; #endif { int sgot; int got=0; fd_set readfds; struct timeval tv; struct timeval *tvp; again: if( msec >= 0 ){ tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; tvp = &tv; } /* negative value means just block and wait */ else{ tvp = NULL; } FD_ZERO(&readfds); if( XPAAddSelect(NULL, &readfds) ){ sgot = xselect(swidth, &readfds, NULL, NULL, tvp); /* error -- what should we do? */ if( sgot < 0 ){ if( xerrno == EINTR ) goto again; if( verbosity ){ perror("XPAPoll() select"); } exit(1); } /* timeout -- just return */ else if( sgot == 0 ) ; /* finally ... something to do */ else{ /* if maxreq < 0, just return how many are ready */ if( maxreq < 0 ) return(sgot); else got = XPAProcessSelect(&readfds, maxreq); } } return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPAMainLoop * * Purpose: non-X programs event loop for handling XPA * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAMainLoop (void) #else int XPAMainLoop() #endif { int sgot; int got=0; fd_set readfds; FD_ZERO(&readfds); while( XPAAddSelect(NULL, &readfds) ){ FPRINTF((stderr, "%sXPAMainLoop: waiting for select() ...\n", _sp)); sgot = xselect(swidth, &readfds, NULL, NULL, NULL); FPRINTF((stderr, "%sXPAMainLoop: select() returned: %d\n", _sp, sgot)); /* error -- what should we do? */ if( sgot < 0 ){ if( xerrno == EINTR ){ FD_ZERO(&readfds); continue; } if( verbosity ){ perror("XPAMainLoop() select"); } exit(1); } /* can't happen, since we have no timeout */ else if( sgot == 0 ) ; /* finally ... something to do */ else got += XPAProcessSelect(&readfds, 0); FD_ZERO(&readfds); } return(got); } /* *---------------------------------------------------------------------------- * * Routine: XPASleep * * Purpose: sleep for specified milliseconds * * Returns: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPASleep (int msec) #else void XPASleep(msec) int msec; #endif { struct timeval tv; if( msec > 0 ){ tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; xselect(1, NULL, NULL, NULL, &tv); } } /* *---------------------------------------------------------------------------- * * Routine: XPAListHead * * Purpose: semi-public routine to return the head of the xpa list * * Results: XPA list pointer on success * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPAMtype (void) #else int XPAMtype() #endif { return(mtype); } /* *---------------------------------------------------------------------------- * * Routine: XPATmpdir * * Purpose: semi-public routine to return the tmpdir value * * Results: tmpdir * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC char * XPATmpdir (void) #else char * XPATmpdir() #endif { return(tmpdir); } /* *---------------------------------------------------------------------------- * * Routine: XPASetBuf * * Purpose: set the return buffer in a server send callback * (use mainly by tcl to transfer tcl buffers to C) * * Returns: 0 on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPASetBuf (XPA xpa, char *buf, size_t len, int xcopy) #else int XPASetBuf(xpa, buf, len, xcopy) XPA xpa; char *buf; size_t len; int xcopy; #endif { /* sanity check */ if( !xpa || !xpa->comm ) return(-1); /* xcopy >0 => make a copy of the data, else just assign */ if( xcopy ){ xpa->comm->len = len; if( (xpa->comm->buf = (char *)xmalloc(len)) == NULL ) return(-1); memcpy(xpa->comm->buf, buf, len); } else{ xpa->comm->len = len; xpa->comm->buf = buf; } return(0); } /* *---------------------------------------------------------------------------- * * Routine: XPASetBuf * * Purpose: set the return buffer in a server send callback * (use mainly by tcl to transfer tcl buffers to C) * * Returns: 0 on success, -1 on failure * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC int XPASetFree (XPA xpa, MyFree myfree, void *myfree_ptr) #else int XPASetFree(xpa, myfree, myfree_ptr) XPA xpa; MyFree myfree; void *myfree_ptr; #endif { /* sanity check */ if( !xpa || !xpa->comm ) return(-1); /* set the free routine and data pointer */ xpa->comm->myfree = myfree; xpa->comm->myfree_ptr = myfree_ptr; return(0); } /* *---------------------------------------------------------------------------- * * Routine: XPACleanup * * Purpose: make valgrind happy by freeing memory * * Results: none * *---------------------------------------------------------------------------- */ #ifdef ANSI_FUNC void XPACleanup (void) #else void XPACleanup () #endif { XPAFreeReserved(); XPAAclFree(); if( tmpdir ){ xfree(tmpdir); tmpdir = NULL; } } #ifdef ANSI_FUNC void XPASaveJmp(void *env) #else void XPASaveJmp(env) void *env; #endif { #if HAVE_SETJMP_H xalloc_savejmp((jmp_buf *)env); #else return; #endif }