/* $Header: CHlookup.c,v 2.4 87/03/14 06:41:56 jqj Exp $ */ /* contains: * CH_LookupAddr * CH_GetFirstCH * CH_GetOtherCH */ /* $Log: CHlookup.c,v $ * Revision 2.4 87/03/14 06:41:56 jqj * major changes to CH location algorithm. * changed LookupAddressDN to use new simpler semantics (can call GetOtherCH * many times) * * Revision 2.3 87/01/05 11:58:47 ed * Webster changes * * Revision 2.3 87/01/05 11:58:47 ed * Eliminate hard file name for BFS and use BFS definition instead * * Revision 2.2 86/06/30 12:23:48 jqj * 1/ convert BDT read to dynamically allocate a buffer, and grow it if * necessary using realloc(). This should fix problems with support at * Xerox, where they have so many CHes that we were running out of space. * 2/ convert CH_GetFirstCH() to use /usr/new/xnsbfs if CH.default fails * to find a CH. xnsbfs does an expanding ring, or at least a local, * broadcast for servers. * * Revision 2.1 86/06/05 09:12:48 jqj * Split out CHdefault.c from this file. The path to the addresses file * is now found in that file. * * Revision 2.0 85/11/21 07:22:32 jqj * 4.3BSD standard release * * Revision 1.3 85/11/21 07:14:19 jqj * first try at 4.3BSD standard release * * Revision 1.2 85/10/21 12:41:55 jqj * Gould version: work around a Gould compiler bug. Default to /usr/new * for consistency. * * Revision 1.1 85/10/18 09:14:53 jqj * Initial revision * * Revision 1.1 85/03/26 06:27:00 jqj * Initial revision * */ #include <stdio.h> #include <sys/types.h> #include <netns/ns.h> #include <xnscourier/Clearinghouse2.h> #include <xnscourier/CHEntries.h> #include <xnscourier/except.h> #include <sys/file.h> #ifndef BFS #define BFS "/usr/new/xnsbfs" #endif /* data structures for very simple (null) authentication */ static Cardinal nullpasswd = 0; static Clearinghouse2_Authenticator nullagent = {{0,{0,0}},{1,&nullpasswd}}; /* * This is an entry in the in-core cache of known clearinghouses. * chl_name Each entry has a name, which may be null for entries * obtained by expanding ring broadcast or initial table lookup. A * particular address may appear in the list multiple times with * different or null names, but mostly the entries are unique. * chl_addr The address is one of the addresses given for this * host. For multihomed CHs, no attempt is made to make sure that * this address is optimal. * chl_status The status of the entry, one of: * chunknown address invalid. * chdown did not answer when probed. * chup address valid, and responds to RetrieveAddresses. * chused has been returned to caller of GetCH routines * as a valid CH. Hence GetOtherCH will not return it * again. Note that GetFirstCH resets chused->chup. * chl_next Link to next clearinghouse in database. * chl_domains [NOT YET USED] List of domains served, for efficiency */ struct chlist { Clearinghouse2_ObjectName *chl_name; struct ns_addr chl_addr; enum {chunknown, chup, chdown, chused} chl_status; struct Clearinghouse2_TwoPartName *chl_domains; struct chlist *chl_next; }; struct chlist *namelist = NULL; /* * add a clearinghouse name to the ch list if it isn't already there. * Initial state is chunknown, since we don't know its address and can't * probe it. */ static struct chlist * addtochlist(ch_name) Clearinghouse2_ObjectName *ch_name; { struct chlist *chptr; extern char *malloc(); if (ch_name != NULL) /* don't eliminate duplicate anonymous entries */ for (chptr = namelist; chptr != NULL; chptr = chptr->chl_next) { if ( chptr->chl_name && (strcmp(chptr->chl_name->object,ch_name->object)==0) && (strcmp(chptr->chl_name->domain,ch_name->domain)==0) && (strcmp(chptr->chl_name->organization, ch_name->organization)==0)) { clear_Clearinghouse2_ObjectName(ch_name); /* dealloc */ return NULL; /* already in list */ } } chptr = (struct chlist *) malloc(sizeof(struct chlist)); chptr->chl_name = ch_name; chptr->chl_status = chunknown; chptr->chl_next = namelist; namelist = chptr; return chptr; } static GetData(conn) CourierConnection *conn; { int count, i; int bufused, bufsize; Unspecified *buffer, *bp; Clearinghouse2_StreamOfObjectName obnames; bufsize = MAXWORDS*4; /* end of available space */ buffer = Allocate(bufsize); bufused = 0; /* number of words used */ while (count = BDTread(conn, (char*)(buffer+bufused), MAXWORDS*sizeof(Unspecified)) ) { bufused += count/sizeof(Unspecified); /* leave room for one last packet */ if (bufused+MAXWORDS > bufsize) { bufsize += bufsize; /* double our space */ buffer = (Unspecified *) realloc((char *) buffer, bufsize*sizeof(Unspecified)); } } bp = buffer; while (bp-buffer < bufused) { bp += internalize_Clearinghouse2_StreamOfObjectName(&obnames,bp); if (0 == (int) obnames.designator) { for (i = 0; i < obnames.nextSegment_case.segment.length; i++) addtochlist( &obnames.nextSegment_case.segment.sequence[i]); } else { for (i = 0; i < obnames.lastSegment_case.length; i++) addtochlist( &obnames.lastSegment_case.sequence[i]); Deallocate(buffer); return; } } Deallocate(buffer); return; } static struct ns_addr* itemtonsaddr(itemptr) Clearinghouse2_Item *itemptr; /* i.e. a sequence of Unspecified */ { static union { struct ns_addr addr; u_short shrt[6]; } result; register int i; register Unspecified *seq; if (itemptr->length < 7) return(0); seq = itemptr->sequence +1; /* itemptr->sequence[0] == number of addresses */ for (i = 0; i < 6; i++, seq++) result.shrt[i] = ntohs(*seq); return(&result.addr); } /* * Open an SPP connection to the address given as chaddr, then * check whether the CHS pointed to by conn is up. If not, close * conn and return 0. If so, return the connection. */ static CourierConnection* ClearinghouseOpen(chaddr) struct ns_addr chaddr; { CourierConnection *conn; Clearinghouse2_RetrieveAddressesResults raresult; conn.x_port = 0; if ((conn = CourierOpen(&chaddr)) == NULL) return(conn); DURING raresult = Clearinghouse2_RetrieveAddresses(conn,NULL); HANDLER { CourierClose(conn); conn = NULL; } END_HANDLER; clear_Clearinghouse2_RetrieveAddressesResults(&raresult); return(conn); } /* * path to look for the addresses file on * (I wish I'd picked a shorter name, so we could rendezvous in /etc. * But long file names in /etc are very unpopular!). * This path is defined n CHdefault.c, and used by CH_NameDefault() */ extern char *chaddrpath[]; CourierConnection* CH_GetFirstCH() { struct ns_addr chaddr; extern struct ns_addr *getXNSaddr(); extern struct ns_addr ns_addr(); char buf[BUFSIZ]; CourierConnection *result; FILE *chfile; struct chlist *chptr; int i; result = (CourierConnection *) NULL; /* first, look in the process cache */ for (chptr = namelist; chptr != NULL; chptr = chptr->chl_next) { switch (chptr->chl_status) { case chunknown: case chdown: break; case chused: chptr->chl_status = chup; case chup: if (result == NULL) result = ClearinghouseOpen(chptr->chl_addr); } } if (result) return(result); /* Now, use hard-coded CH */ /* eventually we'll do an expanding-ring or something */ for (i = 0; chaddrpath[i] != NULL; i++) { if ((chfile = fopen(chaddrpath[i],"r")) == NULL) continue; flock(fileno(chfile),LOCK_SH); while (fgets(buf, BUFSIZ, chfile) != NULL) if ((buf[0] != '#') && (chaddr = ns_addr(buf), result = ClearinghouseOpen((chaddr)))) { if ((chptr = addtochlist(NULL))) { chptr->chl_addr = chaddr; chptr->chl_status = chused; } flock(fileno(chfile),LOCK_UN); fclose(chfile); return(result); } flock(fileno(chfile),LOCK_UN); fclose(chfile); break; /* don't look for other files */ } /* try expanding-ring here -- this could be more efficient! */ if ((chfile = popen(BFS,"r")) != NULL) { while (fgets(buf, BUFSIZ, chfile) != NULL) if ((buf[0] != '#') && (chaddr=ns_addr(buf), result = ClearinghouseOpen(chaddr))) { if ((chptr = addtochlist(NULL))) { chptr->chl_addr = chaddr; chptr->chl_status = chused; } pclose(chfile); return(result); } pclose(chfile); } return(result); } CourierConnection * CH_GetOtherCH(conn,hint) CourierConnection *conn; Clearinghouse2_ObjectName hint; { struct ns_addr *ch2addr; CourierConnection *ch2conn, *chresult; Clearinghouse2_RetrieveItemResults riresult; Clearinghouse2_RetrieveMembersResults rmresult; struct chlist *chptr; Clearinghouse2_Property clearinghouseNames = 3; Clearinghouse2_Property clearinghouseAddresses = 4; if (hint.object != NULL && *hint.object != '\0') { DURING /* get list of possible other clearinghouses */ rmresult = Clearinghouse2_RetrieveMembers(conn, GetData, hint, clearinghouseNames, BulkData1_immediateSink, nullagent); HANDLER { /* some race condition */ return(NULL); } END_HANDLER; /* throw away the distinguished name, which we don't need */ clear_Clearinghouse2_RetrieveMembersResults(&rmresult); } /* for each member of potentials list, probe it */ ch2conn = chresult = NULL; for (chptr = namelist; chptr != NULL; chptr = chptr->chl_next) { switch (chptr->chl_status) { case chunknown: /* a new entry, presumably */ /* get its address */ DURING riresult = Clearinghouse2_RetrieveItem(conn, NULL, *(chptr->chl_name), clearinghouseAddresses, nullagent); HANDLER break; /* try next in namelist */ END_HANDLER; chptr->chl_addr = *itemtonsaddr(&riresult.value); clear_Clearinghouse2_RetrieveItemResults(&riresult); chptr->chl_status = chdown; /* fall through to: */ case chdown: /* check down CH to see if it has revived */ /* make sure the second CH is really there */ if ((ch2conn = ClearinghouseOpen(chptr->chl_addr))) { chptr->chl_status = chup; /* we got it! */ if (chresult == NULL) { chresult = ch2conn; chptr->chl_status = chused; } else CourierClose(ch2conn); ch2conn = NULL; } break; case chup: case chused: break; } /* end of switch */ } /* inspect next in list */ /* * if no new one, return an old one */ for (chptr= namelist; chptr && (chresult==NULL); chptr= chptr->chl_next) { if (chptr->chl_status == chup) { chptr->chl_status = (chresult=ClearinghouseOpen(chptr->chl_addr)) ? chused : chdown; } } return(chresult); } /* * Given a Clearinghouse three part name (possibly containing wild cards) * and the property number on which a NetworkAddress is expected to occur, * returns the ns_addr structure associated with that name. * Note that the ns_addr structure is statically allocated! * Resets pattern to be the distinguished name of the object found. */ struct ns_addr * CH_LookupAddr(pattern,property) Clearinghouse2_ObjectNamePattern pattern; Clearinghouse2_Property property; { struct ns_addr* CH_LookupAddrDN(); return(CH_LookupAddrDN(pattern,property,0,0)); } /* * Lookup a clearinghouse address, returning the address and the * distinguished name of the object found. */ struct ns_addr * CH_LookupAddrDN(pattern,property,distnamep,distnamelen) Clearinghouse2_ObjectNamePattern pattern; Clearinghouse2_Property property; char *distnamep; int distnamelen; { struct ns_addr *chaddr, *resultaddr; CourierConnection *conn, *ch2conn; Clearinghouse2_ObjectName hint; /* from WrongServer errors */ Clearinghouse2_RetrieveItemResults riresult; int i; if ((long) property <= 0) /* default to addressList i.e. 4 */ property = CHEntries0_addressList; if (pattern.object == NULL || pattern.domain == NULL || pattern.organization == NULL) return(NULL); /* can't handle defaults */ if ((conn = CH_GetFirstCH()) == NULL) { fprintf(stderr,"Can't open connection to local Clearinghouse\n"); exit(1); } /* ask our primary clearinghouse for the address of this thing */ for (i = 5; i > 0; i--) { DURING { riresult = Clearinghouse2_RetrieveItem(conn, NULL, pattern, property, nullagent); i = -1; } HANDLER { if (Exception.Code == REJECT_ERROR) { fprintf(stderr,"Problem with CH.addrs. CH rejected request\n"); hint.object = hint.domain = hint.organization = NULL; } else if (Exception.Code != Clearinghouse2_WrongServer) { /* some random error */ hint.object = hint.domain = hint.organization = NULL; } else hint = CourierErrArgs(Clearinghouse2_WrongServerArgs, hint); ch2conn = CH_GetOtherCH(conn, hint); CourierClose(conn); if (ch2conn == NULL) return(0); conn = ch2conn; } END_HANDLER; } if (i >= 0) { return(0); } resultaddr = itemtonsaddr(&riresult.value); if (distnamep != NULL && distnamelen >= (2+strlen(riresult.distinguishedObject.object)+ strlen(riresult.distinguishedObject.domain)+ strlen(riresult.distinguishedObject.organization))) strcpy(distnamep,CH_NameToString(riresult.distinguishedObject)); clear_Clearinghouse2_RetrieveItemResults(&riresult); CourierClose(conn); return(resultaddr); }