|  | /* | 
|  | * This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. | 
|  | */ | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <parlib.h> | 
|  | #include <unistd.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <signal.h> | 
|  | #include <pthread.h> | 
|  | #include <fcntl.h> | 
|  | #include <ctype.h> | 
|  | #include <error.h> | 
|  | #include <iplib.h> | 
|  | #include <ndb.h> | 
|  | #include <fcallfmt.h> | 
|  | #include <fcall.h> | 
|  |  | 
|  | #define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0])) | 
|  |  | 
|  | enum | 
|  | { | 
|  | Nreply=			20, | 
|  | Maxreply=		256, | 
|  | Maxrequest=		128, | 
|  | Maxpath=		128, | 
|  | Maxfdata=		8192, | 
|  | Maxhost=		64,		/* maximum host name size */ | 
|  | Maxservice=		64,		/* maximum service name size */ | 
|  |  | 
|  | Qdir=			0, | 
|  | Qcs=			1, | 
|  | }; | 
|  |  | 
|  | typedef struct Mfile	Mfile; | 
|  | typedef struct Mlist	Mlist; | 
|  | typedef struct Network	Network; | 
|  | typedef struct Flushreq	Flushreq; | 
|  | typedef struct Job	Job; | 
|  |  | 
|  | int vers;		/* incremented each clone/attach */ | 
|  | /* need to resolve the #inluce for all this stuff. */ | 
|  | #define DMDIR              0x80000000      /* mode bit for directories */ | 
|  | #define QTDIR 0x80 | 
|  | #define QTFILE 0 | 
|  | #define ERRMAX 128 | 
|  |  | 
|  | struct Mfile | 
|  | { | 
|  | int		busy; | 
|  |  | 
|  | char		*user; | 
|  | struct qid     qid; | 
|  | int		fid; | 
|  |  | 
|  | /* | 
|  | *  current request | 
|  | */ | 
|  | char		*net; | 
|  | char		*host; | 
|  | char		*serv; | 
|  | char		*rem; | 
|  |  | 
|  | /* | 
|  | *  result of the last lookup | 
|  | */ | 
|  | Network		*nextnet; | 
|  | int		nreply; | 
|  | char		*reply[Nreply]; | 
|  | int		replylen[Nreply]; | 
|  | }; | 
|  |  | 
|  | struct Mlist | 
|  | { | 
|  | Mlist	*next; | 
|  | Mfile	mf; | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  active requests | 
|  | */ | 
|  | struct Job | 
|  | { | 
|  | Job	*next; | 
|  | int	flushed; | 
|  | struct fcall	request; | 
|  | struct fcall	reply; | 
|  | pthread_t thread; | 
|  | }; | 
|  |  | 
|  | //spinlock_t	joblock; | 
|  | int joblock; | 
|  | Job	*joblist; | 
|  |  | 
|  | Mlist	*mlist; | 
|  | int	mfd[2]; | 
|  | int	debug; | 
|  | int	paranoia; | 
|  | int	ipv6lookups = 1; | 
|  | char	*dbfile = "lib/ndb/local"; | 
|  | struct ndb	*db, *netdb; | 
|  |  | 
|  | void	rversion(Job*); | 
|  | void	rflush(Job*); | 
|  | void	rattach(Job*, Mfile*); | 
|  | char*	rwalk(Job*, Mfile*); | 
|  | void	ropen(Job*, Mfile*); | 
|  | void	rcreate(Job*, Mfile*); | 
|  | void	rread(Job*, Mfile*); | 
|  | void	rwrite(Job*, Mfile*); | 
|  | void	rclunk(Job*, Mfile*); | 
|  | void	rremove(Job*, Mfile*); | 
|  | void	rstat(Job*, Mfile*); | 
|  | void	rwstat(Job*, Mfile*); | 
|  | void	rauth(Job*); | 
|  | void	sendmsg(Job*, char*); | 
|  | void	mountinit(char*, char*); | 
|  | void	io(void); | 
|  | void	ndbinit(void); | 
|  | void	netinit(int); | 
|  | void	netadd(char*); | 
|  | char	*genquery(Mfile*, char*); | 
|  | char*	ipinfoquery(Mfile*, char**, int); | 
|  | int	needproto(Network*, struct ndbtuple*); | 
|  | int	lookup(Mfile*); | 
|  | struct ndbtuple*	reorder(struct ndbtuple*, struct ndbtuple*); | 
|  | void	ipid(void); | 
|  | void	readipinterfaces(void); | 
|  | void*	emalloc(int); | 
|  | char*	estrdup(char*); | 
|  | Job*	newjob(void); | 
|  | void	freejob(Job*); | 
|  | void	setext(char*, int, char*); | 
|  | void	cleanmf(Mfile*); | 
|  |  | 
|  | extern void	paralloc(void); | 
|  |  | 
|  | spinlock_t	dblock;		/* mutex on database operations */ | 
|  | spinlock_t	netlock;	/* mutex for netinit() */ | 
|  |  | 
|  | char	*logfile = "cs"; | 
|  | char	*paranoiafile = "cs.paranoia"; | 
|  |  | 
|  | char	mntpt[Maxpath]; | 
|  | char	netndb[Maxpath]; | 
|  |  | 
|  | /* | 
|  | *  Network specific translators | 
|  | */ | 
|  | struct ndbtuple*	iplookup(Network*, char*, char*, int); | 
|  | char*		iptrans(struct ndbtuple*, Network*, char*, char*, int); | 
|  | struct ndbtuple*	telcolookup(Network*, char*, char*, int); | 
|  | char*		telcotrans(struct ndbtuple*, Network*, char*, char*, int); | 
|  | struct ndbtuple*	dnsiplookup(char*, struct ndbs*); | 
|  |  | 
|  | struct Network | 
|  | { | 
|  | char		*net; | 
|  | struct ndbtuple	*(*lookup)(Network*, char*, char*, int); | 
|  | char		*(*trans)(struct ndbtuple*, Network*, char*, char*, int); | 
|  | int		considered;		/* flag: ignored for "net!"? */ | 
|  | int		fasttimeouthack;	/* flag. was for IL */ | 
|  | Network		*next; | 
|  | }; | 
|  |  | 
|  | enum | 
|  | { | 
|  | Ntcp = 0, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | *  net doesn't apply to (r)udp, icmp(v6), or telco (for speed). | 
|  | */ | 
|  | Network network[] = { | 
|  | [Ntcp]	{ "tcp",	iplookup,	iptrans,	0 }, | 
|  | { "udp",	iplookup,	iptrans,	1 }, | 
|  | { "icmp",	iplookup,	iptrans,	1 }, | 
|  | { "icmpv6",	iplookup,	iptrans,	1 }, | 
|  | { "rudp",	iplookup,	iptrans,	1 }, | 
|  | { "ssh",	iplookup,	iptrans,	1 }, | 
|  | { "telco",	telcolookup,	telcotrans,	1 }, | 
|  | { 0 }, | 
|  | }; | 
|  |  | 
|  | spinlock_t ipifclock; | 
|  | struct ipifc *ipifcs; | 
|  |  | 
|  | char	eaddr[16];		/* ascii ethernet address */ | 
|  | char	ipaddr[64];		/* ascii internet address */ | 
|  | uint8_t	ipa[IPaddrlen];		/* binary internet address */ | 
|  | char	*mysysname; | 
|  |  | 
|  | Network *netlist;		/* networks ordered by preference */ | 
|  | Network *last; | 
|  |  | 
|  | static void | 
|  | nstrcpy(char *to, char *from, int len) | 
|  | { | 
|  | strncpy(to, from, len); | 
|  | to[len-1] = 0; | 
|  | } | 
|  |  | 
|  | char *argv0; | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprintf(stderr, "CS:usage: %s [-dn] [-f ndb-file] [-x netmtpt]\n", argv0); | 
|  | fprintf(stderr, "CS:usage"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * based on libthread's threadsetname, but drags in less library code. | 
|  | * actually just sets the arguments displayed. | 
|  | */ | 
|  | void | 
|  | procsetname(char *fmt, ...) | 
|  | { | 
|  | /* someday ... */ | 
|  | #if 0 | 
|  | int fd; | 
|  | char *cmdname; | 
|  | char buf[128]; | 
|  | va_list arg; | 
|  |  | 
|  | va_start(arg, fmt); | 
|  | cmdname = vsmprint(fmt, arg); | 
|  | va_end(arg); | 
|  | if (cmdname == NULL) | 
|  | return; | 
|  | snprintf(buf, sizeof buf, "#p/%d/args", getpid()); | 
|  | if((fd = open(buf, OWRITE)) >= 0){ | 
|  | write(fd, cmdname, strlen(cmdname)+1); | 
|  | close(fd); | 
|  | } | 
|  | free(cmdname); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int justsetname; | 
|  | char ext[Maxpath], servefile[Maxpath]; | 
|  | argv0 = argv[0]; | 
|  | justsetname = 0; | 
|  | setnetmtpt(mntpt, sizeof(mntpt), NULL); | 
|  | register_printf_specifier('F', printf_fcall, printf_fcall_info); | 
|  | ext[0] = 0; | 
|  | argc--, argv++; | 
|  | while (argc && **argv == '-'){ | 
|  | switch(argv[0][1]){ | 
|  | case '4': | 
|  | ipv6lookups = 0; | 
|  | break; | 
|  | case 'd': | 
|  | debug = 1; | 
|  | break; | 
|  | case 'f': | 
|  | if (argc < 2) | 
|  | usage(); | 
|  | dbfile = argv[1]; | 
|  | argc--,argv++; | 
|  | break; | 
|  | case 'n': | 
|  | justsetname = 1; | 
|  | break; | 
|  | case 'x': | 
|  | if (argc < 2) | 
|  | usage(); | 
|  | setnetmtpt(mntpt, sizeof(mntpt), argv[1]); | 
|  | argc--,argv++; | 
|  | setext(ext, sizeof(ext), mntpt); | 
|  | break; | 
|  | } | 
|  | argc--,argv++; | 
|  | } | 
|  |  | 
|  | //rfork(RFREND|RFNOTEG); | 
|  |  | 
|  | snprintf(servefile, sizeof(servefile), "#s/cs%s", ext); | 
|  | snprintf(netndb, sizeof(netndb), "%s/ndb", mntpt); | 
|  | syscall(SYS_nunmount, (unsigned long)servefile, strlen(servefile), | 
|  | (unsigned long)mntpt, strlen(mntpt)); | 
|  | remove(servefile); | 
|  |  | 
|  | ndbinit(); | 
|  | netinit(0); | 
|  |  | 
|  | if(!justsetname){ | 
|  | mountinit(servefile, mntpt); | 
|  | io(); | 
|  | } | 
|  | exit(0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  if a mount point is specified, set the cs extention to be the mount point | 
|  | *  with '_'s replacing '/'s | 
|  | */ | 
|  | void | 
|  | setext(char *ext, int n, char *p) | 
|  | { | 
|  | int i, c; | 
|  |  | 
|  | n--; | 
|  | for(i = 0; i < n; i++){ | 
|  | c = p[i]; | 
|  | if(c == 0) | 
|  | break; | 
|  | if(c == '/') | 
|  | c = '_'; | 
|  | ext[i] = c; | 
|  | } | 
|  | ext[i] = 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | mountinit(char *service, char *mntpt) | 
|  | { | 
|  | int f; | 
|  | int p[2]; | 
|  | char buf[32]; | 
|  | int ret; | 
|  |  | 
|  | ret = syscall(SYS_pipe, (unsigned long)p); | 
|  | if (ret < 0){ | 
|  | error(1, 0, "pipe: %r"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  make a /srv/cs | 
|  | * ORCLOSE means remove on last close. Handy. Not here yet. | 
|  | */ | 
|  | f = open(service, O_WRONLY|O_CREAT/*|ORCLOSE*/, 0666); | 
|  | if(f < 0) | 
|  | error(1, 0, "%s: %r",service); | 
|  | snprintf(buf, sizeof(buf), "%d", p[1]); | 
|  | if(write(f, buf, strlen(buf)) != strlen(buf)) | 
|  | error(1, 0, "Write %s: %r", service); | 
|  | /* using #s: we create a pipe and drop it into #s. | 
|  | * we no longer mount. That's up to you. | 
|  | * #s will route requests to us. | 
|  | */ | 
|  | close(p[1]); | 
|  |  | 
|  | mfd[0] = mfd[1] = p[0]; | 
|  | } | 
|  |  | 
|  | void | 
|  | ndbinit(void) | 
|  | { | 
|  | db = ndbopen(dbfile); | 
|  | if(db == NULL) | 
|  | error(1, 0, "%s: %r","can't open network database"); | 
|  |  | 
|  | netdb = ndbopen(netndb); | 
|  | if(netdb != NULL){ | 
|  | netdb->nohash = 1; | 
|  | db = ndbcat(netdb, db); | 
|  | } | 
|  | } | 
|  |  | 
|  | Mfile* | 
|  | newfid(int fid) | 
|  | { | 
|  | Mlist *f, *ff; | 
|  | Mfile *mf; | 
|  |  | 
|  | ff = 0; | 
|  | for(f = mlist; f; f = f->next) | 
|  | if(f->mf.busy && f->mf.fid == fid) | 
|  | return &f->mf; | 
|  | else if(!ff && !f->mf.busy) | 
|  | ff = f; | 
|  | if(ff == 0){ | 
|  | ff = emalloc(sizeof *f); | 
|  | ff->next = mlist; | 
|  | mlist = ff; | 
|  | } | 
|  | mf = &ff->mf; | 
|  | memset(mf, 0, sizeof *mf); | 
|  | mf->fid = fid; | 
|  | return mf; | 
|  | } | 
|  |  | 
|  | Job* | 
|  | newjob(void) | 
|  | { | 
|  | Job *job; | 
|  |  | 
|  | job = calloc(1, sizeof(Job)); | 
|  | if (! job){ | 
|  | error(1, 0, "%s: %r","job calloc"); | 
|  | } | 
|  | //	//lock(&joblock); | 
|  | job->next = joblist; | 
|  | joblist = job; | 
|  | job->request.tag = -1; | 
|  | //	//unlock(&joblock); | 
|  | return job; | 
|  | } | 
|  |  | 
|  | void | 
|  | freejob(Job *job) | 
|  | { | 
|  | Job **l; | 
|  | //lock(&joblock); | 
|  | for(l = &joblist; *l; l = &(*l)->next){ | 
|  | if((*l) == job){ | 
|  | *l = job->next; | 
|  | free(job); | 
|  | break; | 
|  | } | 
|  | } | 
|  | //unlock(&joblock); | 
|  | } | 
|  |  | 
|  | void | 
|  | flushjob(int tag) | 
|  | { | 
|  | Job *job; | 
|  |  | 
|  | //lock(&joblock); | 
|  | for(job = joblist; job; job = job->next){ | 
|  | if(job->request.tag == tag && job->request.type != Tflush){ | 
|  | job->flushed = 1; | 
|  | break; | 
|  | } | 
|  | } | 
|  | //unlock(&joblock); | 
|  | } | 
|  |  | 
|  | void *job_thread(void* arg) | 
|  | { | 
|  | Mfile *mf; | 
|  | Job *job = arg; | 
|  | //lock(&dblock); | 
|  | mf = newfid(job->request.fid); | 
|  |  | 
|  | if(debug) | 
|  | fprintf(stderr, "CS:%F", &job->request); | 
|  | switch(job->request.type){ | 
|  | default: | 
|  | fprintf(stderr, "CS:unknown request type %d", job->request.type); | 
|  | break; | 
|  | case Tversion: | 
|  | rversion(job); | 
|  | break; | 
|  | case Tauth: | 
|  | rauth(job); | 
|  | break; | 
|  | case Tflush: | 
|  | rflush(job); | 
|  | break; | 
|  | case Tattach: | 
|  | rattach(job, mf); | 
|  | break; | 
|  | case Twalk: | 
|  | rwalk(job, mf); | 
|  | break; | 
|  | case Topen: | 
|  | ropen(job, mf); | 
|  | break; | 
|  | case Tcreate: | 
|  | rcreate(job, mf); | 
|  | break; | 
|  | case Tread: | 
|  | rread(job, mf); | 
|  | break; | 
|  | case Twrite: | 
|  | rwrite(job, mf); | 
|  | break; | 
|  | case Tclunk: | 
|  | rclunk(job, mf); | 
|  | break; | 
|  | case Tremove: | 
|  | rremove(job, mf); | 
|  | break; | 
|  | case Tstat: | 
|  | rstat(job, mf); | 
|  | break; | 
|  | case Twstat: | 
|  | rwstat(job, mf); | 
|  | break; | 
|  | } | 
|  | //unlock(&dblock); | 
|  |  | 
|  | freejob(job); | 
|  |  | 
|  | if (debug) | 
|  | fprintf(stderr, "CS:Job done\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | io(void) | 
|  | { | 
|  | long n; | 
|  |  | 
|  | uint8_t mdata[IOHDRSZ + Maxfdata]; | 
|  | Job *job; | 
|  |  | 
|  | /* | 
|  | * each request is handled via a thread. Somewhat less efficient than the old | 
|  | * cs but way cleaner. | 
|  | */ | 
|  |  | 
|  | for(;;){ | 
|  | n = read9pmsg(mfd[0], mdata, sizeof mdata); | 
|  | if(n<=0) | 
|  | error(1, 0, "%s: %r","mount read"); | 
|  | job = newjob(); | 
|  | if(convM2S(mdata, n, &job->request) != n){ | 
|  | fprintf(stderr, "convM2S went south: format error %ux %ux %ux %ux %ux", | 
|  | mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]); | 
|  | error(1, 0, "format error %ux %ux %ux %ux %ux", | 
|  | mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]); | 
|  | freejob(job); | 
|  | continue; | 
|  | } | 
|  | /* stash the thread in the job so we can join them all | 
|  | * later if we want to. | 
|  | */ | 
|  | #if 0 | 
|  | if (pthread_create(&job->thread, NULL, &job_thread, job)) { | 
|  | error(1, 0, "%s: %r","Failed to create job"); | 
|  | continue; | 
|  | } | 
|  | #endif | 
|  | job_thread(job); | 
|  |  | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | rversion(Job *job) | 
|  | { | 
|  | if(job->request.msize > IOHDRSZ + Maxfdata) | 
|  | job->reply.msize = IOHDRSZ + Maxfdata; | 
|  | else | 
|  | job->reply.msize = job->request.msize; | 
|  | if(strncmp(job->request.version, "9P2000", 6) != 0) | 
|  | sendmsg(job, "unknown 9P version"); | 
|  | else{ | 
|  | job->reply.version = "9P2000"; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | rauth(Job *job) | 
|  | { | 
|  | sendmsg(job, "cs: authentication not required"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  don't flush till all the threads  are done | 
|  | */ | 
|  | void | 
|  | rflush(Job *job) | 
|  | { | 
|  | flushjob(job->request.oldtag); | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  | void | 
|  | rattach(Job *job, Mfile *mf) | 
|  | { | 
|  | if(mf->busy == 0){ | 
|  | mf->busy = 1; | 
|  | mf->user = estrdup(job->request.uname); | 
|  | } | 
|  | mf->qid.vers = vers++; | 
|  | mf->qid.type = QTDIR; | 
|  | mf->qid.path = 0LL; | 
|  | job->reply.qid = mf->qid; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  |  | 
|  | char* | 
|  | rwalk(Job *job, Mfile *mf) | 
|  | { | 
|  | char *err; | 
|  | char **elems; | 
|  | int nelems; | 
|  | int i; | 
|  | Mfile *nmf; | 
|  | struct qid qid; | 
|  |  | 
|  | err = 0; | 
|  | nmf = NULL; | 
|  | elems = job->request.wname; | 
|  | nelems = job->request.nwname; | 
|  | job->reply.nwqid = 0; | 
|  |  | 
|  | if(job->request.newfid != job->request.fid){ | 
|  | /* clone fid */ | 
|  | nmf = newfid(job->request.newfid); | 
|  | if(nmf->busy){ | 
|  | nmf = NULL; | 
|  | err = "clone to used channel"; | 
|  | goto send; | 
|  | } | 
|  | *nmf = *mf; | 
|  | nmf->user = estrdup(mf->user); | 
|  | nmf->fid = job->request.newfid; | 
|  | nmf->qid.vers = vers++; | 
|  | mf = nmf; | 
|  | } | 
|  | /* else nmf will be nil */ | 
|  |  | 
|  | qid = mf->qid; | 
|  | if(nelems > 0){ | 
|  | /* walk fid */ | 
|  | for(i=0; i<nelems && i<MAXWELEM; i++){ | 
|  | if((qid.type & QTDIR) == 0){ | 
|  | err = "not a directory"; | 
|  | break; | 
|  | } | 
|  | if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){ | 
|  | qid.type = QTDIR; | 
|  | qid.path = Qdir; | 
|  | Found: | 
|  | job->reply.wqid[i] = qid; | 
|  | job->reply.nwqid++; | 
|  | continue; | 
|  | } | 
|  | if(strcmp(elems[i], "cs") == 0){ | 
|  | qid.type = QTFILE; | 
|  | qid.path = Qcs; | 
|  | goto Found; | 
|  | } | 
|  | err = "file does not exist"; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | send: | 
|  | if(nmf != NULL && (err!=NULL || job->reply.nwqid<nelems)){ | 
|  | cleanmf(nmf); | 
|  | free(nmf->user); | 
|  | nmf->user = 0; | 
|  | nmf->busy = 0; | 
|  | nmf->fid = 0; | 
|  | } | 
|  | if(err == NULL) | 
|  | mf->qid = qid; | 
|  | sendmsg(job, err); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | void | 
|  | ropen(Job *job, Mfile *mf) | 
|  | { | 
|  | int mode; | 
|  | char *err; | 
|  |  | 
|  | err = 0; | 
|  | mode = job->request.mode; | 
|  | if(mf->qid.type & QTDIR){ | 
|  | if(mode) | 
|  | err = "permission denied"; | 
|  | } | 
|  | job->reply.qid = mf->qid; | 
|  | job->reply.iounit = 0; | 
|  | sendmsg(job, err); | 
|  | } | 
|  |  | 
|  | void | 
|  | rcreate(Job *job, Mfile *mf) | 
|  | { | 
|  | sendmsg(job, "creation permission denied"); | 
|  | } | 
|  |  | 
|  | void | 
|  | rread(Job *job, Mfile *mf) | 
|  | { | 
|  | int i, n, cnt; | 
|  | long off, toff, clock; | 
|  | struct dir dir; | 
|  | uint8_t buf[Maxfdata]; | 
|  | char *err; | 
|  |  | 
|  | n = 0; | 
|  | err = 0; | 
|  | off = job->request.offset; | 
|  | cnt = job->request.count; | 
|  | if(mf->qid.type & QTDIR){ | 
|  | //	clock = time(0); | 
|  | if(off == 0){ | 
|  | memset(&dir, 0, sizeof dir); | 
|  | dir.name = "cs"; | 
|  | dir.qid.type = QTFILE; | 
|  | dir.qid.vers = vers; | 
|  | dir.qid.path = Qcs; | 
|  | dir.mode = 0666; | 
|  | dir.length = 0; | 
|  | dir.uid = mf->user; | 
|  | dir.gid = mf->user; | 
|  | dir.muid = mf->user; | 
|  | dir.atime = clock;	/* wrong */ | 
|  | dir.mtime = clock;	/* wrong */ | 
|  | n = convD2M(&dir, buf, sizeof buf); | 
|  | } | 
|  | job->reply.data = (char*)buf; | 
|  | } else { | 
|  | for(;;){ | 
|  | /* look for an answer at the right offset */ | 
|  | toff = 0; | 
|  | for(i = 0; mf->reply[i] && i < mf->nreply; i++){ | 
|  | n = mf->replylen[i]; | 
|  | if(off < toff + n) | 
|  | break; | 
|  | toff += n; | 
|  | } | 
|  | if(i < mf->nreply) | 
|  | break;		/* got something to return */ | 
|  |  | 
|  | /* try looking up more answers */ | 
|  | if(lookup(mf) == 0){ | 
|  | /* no more */ | 
|  | n = 0; | 
|  | goto send; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* give back a single reply (or part of one) */ | 
|  | job->reply.data = mf->reply[i] + (off - toff); | 
|  | if(cnt > toff - off + n) | 
|  | n = toff - off + n; | 
|  | else | 
|  | n = cnt; | 
|  | } | 
|  | send: | 
|  | job->reply.count = n; | 
|  | sendmsg(job, err); | 
|  | } | 
|  | void | 
|  | cleanmf(Mfile *mf) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if(mf->net != NULL){ | 
|  | free(mf->net); | 
|  | mf->net = NULL; | 
|  | } | 
|  | if(mf->host != NULL){ | 
|  | free(mf->host); | 
|  | mf->host = NULL; | 
|  | } | 
|  | if(mf->serv != NULL){ | 
|  | free(mf->serv); | 
|  | mf->serv = NULL; | 
|  | } | 
|  | if(mf->rem != NULL){ | 
|  | free(mf->rem); | 
|  | mf->rem = NULL; | 
|  | } | 
|  | for(i = 0; i < mf->nreply; i++){ | 
|  | free(mf->reply[i]); | 
|  | mf->reply[i] = NULL; | 
|  | mf->replylen[i] = 0; | 
|  | } | 
|  | mf->nreply = 0; | 
|  | mf->nextnet = netlist; | 
|  | } | 
|  |  | 
|  | void | 
|  | rwrite(Job *job, Mfile *mf) | 
|  | { | 
|  | int cnt, n; | 
|  | char *err; | 
|  | char *field[4]; | 
|  | char curerr[64]; | 
|  |  | 
|  | err = 0; | 
|  | cnt = job->request.count; | 
|  | if(mf->qid.type & QTDIR){ | 
|  | err = "can't write directory"; | 
|  | goto send; | 
|  | } | 
|  | if(cnt >= Maxrequest){ | 
|  | err = "request too long"; | 
|  | goto send; | 
|  | } | 
|  | job->request.data[cnt] = 0; | 
|  | /* | 
|  | *  toggle debugging | 
|  | */ | 
|  | if(strncmp(job->request.data, "debug", 5)==0){ | 
|  | debug ^= 1; | 
|  | fprintf(stderr, "CS:debug %d", debug); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  toggle ipv6 lookups | 
|  | */ | 
|  | if(strncmp(job->request.data, "ipv6", 4)==0){ | 
|  | ipv6lookups ^= 1; | 
|  | fprintf(stderr, "CS:ipv6lookups %d", ipv6lookups); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  toggle debugging | 
|  | */ | 
|  | if(strncmp(job->request.data, "paranoia", 8)==0){ | 
|  | paranoia ^= 1; | 
|  | fprintf(stderr,  "CS:paranoia %d", paranoia); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  add networks to the default list | 
|  | */ | 
|  | if(strncmp(job->request.data, "add ", 4)==0){ | 
|  | if(job->request.data[cnt-1] == '\n') | 
|  | job->request.data[cnt-1] = 0; | 
|  | netadd(job->request.data+4); | 
|  | readipinterfaces(); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  refresh all state | 
|  | */ | 
|  | if(strncmp(job->request.data, "refresh", 7)==0){ | 
|  | netinit(0/*1*/); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | /* start transaction with a clean slate */ | 
|  | cleanmf(mf); | 
|  |  | 
|  | /* | 
|  | *  look for a general query | 
|  | */ | 
|  | if(*job->request.data == '!'){ | 
|  | err = genquery(mf, job->request.data+1); | 
|  | goto send; | 
|  | } | 
|  |  | 
|  | if(debug) | 
|  | fprintf(stderr,  "CS:write %s", job->request.data); | 
|  | if(paranoia) | 
|  | fprintf(stderr,  "CS:write %s by %s", job->request.data, mf->user); | 
|  |  | 
|  | /* | 
|  | *  break up name | 
|  | */ | 
|  | n = getfields(job->request.data, field, 4, 1, "!"); | 
|  | switch(n){ | 
|  | case 1: | 
|  | mf->net = strdup("net"); | 
|  | mf->host = strdup(field[0]); | 
|  | break; | 
|  | case 4: | 
|  | mf->rem = strdup(field[3]); | 
|  | /* fall through */ | 
|  | case 3: | 
|  | mf->serv = strdup(field[2]); | 
|  | /* fall through */ | 
|  | case 2: | 
|  | mf->host = strdup(field[1]); | 
|  | mf->net = strdup(field[0]); | 
|  | break; | 
|  | } | 
|  | /* | 
|  | *  do the first net worth of lookup | 
|  | */ | 
|  | if(lookup(mf) == 0){ | 
|  | snprintf(curerr, sizeof curerr, "%r"); | 
|  | err = curerr; | 
|  | } | 
|  | send: | 
|  | job->reply.count = cnt; | 
|  | sendmsg(job, err); | 
|  | } | 
|  |  | 
|  | void | 
|  | rclunk(Job *job, Mfile *mf) | 
|  | { | 
|  | cleanmf(mf); | 
|  | free(mf->user); | 
|  | mf->user = 0; | 
|  | mf->busy = 0; | 
|  | mf->fid = 0; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  | void | 
|  | rremove(Job *job, Mfile *mf) | 
|  | { | 
|  | sendmsg(job, "remove permission denied"); | 
|  | } | 
|  |  | 
|  | void | 
|  | rstat(Job *job, Mfile *mf) | 
|  | { | 
|  | struct dir dir; | 
|  | uint8_t buf[IOHDRSZ+Maxfdata]; | 
|  |  | 
|  | memset(&dir, 0, sizeof dir); | 
|  | if(mf->qid.type & QTDIR){ | 
|  | dir.name = "."; | 
|  | dir.mode = DMDIR|0555; | 
|  | } else { | 
|  | dir.name = "cs"; | 
|  | dir.mode = 0666; | 
|  | } | 
|  | dir.qid = mf->qid; | 
|  | dir.length = 0; | 
|  | dir.uid = mf->user; | 
|  | dir.gid = mf->user; | 
|  | dir.muid = mf->user; | 
|  | //dir.atime = dir.mtime = time(0); | 
|  | job->reply.nstat = convD2M(&dir, buf, sizeof buf); | 
|  | job->reply.stat = buf; | 
|  | sendmsg(job, 0); | 
|  | } | 
|  |  | 
|  | void | 
|  | rwstat(Job *job, Mfile *mf) | 
|  | { | 
|  | sendmsg(job, "wstat permission denied"); | 
|  | } | 
|  |  | 
|  | void | 
|  | sendmsg(Job *job, char *err) | 
|  | { | 
|  | int n; | 
|  | uint8_t mdata[IOHDRSZ + Maxfdata]; | 
|  | char ename[ERRMAX]; | 
|  |  | 
|  | if(err){ | 
|  | job->reply.type = Rerror; | 
|  | snprintf(ename, sizeof(ename), "cs: %s", err); | 
|  | job->reply.ename = ename; | 
|  | }else{ | 
|  | job->reply.type = job->request.type+1; | 
|  | } | 
|  | job->reply.tag = job->request.tag; | 
|  | n = convS2M(&job->reply, mdata, sizeof mdata); | 
|  | if(n == 1){ | 
|  | fprintf(stderr,  "CS:sendmsg convS2M of %F returns 0", &job->reply); | 
|  | abort(); | 
|  | } | 
|  | //lock(&joblock); | 
|  | if(job->flushed == 0) | 
|  | if(write(mfd[1], mdata, n)!=n) | 
|  | error(1, 0, "%s: %r","mount write"); | 
|  | //unlock(&joblock); | 
|  | if(debug) | 
|  | fprintf(stderr,  "CS:%F %d", &job->reply, n); | 
|  | } | 
|  |  | 
|  | static int | 
|  | isvalidip(uint8_t *ip) | 
|  | { | 
|  | return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0; | 
|  | } | 
|  |  | 
|  | static uint8_t loopbacknet[IPaddrlen] = { | 
|  | 0, 0, 0, 0, | 
|  | 0, 0, 0, 0, | 
|  | 0, 0, 0xff, 0xff, | 
|  | 127, 0, 0, 0 | 
|  | }; | 
|  | static uint8_t loopbackmask[IPaddrlen] = { | 
|  | 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0xff, 0xff, 0xff, | 
|  | 0xff, 0, 0, 0 | 
|  | }; | 
|  |  | 
|  | void | 
|  | readipinterfaces(void) | 
|  | { | 
|  | if(myipaddr(ipa, mntpt) != 0) | 
|  | ipmove(ipa, IPnoaddr); | 
|  | snprintf(ipaddr, sizeof(ipaddr), "%I", ipa); | 
|  | if (debug) | 
|  | fprintf(stderr, "CS:dns", "ipaddr is %s\n", ipaddr); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  get the system name | 
|  | */ | 
|  | void | 
|  | ipid(void) | 
|  | { | 
|  | uint8_t addr[6]; | 
|  | struct ndbtuple *t, *tt; | 
|  | char *p, *attr; | 
|  | struct ndbs s; | 
|  | int f; | 
|  | char buf[Maxpath]; | 
|  |  | 
|  | /* use environment, ether addr, or ipaddr to get system name */ | 
|  | if(mysysname == 0){ | 
|  | /* | 
|  | *  environment has priority. | 
|  | * | 
|  | *  on the sgi power the default system name | 
|  | *  is the ip address.  ignore that. | 
|  | * | 
|  | */ | 
|  | p = getenv("sysname"); | 
|  | if(p && *p){ | 
|  | attr = ipattr(p); | 
|  | if(strcmp(attr, "ip") != 0) | 
|  | mysysname = strdup(p); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  the /net/ndb contains what the network | 
|  | *  figured out from DHCP.  use that name if | 
|  | *  there is one. | 
|  | */ | 
|  | if(mysysname == 0 && netdb != NULL){ | 
|  | ndbreopen(netdb); | 
|  | for(tt = t = ndbparse(netdb); t != NULL; t = t->entry){ | 
|  | if(strcmp(t->attr, "sys") == 0){ | 
|  | mysysname = strdup(t->val); | 
|  | break; | 
|  | } | 
|  | } | 
|  | ndbfree(tt); | 
|  | } | 
|  |  | 
|  | /* next network database, ip address, and ether address to find a name */ | 
|  | if(mysysname == 0){ | 
|  | t = NULL; | 
|  | if(isvalidip(ipa)) | 
|  | free(ndbgetvalue(db, &s, "ip", ipaddr, "sys", &t)); | 
|  | if(t == NULL){ | 
|  | for(f = 0; f < 3; f++){ | 
|  | snprintf(buf, sizeof buf, "%s/ether%d", mntpt, f); | 
|  | if(myetheraddr(addr, buf) >= 0){ | 
|  | snprintf(eaddr, sizeof(eaddr), "%E", addr); | 
|  | free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t)); | 
|  | if(t != NULL) | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | for(tt = t; tt != NULL; tt = tt->entry){ | 
|  | if(strcmp(tt->attr, "sys") == 0){ | 
|  | mysysname = strdup(tt->val); | 
|  | break; | 
|  | } | 
|  | } | 
|  | ndbfree(t); | 
|  | } | 
|  |  | 
|  | /* nothing else worked, use the ip address */ | 
|  | if(mysysname == 0 && isvalidip(ipa)) | 
|  | mysysname = strdup(ipaddr); | 
|  |  | 
|  |  | 
|  | /* set /dev/sysname if we now know it */ | 
|  | if(mysysname){ | 
|  | f = open("/dev/sysname", O_RDWR); | 
|  | if(f >= 0){ | 
|  | write(f, mysysname, strlen(mysysname)); | 
|  | close(f); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  Set up a list of default networks by looking for | 
|  | *  /net/^*^/clone. | 
|  | *  For now, never background. | 
|  | */ | 
|  | void | 
|  | netinit(int background) | 
|  | { | 
|  | char clone[Maxpath]; | 
|  | Network *np; | 
|  | static int working; | 
|  |  | 
|  | /* add the mounted networks to the default list */ | 
|  | for(np = network; np->net; np++){ | 
|  | int fuckup; | 
|  | if(np->considered) | 
|  | continue; | 
|  | snprintf(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net); | 
|  | fuckup = open(clone, O_RDONLY); | 
|  | if (fuckup < 0) | 
|  | continue; | 
|  | close(fuckup); | 
|  | //if(access(clone, R_OK)) | 
|  | //continue; | 
|  | if(netlist) | 
|  | last->next = np; | 
|  | else | 
|  | netlist = np; | 
|  | last = np; | 
|  | np->next = 0; | 
|  | np->considered = 1; | 
|  | } | 
|  |  | 
|  | /* find out what our ip address is */ | 
|  | readipinterfaces(); | 
|  |  | 
|  | /* set the system name if we need to, these days ip is all we have */ | 
|  | ipid(); | 
|  |  | 
|  | if(debug) | 
|  | fprintf(stderr, logfile, "CS:mysysname %s eaddr %s ipaddr %s ipa %I\n", | 
|  | mysysname?mysysname:"???", eaddr, ipaddr, ipa); | 
|  |  | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  add networks to the standard list | 
|  | */ | 
|  | void | 
|  | netadd(char *p) | 
|  | { | 
|  | Network *np; | 
|  | char *field[12]; | 
|  | int i, n; | 
|  |  | 
|  | n = getfields(p, field, 12, 1, " "); | 
|  | for(i = 0; i < n; i++){ | 
|  | for(np = network; np->net; np++){ | 
|  | if(strcmp(field[i], np->net) != 0) | 
|  | continue; | 
|  | if(np->considered) | 
|  | break; | 
|  | if(netlist) | 
|  | last->next = np; | 
|  | else | 
|  | netlist = np; | 
|  | last = np; | 
|  | np->next = 0; | 
|  | np->considered = 1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | lookforproto(struct ndbtuple *t, char *proto) | 
|  | { | 
|  | for(; t != NULL; t = t->entry) | 
|  | if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup a request.  the network "net" means we should pick the | 
|  | *  best network to get there. | 
|  | */ | 
|  | int | 
|  | lookup(Mfile *mf) | 
|  | { | 
|  | Network *np; | 
|  | char *cp; | 
|  | struct ndbtuple *nt, *t; | 
|  | char reply[Maxreply]; | 
|  | int i, rv; | 
|  | int hack; | 
|  |  | 
|  | /* open up the standard db files */ | 
|  | if(db == 0) | 
|  | ndbinit(); | 
|  | if(db == 0) | 
|  | error(1, 0, "%s: %r","can't open mf->network database\n"); | 
|  |  | 
|  | rv = 0; | 
|  |  | 
|  | if(mf->net == NULL) | 
|  | return 0;	/* must have been a genquery */ | 
|  |  | 
|  | if(strcmp(mf->net, "net") == 0){ | 
|  | /* | 
|  | *  go through set of default nets | 
|  | */ | 
|  | for(np = mf->nextnet; np; np = np->next){ | 
|  | nt = (*np->lookup)(np, mf->host, mf->serv, 1); | 
|  | if(nt == NULL) | 
|  | continue; | 
|  | hack = np->fasttimeouthack && !lookforproto(nt, np->net); | 
|  | for(t = nt; mf->nreply < Nreply && t; t = t->entry){ | 
|  | cp = (*np->trans)(t, np, mf->serv, mf->rem, hack); | 
|  | if(cp){ | 
|  | /* avoid duplicates */ | 
|  | for(i = 0; i < mf->nreply; i++) | 
|  | if(strcmp(mf->reply[i], cp) == 0) | 
|  | break; | 
|  | if(i == mf->nreply){ | 
|  | /* save the reply */ | 
|  | mf->replylen[mf->nreply] = strlen(cp); | 
|  | mf->reply[mf->nreply++] = cp; | 
|  | rv++; | 
|  | } | 
|  | } | 
|  | } | 
|  | ndbfree(nt); | 
|  | np = np->next; | 
|  | break; | 
|  | } | 
|  | mf->nextnet = np; | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  if not /net, we only get one lookup | 
|  | */ | 
|  | if(mf->nreply != 0) | 
|  | return 0; | 
|  | /* | 
|  | *  look for a specific network | 
|  | */ | 
|  | for(np = netlist; np && np->net != NULL; np++){ | 
|  | if(np->fasttimeouthack) | 
|  | continue; | 
|  | if(strcmp(np->net, mf->net) == 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(np && np->net != NULL){ | 
|  | /* | 
|  | *  known network | 
|  | */ | 
|  | nt = (*np->lookup)(np, mf->host, mf->serv, 1); | 
|  | for(t = nt; mf->nreply < Nreply && t; t = t->entry){ | 
|  | cp = (*np->trans)(t, np, mf->serv, mf->rem, 0); | 
|  | if(cp){ | 
|  | mf->replylen[mf->nreply] = strlen(cp); | 
|  | mf->reply[mf->nreply++] = cp; | 
|  | rv++; | 
|  | } | 
|  | } | 
|  | ndbfree(nt); | 
|  | return rv; | 
|  | } else { | 
|  | /* | 
|  | *  not a known network, don't translate host or service | 
|  | */ | 
|  | if(mf->serv) | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s", | 
|  | mntpt, mf->net, mf->host, mf->serv); | 
|  | else | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s", | 
|  | mntpt, mf->net, mf->host); | 
|  | mf->reply[0] = strdup(reply); | 
|  | mf->replylen[0] = strlen(reply); | 
|  | mf->nreply = 1; | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  translate an ip service name into a port number.  If it's a numeric port | 
|  | *  number, look for restricted access. | 
|  | * | 
|  | *  the service '*' needs no translation. | 
|  | */ | 
|  | char* | 
|  | ipserv(Network *np, char *name, char *buf, int blen) | 
|  | { | 
|  | char *p; | 
|  | int alpha = 0; | 
|  | int restr = 0; | 
|  | char port[10]; | 
|  | struct ndbtuple *t, *nt; | 
|  | struct ndbs s; | 
|  |  | 
|  | /* '*' means any service */ | 
|  | if(strcmp(name, "*")==0){ | 
|  | strcpy(buf, name); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | /*  see if it's numeric or symbolic */ | 
|  | port[0] = 0; | 
|  | for(p = name; *p; p++){ | 
|  | if(isdigit(*p)) | 
|  | {} | 
|  | else if(isalpha(*p) || *p == '-' || *p == '$') | 
|  | alpha = 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  | t = NULL; | 
|  | p = NULL; | 
|  | if(alpha){ | 
|  | p = ndbgetvalue(db, &s, np->net, name, "port", &t); | 
|  | if(p == NULL) | 
|  | return 0; | 
|  | } else { | 
|  | /* look up only for tcp ports < 1024 to get the restricted | 
|  | * attribute | 
|  | */ | 
|  | if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0) | 
|  | p = ndbgetvalue(db, &s, "port", name, "port", &t); | 
|  | if(p == NULL) | 
|  | p = strdup(name); | 
|  | } | 
|  |  | 
|  | if(t){ | 
|  | for(nt = t; nt; nt = nt->entry) | 
|  | if(strcmp(nt->attr, "restricted") == 0) | 
|  | restr = 1; | 
|  | ndbfree(t); | 
|  | } | 
|  | snprintf(buf, blen, "%s%s", p, restr ? "!r" : ""); | 
|  | free(p); | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup an ip attribute | 
|  | */ | 
|  | int | 
|  | ipattrlookup(struct ndb *db, char *ipa, char *attr, char *val, int vlen) | 
|  | { | 
|  |  | 
|  | struct ndbtuple *t, *nt; | 
|  | char *alist[2]; | 
|  |  | 
|  | alist[0] = attr; | 
|  | t = ndbipinfo(db, "ip", ipa, alist, 1); | 
|  | if(t == NULL) | 
|  | return 0; | 
|  | for(nt = t; nt != NULL; nt = nt->entry){ | 
|  | if(strcmp(nt->attr, attr) == 0){ | 
|  | nstrcpy(val, nt->val, vlen); | 
|  | ndbfree(t); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* we shouldn't get here */ | 
|  | ndbfree(t); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup (and translate) an ip destination | 
|  | */ | 
|  | struct ndbtuple* | 
|  | iplookup(Network *np, char *host, char *serv, int nolookup) | 
|  | { | 
|  | char *attr, *dnsname; | 
|  | struct ndbtuple *t, *nt; | 
|  | struct ndbs s; | 
|  | char ts[Maxservice]; | 
|  | char dollar[Maxhost]; | 
|  | uint8_t ip[IPaddrlen]; | 
|  | uint8_t net[IPaddrlen]; | 
|  | uint8_t tnet[IPaddrlen]; | 
|  | struct ipifc *ifc; | 
|  | struct iplifc *lifc; | 
|  |  | 
|  | /* | 
|  | *  start with the service since it's the most likely to fail | 
|  | *  and costs the least | 
|  | */ | 
|  | werrstr("can't translate address"); | 
|  | if(serv==0 || ipserv(np, serv, ts, sizeof ts) == 0){ | 
|  | werrstr("can't translate service"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* for dial strings with no host */ | 
|  | if(strcmp(host, "*") == 0) | 
|  | return ndbnew("ip", "*"); | 
|  |  | 
|  | /* | 
|  | *  hack till we go v6 :: = 0.0.0.0 | 
|  | */ | 
|  | if(strcmp("::", host) == 0) | 
|  | return ndbnew("ip", "*"); | 
|  |  | 
|  | /* | 
|  | *  '$' means the rest of the name is an attribute that we | 
|  | *  need to search for | 
|  | */ | 
|  | if(*host == '$'){ | 
|  | if(ipattrlookup(db, ipaddr, host+1, dollar, sizeof dollar)) | 
|  | host = dollar; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  turn '[ip address]' into just 'ip address' | 
|  | */ | 
|  | if(*host == '[' && host[strlen(host)-1] == ']'){ | 
|  | host++; | 
|  | host[strlen(host)-1] = 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  just accept addresses | 
|  | */ | 
|  | attr = ipattr(host); | 
|  | if(strcmp(attr, "ip") == 0) | 
|  | return ndbnew("ip", host); | 
|  |  | 
|  | /* | 
|  | *  give the domain name server the first opportunity to | 
|  | *  resolve domain names.  if that fails try the database. | 
|  | */ | 
|  | t = 0; | 
|  | werrstr("can't translate address"); | 
|  | if(strcmp(attr, "dom") == 0) | 
|  | t = dnsiplookup(host, &s); | 
|  | if(t == 0) | 
|  | free(ndbgetvalue(db, &s, attr, host, "ip", &t)); | 
|  | if(t == 0){ | 
|  | dnsname = ndbgetvalue(db, &s, attr, host, "dom", NULL); | 
|  | if(dnsname){ | 
|  | t = dnsiplookup(dnsname, &s); | 
|  | free(dnsname); | 
|  | } | 
|  | } | 
|  | if(t == 0) | 
|  | t = dnsiplookup(host, &s); | 
|  | if(t == 0) | 
|  | return 0; | 
|  |  | 
|  | /* | 
|  | *  reorder the tuple to have the matched line first and | 
|  | *  save that in the request structure. | 
|  | */ | 
|  | t = reorder(t, s.t); | 
|  |  | 
|  | /* | 
|  | * reorder according to our interfaces | 
|  | */ | 
|  | //lock(&ipifclock); | 
|  | for(ifc = ipifcs; ifc != NULL; ifc = ifc->next){ | 
|  | for(lifc = ifc->lifc; lifc != NULL; lifc = lifc->next){ | 
|  | maskip(lifc->ip, lifc->mask, net); | 
|  | for(nt = t; nt; nt = nt->entry){ | 
|  | if(strcmp(nt->attr, "ip") != 0) | 
|  | continue; | 
|  | parseip(ip, nt->val); | 
|  | maskip(ip, lifc->mask, tnet); | 
|  | if(memcmp(net, tnet, IPaddrlen) == 0){ | 
|  | t = reorder(t, nt); | 
|  | //unlock(&ipifclock); | 
|  | return t; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | //unlock(&ipifclock); | 
|  |  | 
|  | return t; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  translate an ip address | 
|  | */ | 
|  | char* | 
|  | iptrans(struct ndbtuple *t, Network *np, char *serv, char *rem, int hack) | 
|  | { | 
|  | char ts[Maxservice]; | 
|  | char reply[Maxreply]; | 
|  | char x[Maxservice]; | 
|  |  | 
|  | if(strcmp(t->attr, "ip") != 0) | 
|  | return 0; | 
|  |  | 
|  | if(serv == 0 || ipserv(np, serv, ts, sizeof ts) == 0){ | 
|  | werrstr("can't translate service"); | 
|  | return 0; | 
|  | } | 
|  | if(rem != NULL) | 
|  | snprintf(x, sizeof(x), "!%s", rem); | 
|  | else | 
|  | *x = 0; | 
|  |  | 
|  | if(*t->val == '*') | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s%s", | 
|  | mntpt, np->net, ts, x); | 
|  | else | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s", | 
|  | mntpt, np->net, t->val, ts, x, hack? "!fasttimeout": ""); | 
|  |  | 
|  | return strdup(reply); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  lookup a telephone number | 
|  | */ | 
|  | struct ndbtuple* | 
|  | telcolookup(Network *np, char *host, char *serv, int nolookup) | 
|  | { | 
|  | struct ndbtuple *t; | 
|  | struct ndbs s; | 
|  |  | 
|  | werrstr("can't translate address"); | 
|  | free(ndbgetvalue(db, &s, "sys", host, "telco", &t)); | 
|  | if(t == 0) | 
|  | return ndbnew("telco", host); | 
|  |  | 
|  | return reorder(t, s.t); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  translate a telephone address | 
|  | */ | 
|  | char* | 
|  | telcotrans(struct ndbtuple *t, Network *np, char *serv, char *rem, int unused) | 
|  | { | 
|  | char reply[Maxreply]; | 
|  | char x[Maxservice]; | 
|  |  | 
|  | if(strcmp(t->attr, "telco") != 0) | 
|  | return 0; | 
|  |  | 
|  | if(rem != NULL) | 
|  | snprintf(x, sizeof(x), "!%s", rem); | 
|  | else | 
|  | *x = 0; | 
|  | if(serv) | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net, | 
|  | t->val, serv, x); | 
|  | else | 
|  | snprintf(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net, | 
|  | t->val, x); | 
|  | return strdup(reply); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  reorder the tuple to put x's line first in the entry | 
|  | */ | 
|  | struct ndbtuple* | 
|  | reorder(struct ndbtuple *t, struct ndbtuple *x) | 
|  | { | 
|  | struct ndbtuple *nt; | 
|  | struct ndbtuple *line; | 
|  |  | 
|  | /* find start of this entry's line */ | 
|  | for(line = x; line->entry == line->line; line = line->line) | 
|  | ; | 
|  | line = line->line; | 
|  | if(line == t) | 
|  | return t;	/* already the first line */ | 
|  |  | 
|  | /* remove this line and everything after it from the entry */ | 
|  | for(nt = t; nt->entry != line; nt = nt->entry) | 
|  | ; | 
|  | nt->entry = 0; | 
|  |  | 
|  | /* make that the start of the entry */ | 
|  | for(nt = line; nt->entry; nt = nt->entry) | 
|  | ; | 
|  | nt->entry = t; | 
|  | return line; | 
|  | } | 
|  |  | 
|  | static struct ndbtuple* | 
|  | dnsip6lookup(char *mntpt, char *buf, struct ndbtuple *t) | 
|  | { | 
|  | struct ndbtuple *t6, *tt; | 
|  |  | 
|  | t6 = dnsquery(mntpt, buf, "ipv6");	/* lookup AAAA dns RRs */ | 
|  | if (t6 == NULL) | 
|  | return t; | 
|  |  | 
|  | /* convert ipv6 attr to ip */ | 
|  | for (tt = t6; tt != NULL; tt = tt->entry) | 
|  | if (strcmp(tt->attr, "ipv6") == 0) | 
|  | strncpy(tt->attr, "ip", sizeof tt->attr - 1); | 
|  |  | 
|  | if (t == NULL) | 
|  | return t6; | 
|  |  | 
|  | /* append t6 list to t list */ | 
|  | for (tt = t; tt->entry != NULL; tt = tt->entry) | 
|  | ; | 
|  | tt->entry = t6; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  call the dns process and have it try to translate a name | 
|  | */ | 
|  | struct ndbtuple* | 
|  | dnsiplookup(char *host, struct ndbs *s) | 
|  | { | 
|  | char buf[Maxreply]; | 
|  | struct ndbtuple *t; | 
|  |  | 
|  | //unlock(&dblock); | 
|  |  | 
|  | /* save the name */ | 
|  | snprintf(buf, sizeof(buf), "%s", host); | 
|  |  | 
|  | if(strcmp(ipattr(buf), "ip") == 0) | 
|  | t = dnsquery(mntpt, buf, "ptr"); | 
|  | else { | 
|  | t = dnsquery(mntpt, buf, "ip"); | 
|  | /* special case: query ipv6 (AAAA dns RR) too */ | 
|  | if (ipv6lookups) | 
|  | t = dnsip6lookup(mntpt, buf, t); | 
|  | } | 
|  | s->t = t; | 
|  |  | 
|  | if(t == NULL){ | 
|  | snprintf(buf, sizeof buf, "%r"); | 
|  | if(strstr(buf, "exist")) | 
|  | werrstr("can't translate address: %s", buf); | 
|  | else if(strstr(buf, "dns failure")) | 
|  | werrstr("temporary problem: %s", buf); | 
|  | } | 
|  |  | 
|  | //lock(&dblock); | 
|  | return t; | 
|  | } | 
|  |  | 
|  | int | 
|  | qmatch(struct ndbtuple *t, char **attr, char **val, int n) | 
|  | { | 
|  | int i, found; | 
|  | struct ndbtuple *nt; | 
|  |  | 
|  | for(i = 1; i < n; i++){ | 
|  | found = 0; | 
|  | for(nt = t; nt; nt = nt->entry) | 
|  | if(strcmp(attr[i], nt->attr) == 0) | 
|  | if(strcmp(val[i], "*") == 0 | 
|  | || strcmp(val[i], nt->val) == 0){ | 
|  | found = 1; | 
|  | break; | 
|  | } | 
|  | if(found == 0) | 
|  | break; | 
|  | } | 
|  | return i == n; | 
|  | } | 
|  |  | 
|  | /* this is awful but I don't want to bring in libstring just for this. | 
|  | * you want real strings don't use C | 
|  | */ | 
|  | void | 
|  | qreply(Mfile *mf, struct ndbtuple *t) | 
|  | { | 
|  | struct ndbtuple *nt; | 
|  | char *s, *cur; | 
|  | int len, amt; | 
|  |  | 
|  | s = malloc(4096); | 
|  | cur = s; | 
|  | len = 4096; | 
|  |  | 
|  | for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){ | 
|  | amt = snprintf(cur, len, "%s=%s", nt->attr, nt->val); | 
|  |  | 
|  | if (amt < 0) | 
|  | len = 0; | 
|  | else { | 
|  | len -= amt; | 
|  | cur += amt; | 
|  | } | 
|  |  | 
|  | if(nt->line != nt->entry){ | 
|  | mf->replylen[mf->nreply] = strlen(s); | 
|  | mf->reply[mf->nreply++] = strdup(s); | 
|  | cur = s; | 
|  | len = 4096; | 
|  | } else { | 
|  | amt = snprintf(cur, len, " "); | 
|  | if (amt < 0) | 
|  | len = 0; | 
|  | else { | 
|  | len -= amt; | 
|  | cur += amt; | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | free(s); | 
|  | } | 
|  |  | 
|  | enum | 
|  | { | 
|  | Maxattr=	32, | 
|  | }; | 
|  |  | 
|  | /* | 
|  | *  generic query lookup.  The query is of one of the following | 
|  | *  forms: | 
|  | * | 
|  | *  attr1=val1 attr2=val2 attr3=val3 ... | 
|  | * | 
|  | *  returns the matching tuple | 
|  | * | 
|  | *  ipinfo attr=val attr1 attr2 attr3 ... | 
|  | * | 
|  | *  is like ipinfo and returns the attr{1-n} | 
|  | *  associated with the ip address. | 
|  | */ | 
|  | char* | 
|  | genquery(Mfile *mf, char *query) | 
|  | { | 
|  | int i, n; | 
|  | char *p; | 
|  | char *attr[Maxattr]; | 
|  | char *val[Maxattr]; | 
|  | struct ndbtuple *t; | 
|  | struct ndbs s; | 
|  |  | 
|  | n = getfields(query, attr, ARRAY_SIZE(attr), 1, " "); | 
|  | if(n == 0) | 
|  | return "bad query"; | 
|  |  | 
|  | if(strcmp(attr[0], "ipinfo") == 0) | 
|  | return ipinfoquery(mf, attr, n); | 
|  |  | 
|  | /* parse pairs */ | 
|  | for(i = 0; i < n; i++){ | 
|  | p = strchr(attr[i], '='); | 
|  | if(p == 0) | 
|  | return "bad query"; | 
|  | *p++ = 0; | 
|  | val[i] = p; | 
|  | } | 
|  |  | 
|  | /* give dns a chance */ | 
|  | if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){ | 
|  | t = dnsiplookup(val[0], &s); | 
|  | if(t){ | 
|  | if(qmatch(t, attr, val, n)){ | 
|  | qreply(mf, t); | 
|  | ndbfree(t); | 
|  | return 0; | 
|  | } | 
|  | ndbfree(t); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* first pair is always the key.  It can't be a '*' */ | 
|  | t = ndbsearch(db, &s, attr[0], val[0]); | 
|  |  | 
|  | /* search is the and of all the pairs */ | 
|  | while(t){ | 
|  | if(qmatch(t, attr, val, n)){ | 
|  | qreply(mf, t); | 
|  | ndbfree(t); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | ndbfree(t); | 
|  | t = ndbsnext(&s, attr[0], val[0]); | 
|  | } | 
|  |  | 
|  | return "no match"; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  resolve an ip address | 
|  | */ | 
|  | static struct ndbtuple* | 
|  | ipresolve(char *attr, char *host) | 
|  | { | 
|  | struct ndbtuple *t, *nt, **l; | 
|  |  | 
|  | t = iplookup(&network[Ntcp], host, "*", 0); | 
|  | for(l = &t; *l != NULL; ){ | 
|  | nt = *l; | 
|  | if(strcmp(nt->attr, "ip") != 0){ | 
|  | *l = nt->entry; | 
|  | nt->entry = NULL; | 
|  | ndbfree(nt); | 
|  | continue; | 
|  | } | 
|  | strcpy(nt->attr, attr); | 
|  | l = &nt->entry; | 
|  | } | 
|  | return t; | 
|  | } | 
|  |  | 
|  | char* | 
|  | ipinfoquery(Mfile *mf, char **list, int n) | 
|  | { | 
|  | int i, nresolve; | 
|  | int resolve[Maxattr]; | 
|  | struct ndbtuple *t, *nt, **l; | 
|  | char *attr, *val; | 
|  |  | 
|  | /* skip 'ipinfo' */ | 
|  | list++; n--; | 
|  |  | 
|  | if(n < 1) | 
|  | return "bad query"; | 
|  |  | 
|  | /* get search attribute=value, or assume ip=myipaddr */ | 
|  | attr = *list; | 
|  | if((val = strchr(attr, '=')) != NULL){ | 
|  | *val++ = 0; | 
|  | list++; | 
|  | n--; | 
|  | }else{ | 
|  | attr = "ip"; | 
|  | val = ipaddr; | 
|  | } | 
|  |  | 
|  | if(n < 1) | 
|  | return "bad query"; | 
|  |  | 
|  | /* | 
|  | *  don't let ndbipinfo resolve the addresses, we're | 
|  | *  better at it. | 
|  | */ | 
|  | nresolve = 0; | 
|  | for(i = 0; i < n; i++) | 
|  | if(*list[i] == '@'){		/* @attr=val ? */ | 
|  | list[i]++; | 
|  | resolve[i] = 1;		/* we'll resolve it */ | 
|  | nresolve++; | 
|  | } else | 
|  | resolve[i] = 0; | 
|  |  | 
|  | t = ndbipinfo(db, attr, val, list, n); | 
|  | if(t == NULL) | 
|  | return "no match"; | 
|  |  | 
|  | if(nresolve != 0){ | 
|  | for(l = &t; *l != NULL;){ | 
|  | nt = *l; | 
|  |  | 
|  | /* already an address? */ | 
|  | if(strcmp(ipattr(nt->val), "ip") == 0){ | 
|  | l = &(*l)->entry; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* user wants it resolved? */ | 
|  | for(i = 0; i < n; i++) | 
|  | if(strcmp(list[i], nt->attr) == 0) | 
|  | break; | 
|  | if(i >= n || resolve[i] == 0){ | 
|  | l = &(*l)->entry; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* resolve address and replace entry */ | 
|  | *l = ipresolve(nt->attr, nt->val); | 
|  | while(*l != NULL) | 
|  | l = &(*l)->entry; | 
|  | *l = nt->entry; | 
|  |  | 
|  | nt->entry = NULL; | 
|  | ndbfree(nt); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* make it all one line */ | 
|  | for(nt = t; nt != NULL; nt = nt->entry){ | 
|  | if(nt->entry == NULL) | 
|  | nt->line = t; | 
|  | else | 
|  | nt->line = nt->entry; | 
|  | } | 
|  |  | 
|  | qreply(mf, t); | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void* | 
|  | emalloc(int size) | 
|  | { | 
|  | void *x; | 
|  |  | 
|  | x = calloc(size, 1); | 
|  | if(x == NULL) | 
|  | abort(); | 
|  | memset(x, 0, size); | 
|  | return x; | 
|  | } | 
|  |  | 
|  | char* | 
|  | estrdup(char *s) | 
|  | { | 
|  | int size; | 
|  | char *p; | 
|  |  | 
|  | size = strlen(s)+1; | 
|  | p = calloc(size, 1); | 
|  | if(p == NULL) | 
|  | abort(); | 
|  | memmove(p, s, size); | 
|  | return p; | 
|  | } |