Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <auth.h>
4
#include <fcall.h>
5
#include <bio.h>
6
#include <ctype.h>
7
#include <ndb.h>
8
#include <ip.h>
9
#include <String.h>
10
 
11
enum
12
{
13
	Nreply=			20,
14
	Maxreply=		256,
15
	Maxrequest=		128,
16
	Maxpath=		128,
17
	Maxfdata=		8192,
18
	Maxhost=		64,		/* maximum host name size */
19
	Maxservice=		64,		/* maximum service name size */
20
 
21
	Qdir=			0,
22
	Qcs=			1,
23
};
24
 
25
typedef struct Mfile	Mfile;
26
typedef struct Mlist	Mlist;
27
typedef struct Network	Network;
28
typedef struct Flushreq	Flushreq;
29
typedef struct Job	Job;
30
 
31
int vers;		/* incremented each clone/attach */
32
 
33
struct Mfile
34
{
35
	int		busy;
36
 
37
	char		*user;
38
	Qid		qid;
39
	int		fid;
40
 
41
	/*
42
	 *  current request
43
	 */
44
	char		*net;
45
	char		*host;
46
	char		*serv;
47
	char		*rem;
48
 
49
	/*
50
	 *  result of the last lookup
51
	 */
52
	Network		*nextnet;
53
	int		nreply;
54
	char		*reply[Nreply];
55
	int		replylen[Nreply];
56
};
57
 
58
struct Mlist
59
{
60
	Mlist	*next;
61
	Mfile	mf;
62
};
63
 
64
 
65
/*
66
 *  active requests
67
 */
68
struct Job
69
{
70
	Job	*next;
71
	int	flushed;
72
	Fcall	request;
73
	Fcall	reply;
74
};
75
Lock	joblock;
76
Job	*joblist;
77
 
78
Mlist	*mlist;
79
int	mfd[2];
80
int	debug;
81
int	paranoia;
82
int	ipv6lookups = 1;
83
jmp_buf	masterjmp;	/* return through here after a slave process has been created */
84
int	*isslave;	/* *isslave non-zero means this is a slave process */
85
char	*dbfile;
86
Ndb	*db, *netdb;
87
 
88
void	rversion(Job*);
89
void	rflush(Job*);
90
void	rattach(Job*, Mfile*);
91
char*	rwalk(Job*, Mfile*);
92
void	ropen(Job*, Mfile*);
93
void	rcreate(Job*, Mfile*);
94
void	rread(Job*, Mfile*);
95
void	rwrite(Job*, Mfile*);
96
void	rclunk(Job*, Mfile*);
97
void	rremove(Job*, Mfile*);
98
void	rstat(Job*, Mfile*);
99
void	rwstat(Job*, Mfile*);
100
void	rauth(Job*);
101
void	sendmsg(Job*, char*);
102
void	error(char*);
103
void	mountinit(char*, char*);
104
void	io(void);
105
void	ndbinit(void);
106
void	netinit(int);
107
void	netadd(char*);
108
char	*genquery(Mfile*, char*);
109
char*	ipinfoquery(Mfile*, char**, int);
110
int	needproto(Network*, Ndbtuple*);
111
int	lookup(Mfile*);
112
Ndbtuple*	reorder(Ndbtuple*, Ndbtuple*);
113
void	ipid(void);
114
void	readipinterfaces(void);
115
void*	emalloc(int);
116
char*	estrdup(char*);
117
Job*	newjob(void);
118
void	freejob(Job*);
119
void	setext(char*, int, char*);
120
void	cleanmf(Mfile*);
121
 
122
extern void	paralloc(void);
123
 
124
Lock	dblock;		/* mutex on database operations */
125
Lock	netlock;	/* mutex for netinit() */
126
 
127
char	*logfile = "cs";
128
char	*paranoiafile = "cs.paranoia";
129
 
130
char	mntpt[Maxpath];
131
char	netndb[Maxpath];
132
 
133
/*
134
 *  Network specific translators
135
 */
136
Ndbtuple*	iplookup(Network*, char*, char*, int);
137
char*		iptrans(Ndbtuple*, Network*, char*, char*, int);
138
Ndbtuple*	telcolookup(Network*, char*, char*, int);
139
char*		telcotrans(Ndbtuple*, Network*, char*, char*, int);
140
Ndbtuple*	dnsiplookup(char*, Ndbs*);
141
 
142
struct Network
143
{
144
	char		*net;
145
	Ndbtuple	*(*lookup)(Network*, char*, char*, int);
146
	char		*(*trans)(Ndbtuple*, Network*, char*, char*, int);
147
	int		considered;		/* flag: ignored for "net!"? */
148
	int		fasttimeouthack;	/* flag. was for IL */
149
	Network		*next;
150
};
151
 
152
enum
153
{
154
	Ntcp = 0,
155
};
156
 
157
/*
158
 *  net doesn't apply to (r)udp, icmp(v6), or telco (for speed).
159
 */
160
Network network[] = {
161
[Ntcp]	{ "tcp",	iplookup,	iptrans,	0 },
162
	{ "udp",	iplookup,	iptrans,	1 },
163
	{ "icmp",	iplookup,	iptrans,	1 },
164
	{ "icmpv6",	iplookup,	iptrans,	1 },
165
	{ "rudp",	iplookup,	iptrans,	1 },
166
	{ "ssh",	iplookup,	iptrans,	1 },
167
	{ "telco",	telcolookup,	telcotrans,	1 },
168
	{ 0 },
169
};
170
 
171
Lock ipifclock;
172
Ipifc *ipifcs;
173
 
174
char	eaddr[16];		/* ascii ethernet address */
175
char	ipaddr[64];		/* ascii internet address */
176
uchar	ipa[IPaddrlen];		/* binary internet address */
177
char	*mysysname;
178
 
179
Network *netlist;		/* networks ordered by preference */
180
Network *last;
181
 
182
static void
183
nstrcpy(char *to, char *from, int len)
184
{
185
	strncpy(to, from, len);
186
	to[len-1] = 0;
187
}
188
 
189
void
190
usage(void)
191
{
192
	fprint(2, "usage: %s [-dn] [-f ndb-file] [-x netmtpt]\n", argv0);
193
	exits("usage");
194
}
195
 
196
/*
197
 * based on libthread's threadsetname, but drags in less library code.
198
 * actually just sets the arguments displayed.
199
 */
200
void
201
procsetname(char *fmt, ...)
202
{
203
	int fd;
204
	char *cmdname;
205
	char buf[128];
206
	va_list arg;
207
 
208
	va_start(arg, fmt);
209
	cmdname = vsmprint(fmt, arg);
210
	va_end(arg);
211
	if (cmdname == nil)
212
		return;
213
	snprint(buf, sizeof buf, "#p/%d/args", getpid());
214
	if((fd = open(buf, OWRITE)) >= 0){
215
		write(fd, cmdname, strlen(cmdname)+1);
216
		close(fd);
217
	}
218
	free(cmdname);
219
}
220
 
221
void
222
main(int argc, char *argv[])
223
{
224
	int justsetname;
225
	char ext[Maxpath], servefile[Maxpath];
226
 
227
	justsetname = 0;
228
	setnetmtpt(mntpt, sizeof(mntpt), nil);
229
	ext[0] = 0;
230
	ARGBEGIN{
231
	case '4':
232
		ipv6lookups = 0;
233
		break;
234
	case 'd':
235
		debug = 1;
236
		break;
237
	case 'f':
238
		dbfile = EARGF(usage());
239
		break;
240
	case 'n':
241
		justsetname = 1;
242
		break;
243
	case 'x':
244
		setnetmtpt(mntpt, sizeof(mntpt), EARGF(usage()));
245
		setext(ext, sizeof(ext), mntpt);
246
		break;
247
	}ARGEND
248
	USED(argc);
249
	USED(argv);
250
 
251
	rfork(RFREND|RFNOTEG);
252
 
253
	snprint(servefile, sizeof(servefile), "#s/cs%s", ext);
254
	snprint(netndb, sizeof(netndb), "%s/ndb", mntpt);
255
	unmount(servefile, mntpt);
256
	remove(servefile);
257
 
258
	fmtinstall('E', eipfmt);
259
	fmtinstall('I', eipfmt);
260
	fmtinstall('M', eipfmt);
261
	fmtinstall('F', fcallfmt);
262
 
263
	ndbinit();
264
	netinit(0);
265
 
266
	if(!justsetname){
267
		mountinit(servefile, mntpt);
268
		io();
269
	}
270
	exits(0);
271
}
272
 
273
/*
274
 *  if a mount point is specified, set the cs extention to be the mount point
275
 *  with '_'s replacing '/'s
276
 */
277
void
278
setext(char *ext, int n, char *p)
279
{
280
	int i, c;
281
 
282
	n--;
283
	for(i = 0; i < n; i++){
284
		c = p[i];
285
		if(c == 0)
286
			break;
287
		if(c == '/')
288
			c = '_';
289
		ext[i] = c;
290
	}
291
	ext[i] = 0;
292
}
293
 
294
void
295
mountinit(char *service, char *mntpt)
296
{
297
	int f;
298
	int p[2];
299
	char buf[32];
300
 
301
	if(pipe(p) < 0)
302
		error("pipe failed");
303
 
304
	/*
305
	 *  make a /srv/cs
306
	 */
307
	f = create(service, OWRITE|ORCLOSE, 0666);
308
	if(f < 0)
309
		error(service);
310
	snprint(buf, sizeof(buf), "%d", p[1]);
311
	if(write(f, buf, strlen(buf)) != strlen(buf))
312
		error("write /srv/cs");
313
 
314
	switch(rfork(RFFDG|RFPROC|RFNAMEG)){
315
	case 0:
316
		close(p[1]);
317
		procsetname("%s", mntpt);
318
		break;
319
	case -1:
320
		error("fork failed\n");
321
	default:
322
		/*
323
		 *  put ourselves into the file system
324
		 */
325
		close(p[0]);
326
		if(mount(p[1], -1, mntpt, MAFTER, "") < 0)
327
			error("mount failed\n");
328
		_exits(0);
329
	}
330
	mfd[0] = mfd[1] = p[0];
331
}
332
 
333
void
334
ndbinit(void)
335
{
336
	db = ndbopen(dbfile);
337
	if(db == nil)
338
		error("can't open network database");
339
 
340
	netdb = ndbopen(netndb);
341
	if(netdb != nil){
342
		netdb->nohash = 1;
343
		db = ndbcat(netdb, db);
344
	}
345
}
346
 
347
Mfile*
348
newfid(int fid)
349
{
350
	Mlist *f, *ff;
351
	Mfile *mf;
352
 
353
	ff = 0;
354
	for(f = mlist; f; f = f->next)
355
		if(f->mf.busy && f->mf.fid == fid)
356
			return &f->mf;
357
		else if(!ff && !f->mf.busy)
358
			ff = f;
359
	if(ff == 0){
360
		ff = emalloc(sizeof *f);
361
		ff->next = mlist;
362
		mlist = ff;
363
	}
364
	mf = &ff->mf;
365
	memset(mf, 0, sizeof *mf);
366
	mf->fid = fid;
367
	return mf;
368
}
369
 
370
Job*
371
newjob(void)
372
{
373
	Job *job;
374
 
375
	job = mallocz(sizeof(Job), 1);
376
	lock(&joblock);
377
	job->next = joblist;
378
	joblist = job;
379
	job->request.tag = -1;
380
	unlock(&joblock);
381
	return job;
382
}
383
 
384
void
385
freejob(Job *job)
386
{
387
	Job **l;
388
 
389
	lock(&joblock);
390
	for(l = &joblist; *l; l = &(*l)->next){
391
		if((*l) == job){
392
			*l = job->next;
393
			free(job);
394
			break;
395
		}
396
	}
397
	unlock(&joblock);
398
}
399
 
400
void
401
flushjob(int tag)
402
{
403
	Job *job;
404
 
405
	lock(&joblock);
406
	for(job = joblist; job; job = job->next){
407
		if(job->request.tag == tag && job->request.type != Tflush){
408
			job->flushed = 1;
409
			break;
410
		}
411
	}
412
	unlock(&joblock);
413
}
414
 
415
void
416
io(void)
417
{
418
	long n;
419
	Mfile *mf;
420
	int slaveflag;
421
	uchar mdata[IOHDRSZ + Maxfdata];
422
	Job *job;
423
 
424
	/*
425
	 *  if we ask dns to fulfill requests,
426
	 *  a slave process is created to wait for replies.  The
427
	 *  master process returns immediately via a longjmp
428
	 *  through 'masterjmp'.
429
	 *
430
	 *  *isslave is a pointer into the call stack to a variable
431
	 *  that tells whether or not the current process is a slave.
432
	 */
433
	slaveflag = 0;		/* init slave variable */
434
	isslave = &slaveflag;
435
	setjmp(masterjmp);
436
 
437
	for(;;){
438
		n = read9pmsg(mfd[0], mdata, sizeof mdata);
439
		if(n<=0)
440
			error("mount read");
441
		job = newjob();
442
		if(convM2S(mdata, n, &job->request) != n){
443
			syslog(1, logfile, "format error %ux %ux %ux %ux %ux",
444
				mdata[0], mdata[1], mdata[2], mdata[3], mdata[4]);
445
			freejob(job);
446
			continue;
447
		}
448
		lock(&dblock);
449
		mf = newfid(job->request.fid);
450
		if(debug)
451
			syslog(0, logfile, "%F", &job->request);
452
 
453
 
454
		switch(job->request.type){
455
		default:
456
			syslog(1, logfile, "unknown request type %d", job->request.type);
457
			break;
458
		case Tversion:
459
			rversion(job);
460
			break;
461
		case Tauth:
462
			rauth(job);
463
			break;
464
		case Tflush:
465
			rflush(job);
466
			break;
467
		case Tattach:
468
			rattach(job, mf);
469
			break;
470
		case Twalk:
471
			rwalk(job, mf);
472
			break;
473
		case Topen:
474
			ropen(job, mf);
475
			break;
476
		case Tcreate:
477
			rcreate(job, mf);
478
			break;
479
		case Tread:
480
			rread(job, mf);
481
			break;
482
		case Twrite:
483
			rwrite(job, mf);
484
			break;
485
		case Tclunk:
486
			rclunk(job, mf);
487
			break;
488
		case Tremove:
489
			rremove(job, mf);
490
			break;
491
		case Tstat:
492
			rstat(job, mf);
493
			break;
494
		case Twstat:
495
			rwstat(job, mf);
496
			break;
497
		}
498
		unlock(&dblock);
499
 
500
		freejob(job);
501
 
502
		/*
503
		 *  slave processes die after replying
504
		 */
505
		if(*isslave){
506
			if(debug)
507
				syslog(0, logfile, "slave death %d", getpid());
508
			_exits(0);
509
		}
510
	}
511
}
512
 
513
void
514
rversion(Job *job)
515
{
516
	if(job->request.msize > IOHDRSZ + Maxfdata)
517
		job->reply.msize = IOHDRSZ + Maxfdata;
518
	else
519
		job->reply.msize = job->request.msize;
520
	if(strncmp(job->request.version, "9P2000", 6) != 0)
521
		sendmsg(job, "unknown 9P version");
522
	else{
523
		job->reply.version = "9P2000";
524
		sendmsg(job, 0);
525
	}
526
}
527
 
528
void
529
rauth(Job *job)
530
{
531
	sendmsg(job, "cs: authentication not required");
532
}
533
 
534
/*
535
 *  don't flush till all the slaves are done
536
 */
537
void
538
rflush(Job *job)
539
{
540
	flushjob(job->request.oldtag);
541
	sendmsg(job, 0);
542
}
543
 
544
void
545
rattach(Job *job, Mfile *mf)
546
{
547
	if(mf->busy == 0){
548
		mf->busy = 1;
549
		mf->user = estrdup(job->request.uname);
550
	}
551
	mf->qid.vers = vers++;
552
	mf->qid.type = QTDIR;
553
	mf->qid.path = 0LL;
554
	job->reply.qid = mf->qid;
555
	sendmsg(job, 0);
556
}
557
 
558
 
559
char*
560
rwalk(Job *job, Mfile *mf)
561
{
562
	char *err;
563
	char **elems;
564
	int nelems;
565
	int i;
566
	Mfile *nmf;
567
	Qid qid;
568
 
569
	err = 0;
570
	nmf = nil;
571
	elems = job->request.wname;
572
	nelems = job->request.nwname;
573
	job->reply.nwqid = 0;
574
 
575
	if(job->request.newfid != job->request.fid){
576
		/* clone fid */
577
		nmf = newfid(job->request.newfid);
578
		if(nmf->busy){
579
			nmf = nil;
580
			err = "clone to used channel";
581
			goto send;
582
		}
583
		*nmf = *mf;
584
		nmf->user = estrdup(mf->user);
585
		nmf->fid = job->request.newfid;
586
		nmf->qid.vers = vers++;
587
		mf = nmf;
588
	}
589
	/* else nmf will be nil */
590
 
591
	qid = mf->qid;
592
	if(nelems > 0){
593
		/* walk fid */
594
		for(i=0; i<nelems && i<MAXWELEM; i++){
595
			if((qid.type & QTDIR) == 0){
596
				err = "not a directory";
597
				break;
598
			}
599
			if(strcmp(elems[i], "..") == 0 || strcmp(elems[i], ".") == 0){
600
				qid.type = QTDIR;
601
				qid.path = Qdir;
602
    Found:
603
				job->reply.wqid[i] = qid;
604
				job->reply.nwqid++;
605
				continue;
606
			}
607
			if(strcmp(elems[i], "cs") == 0){
608
				qid.type = QTFILE;
609
				qid.path = Qcs;
610
				goto Found;
611
			}
612
			err = "file does not exist";
613
			break;
614
		}
615
	}
616
 
617
    send:
618
	if(nmf != nil && (err!=nil || job->reply.nwqid<nelems)){
619
		cleanmf(nmf);
620
		free(nmf->user);
621
		nmf->user = 0;
622
		nmf->busy = 0;
623
		nmf->fid = 0;
624
	}
625
	if(err == nil)
626
		mf->qid = qid;
627
	sendmsg(job, err);
628
	return err;
629
}
630
 
631
void
632
ropen(Job *job, Mfile *mf)
633
{
634
	int mode;
635
	char *err;
636
 
637
	err = 0;
638
	mode = job->request.mode;
639
	if(mf->qid.type & QTDIR){
640
		if(mode)
641
			err = "permission denied";
642
	}
643
	job->reply.qid = mf->qid;
644
	job->reply.iounit = 0;
645
	sendmsg(job, err);
646
}
647
 
648
void
649
rcreate(Job *job, Mfile *mf)
650
{
651
	USED(mf);
652
	sendmsg(job, "creation permission denied");
653
}
654
 
655
void
656
rread(Job *job, Mfile *mf)
657
{
658
	int i, n, cnt;
659
	long off, toff, clock;
660
	Dir dir;
661
	uchar buf[Maxfdata];
662
	char *err;
663
 
664
	n = 0;
665
	err = 0;
666
	off = job->request.offset;
667
	cnt = job->request.count;
668
	if(mf->qid.type & QTDIR){
669
		clock = time(0);
670
		if(off == 0){
671
			memset(&dir, 0, sizeof dir);
672
			dir.name = "cs";
673
			dir.qid.type = QTFILE;
674
			dir.qid.vers = vers;
675
			dir.qid.path = Qcs;
676
			dir.mode = 0666;
677
			dir.length = 0;
678
			dir.uid = mf->user;
679
			dir.gid = mf->user;
680
			dir.muid = mf->user;
681
			dir.atime = clock;	/* wrong */
682
			dir.mtime = clock;	/* wrong */
683
			n = convD2M(&dir, buf, sizeof buf);
684
		}
685
		job->reply.data = (char*)buf;
686
	} else {
687
		for(;;){
688
			/* look for an answer at the right offset */
689
			toff = 0;
690
			for(i = 0; mf->reply[i] && i < mf->nreply; i++){
691
				n = mf->replylen[i];
692
				if(off < toff + n)
693
					break;
694
				toff += n;
695
			}
696
			if(i < mf->nreply)
697
				break;		/* got something to return */
698
 
699
			/* try looking up more answers */
700
			if(lookup(mf) == 0){
701
				/* no more */
702
				n = 0;
703
				goto send;
704
			}
705
		}
706
 
707
		/* give back a single reply (or part of one) */
708
		job->reply.data = mf->reply[i] + (off - toff);
709
		if(cnt > toff - off + n)
710
			n = toff - off + n;
711
		else
712
			n = cnt;
713
	}
714
send:
715
	job->reply.count = n;
716
	sendmsg(job, err);
717
}
718
void
719
cleanmf(Mfile *mf)
720
{
721
	int i;
722
 
723
	if(mf->net != nil){
724
		free(mf->net);
725
		mf->net = nil;
726
	}
727
	if(mf->host != nil){
728
		free(mf->host);
729
		mf->host = nil;
730
	}
731
	if(mf->serv != nil){
732
		free(mf->serv);
733
		mf->serv = nil;
734
	}
735
	if(mf->rem != nil){
736
		free(mf->rem);
737
		mf->rem = nil;
738
	}
739
	for(i = 0; i < mf->nreply; i++){
740
		free(mf->reply[i]);
741
		mf->reply[i] = nil;
742
		mf->replylen[i] = 0;
743
	}
744
	mf->nreply = 0;
745
	mf->nextnet = netlist;
746
}
747
 
748
void
749
rwrite(Job *job, Mfile *mf)
750
{
751
	int cnt, n;
752
	char *err;
753
	char *field[4];
754
	char curerr[64];
755
 
756
	err = 0;
757
	cnt = job->request.count;
758
	if(mf->qid.type & QTDIR){
759
		err = "can't write directory";
760
		goto send;
761
	}
762
	if(cnt >= Maxrequest){
763
		err = "request too long";
764
		goto send;
765
	}
766
	job->request.data[cnt] = 0;
767
 
768
	/*
769
	 *  toggle debugging
770
	 */
771
	if(strncmp(job->request.data, "debug", 5)==0){
772
		debug ^= 1;
773
		syslog(1, logfile, "debug %d", debug);
774
		goto send;
775
	}
776
 
777
	/*
778
	 *  toggle ipv6 lookups
779
	 */
780
	if(strncmp(job->request.data, "ipv6", 4)==0){
781
		ipv6lookups ^= 1;
782
		syslog(1, logfile, "ipv6lookups %d", ipv6lookups);
783
		goto send;
784
	}
785
 
786
	/*
787
	 *  toggle debugging
788
	 */
789
	if(strncmp(job->request.data, "paranoia", 8)==0){
790
		paranoia ^= 1;
791
		syslog(1, logfile, "paranoia %d", paranoia);
792
		goto send;
793
	}
794
 
795
	/*
796
	 *  add networks to the default list
797
	 */
798
	if(strncmp(job->request.data, "add ", 4)==0){
799
		if(job->request.data[cnt-1] == '\n')
800
			job->request.data[cnt-1] = 0;
801
		netadd(job->request.data+4);
802
		readipinterfaces();
803
		goto send;
804
	}
805
 
806
	/*
807
	 *  refresh all state
808
	 */
809
	if(strncmp(job->request.data, "refresh", 7)==0){
810
		netinit(1);
811
		goto send;
812
	}
813
 
814
	/* start transaction with a clean slate */
815
	cleanmf(mf);
816
 
817
	/*
818
	 *  look for a general query
819
	 */
820
	if(*job->request.data == '!'){
821
		err = genquery(mf, job->request.data+1);
822
		goto send;
823
	}
824
 
825
	if(debug)
826
		syslog(0, logfile, "write %s", job->request.data);
827
	if(paranoia)
828
		syslog(0, paranoiafile, "write %s by %s", job->request.data, mf->user);
829
 
830
	/*
831
	 *  break up name
832
	 */
833
	n = getfields(job->request.data, field, 4, 1, "!");
834
	switch(n){
835
	case 1:
836
		mf->net = strdup("net");
837
		mf->host = strdup(field[0]);
838
		break;
839
	case 4:
840
		mf->rem = strdup(field[3]);
841
		/* fall through */
842
	case 3:
843
		mf->serv = strdup(field[2]);
844
		/* fall through */
845
	case 2:
846
		mf->host = strdup(field[1]);
847
		mf->net = strdup(field[0]);
848
		break;
849
	}
850
 
851
	/*
852
	 *  do the first net worth of lookup
853
	 */
854
	if(lookup(mf) == 0){
855
		rerrstr(curerr, sizeof curerr);
856
		err = curerr;
857
	}
858
send:
859
	job->reply.count = cnt;
860
	sendmsg(job, err);
861
}
862
 
863
void
864
rclunk(Job *job, Mfile *mf)
865
{
866
	cleanmf(mf);
867
	free(mf->user);
868
	mf->user = 0;
869
	mf->busy = 0;
870
	mf->fid = 0;
871
	sendmsg(job, 0);
872
}
873
 
874
void
875
rremove(Job *job, Mfile *mf)
876
{
877
	USED(mf);
878
	sendmsg(job, "remove permission denied");
879
}
880
 
881
void
882
rstat(Job *job, Mfile *mf)
883
{
884
	Dir dir;
885
	uchar buf[IOHDRSZ+Maxfdata];
886
 
887
	memset(&dir, 0, sizeof dir);
888
	if(mf->qid.type & QTDIR){
889
		dir.name = ".";
890
		dir.mode = DMDIR|0555;
891
	} else {
892
		dir.name = "cs";
893
		dir.mode = 0666;
894
	}
895
	dir.qid = mf->qid;
896
	dir.length = 0;
897
	dir.uid = mf->user;
898
	dir.gid = mf->user;
899
	dir.muid = mf->user;
900
	dir.atime = dir.mtime = time(0);
901
	job->reply.nstat = convD2M(&dir, buf, sizeof buf);
902
	job->reply.stat = buf;
903
	sendmsg(job, 0);
904
}
905
 
906
void
907
rwstat(Job *job, Mfile *mf)
908
{
909
	USED(mf);
910
	sendmsg(job, "wstat permission denied");
911
}
912
 
913
void
914
sendmsg(Job *job, char *err)
915
{
916
	int n;
917
	uchar mdata[IOHDRSZ + Maxfdata];
918
	char ename[ERRMAX];
919
 
920
	if(err){
921
		job->reply.type = Rerror;
922
		snprint(ename, sizeof(ename), "cs: %s", err);
923
		job->reply.ename = ename;
924
	}else{
925
		job->reply.type = job->request.type+1;
926
	}
927
	job->reply.tag = job->request.tag;
928
	n = convS2M(&job->reply, mdata, sizeof mdata);
929
	if(n == 0){
930
		syslog(1, logfile, "sendmsg convS2M of %F returns 0", &job->reply);
931
		abort();
932
	}
933
	lock(&joblock);
934
	if(job->flushed == 0)
935
		if(write(mfd[1], mdata, n)!=n)
936
			error("mount write");
937
	unlock(&joblock);
938
	if(debug)
939
		syslog(0, logfile, "%F %d", &job->reply, n);
940
}
941
 
942
void
943
error(char *s)
944
{
945
	syslog(1, "cs", "%s: %r", s);
946
	_exits(0);
947
}
948
 
949
static int
950
isvalidip(uchar *ip)
951
{
952
	return ipcmp(ip, IPnoaddr) != 0 && ipcmp(ip, v4prefix) != 0;
953
}
954
 
955
static uchar loopbacknet[IPaddrlen] = {
956
	0, 0, 0, 0,
957
	0, 0, 0, 0,
958
	0, 0, 0xff, 0xff,
959
	127, 0, 0, 0
960
};
961
static uchar loopbackmask[IPaddrlen] = {
962
	0xff, 0xff, 0xff, 0xff,
963
	0xff, 0xff, 0xff, 0xff,
964
	0xff, 0xff, 0xff, 0xff,
965
	0xff, 0, 0, 0
966
};
967
 
968
void
969
readipinterfaces(void)
970
{
971
	if(myipaddr(ipa, mntpt) != 0)
972
		ipmove(ipa, IPnoaddr);
973
	sprint(ipaddr, "%I", ipa);
974
	if (debug)
975
		syslog(0, "dns", "ipaddr is %s\n", ipaddr);
976
}
977
 
978
/*
979
 *  get the system name
980
 */
981
void
982
ipid(void)
983
{
984
	uchar addr[6];
985
	Ndbtuple *t, *tt;
986
	char *p, *attr;
987
	Ndbs s;
988
	int f;
989
	char buf[Maxpath];
990
 
991
	/* use environment, ether addr, or ipaddr to get system name */
992
	if(mysysname == 0){
993
		/*
994
		 *  environment has priority.
995
		 *
996
		 *  on the sgi power the default system name
997
		 *  is the ip address.  ignore that.
998
		 *
999
		 */
1000
		p = getenv("sysname");
1001
		if(p && *p){
1002
			attr = ipattr(p);
1003
			if(strcmp(attr, "ip") != 0)
1004
				mysysname = strdup(p);
1005
		}
1006
 
1007
		/*
1008
		 *  the /net/ndb contains what the network
1009
		 *  figured out from DHCP.  use that name if
1010
		 *  there is one.
1011
		 */
1012
		if(mysysname == 0 && netdb != nil){
1013
			ndbreopen(netdb);
1014
			for(tt = t = ndbparse(netdb); t != nil; t = t->entry){
1015
				if(strcmp(t->attr, "sys") == 0){
1016
					mysysname = strdup(t->val);
1017
					break;
1018
				}
1019
			}
1020
			ndbfree(tt);
1021
		}
1022
 
1023
		/* next network database, ip address, and ether address to find a name */
1024
		if(mysysname == 0){
1025
			t = nil;
1026
			if(isvalidip(ipa))
1027
				free(ndbgetvalue(db, &s, "ip", ipaddr, "sys", &t));
1028
			if(t == nil){
1029
				for(f = 0; f < 3; f++){
1030
					snprint(buf, sizeof buf, "%s/ether%d", mntpt, f);
1031
					if(myetheraddr(addr, buf) >= 0){
1032
						snprint(eaddr, sizeof(eaddr), "%E", addr);
1033
						free(ndbgetvalue(db, &s, "ether", eaddr, "sys", &t));
1034
						if(t != nil)
1035
							break;
1036
					}
1037
				}
1038
			}
1039
			for(tt = t; tt != nil; tt = tt->entry){
1040
				if(strcmp(tt->attr, "sys") == 0){
1041
					mysysname = strdup(tt->val);
1042
					break;
1043
				}
1044
			}
1045
			ndbfree(t);
1046
		}
1047
 
1048
		/* nothing else worked, use the ip address */
1049
		if(mysysname == 0 && isvalidip(ipa))
1050
			mysysname = strdup(ipaddr);
1051
 
1052
 
1053
		/* set /dev/sysname if we now know it */
1054
		if(mysysname){
1055
			f = open("/dev/sysname", OWRITE);
1056
			if(f >= 0){
1057
				write(f, mysysname, strlen(mysysname));
1058
				close(f);
1059
			}
1060
		}
1061
	}
1062
}
1063
 
1064
/*
1065
 *  Set up a list of default networks by looking for
1066
 *  /net/^*^/clone.
1067
 */
1068
void
1069
netinit(int background)
1070
{
1071
	char clone[Maxpath];
1072
	Network *np;
1073
	static int working;
1074
 
1075
	if(background){
1076
		switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1077
		case 0:
1078
			break;
1079
		default:
1080
			return;
1081
		}
1082
		lock(&netlock);
1083
	}
1084
 
1085
	/* add the mounted networks to the default list */
1086
	for(np = network; np->net; np++){
1087
		if(np->considered)
1088
			continue;
1089
		snprint(clone, sizeof(clone), "%s/%s/clone", mntpt, np->net);
1090
		if(access(clone, AEXIST) < 0)
1091
			continue;
1092
		if(netlist)
1093
			last->next = np;
1094
		else
1095
			netlist = np;
1096
		last = np;
1097
		np->next = 0;
1098
		np->considered = 1;
1099
	}
1100
 
1101
	/* find out what our ip address is */
1102
	readipinterfaces();
1103
 
1104
	/* set the system name if we need to, these days ip is all we have */
1105
	ipid();
1106
 
1107
	if(debug)
1108
		syslog(0, logfile, "mysysname %s eaddr %s ipaddr %s ipa %I\n",
1109
			mysysname?mysysname:"???", eaddr, ipaddr, ipa);
1110
 
1111
	if(background){
1112
		unlock(&netlock);
1113
		_exits(0);
1114
	}
1115
}
1116
 
1117
/*
1118
 *  add networks to the standard list
1119
 */
1120
void
1121
netadd(char *p)
1122
{
1123
	Network *np;
1124
	char *field[12];
1125
	int i, n;
1126
 
1127
	n = getfields(p, field, 12, 1, " ");
1128
	for(i = 0; i < n; i++){
1129
		for(np = network; np->net; np++){
1130
			if(strcmp(field[i], np->net) != 0)
1131
				continue;
1132
			if(np->considered)
1133
				break;
1134
			if(netlist)
1135
				last->next = np;
1136
			else
1137
				netlist = np;
1138
			last = np;
1139
			np->next = 0;
1140
			np->considered = 1;
1141
		}
1142
	}
1143
}
1144
 
1145
int
1146
lookforproto(Ndbtuple *t, char *proto)
1147
{
1148
	for(; t != nil; t = t->entry)
1149
		if(strcmp(t->attr, "proto") == 0 && strcmp(t->val, proto) == 0)
1150
			return 1;
1151
	return 0;
1152
}
1153
 
1154
/*
1155
 *  lookup a request.  the network "net" means we should pick the
1156
 *  best network to get there.
1157
 */
1158
int
1159
lookup(Mfile *mf)
1160
{
1161
	Network *np;
1162
	char *cp;
1163
	Ndbtuple *nt, *t;
1164
	char reply[Maxreply];
1165
	int i, rv;
1166
	int hack;
1167
 
1168
	/* open up the standard db files */
1169
	if(db == 0)
1170
		ndbinit();
1171
	if(db == 0)
1172
		error("can't open mf->network database\n");
1173
 
1174
	rv = 0;
1175
 
1176
	if(mf->net == nil)
1177
		return 0;	/* must have been a genquery */
1178
 
1179
	if(strcmp(mf->net, "net") == 0){
1180
		/*
1181
		 *  go through set of default nets
1182
		 */
1183
		for(np = mf->nextnet; np; np = np->next){
1184
			nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1185
			if(nt == nil)
1186
				continue;
1187
			hack = np->fasttimeouthack && !lookforproto(nt, np->net);
1188
			for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1189
				cp = (*np->trans)(t, np, mf->serv, mf->rem, hack);
1190
				if(cp){
1191
					/* avoid duplicates */
1192
					for(i = 0; i < mf->nreply; i++)
1193
						if(strcmp(mf->reply[i], cp) == 0)
1194
							break;
1195
					if(i == mf->nreply){
1196
						/* save the reply */
1197
						mf->replylen[mf->nreply] = strlen(cp);
1198
						mf->reply[mf->nreply++] = cp;
1199
						rv++;
1200
					}
1201
				}
1202
			}
1203
			ndbfree(nt);
1204
			np = np->next;
1205
			break;
1206
		}
1207
		mf->nextnet = np;
1208
		return rv;
1209
	}
1210
 
1211
	/*
1212
	 *  if not /net, we only get one lookup
1213
	 */
1214
	if(mf->nreply != 0)
1215
		return 0;
1216
	/*
1217
	 *  look for a specific network
1218
	 */
1219
	for(np = netlist; np && np->net != nil; np++){
1220
		if(np->fasttimeouthack)
1221
			continue;
1222
		if(strcmp(np->net, mf->net) == 0)
1223
			break;
1224
	}
1225
 
1226
	if(np && np->net != nil){
1227
		/*
1228
		 *  known network
1229
		 */
1230
		nt = (*np->lookup)(np, mf->host, mf->serv, 1);
1231
		for(t = nt; mf->nreply < Nreply && t; t = t->entry){
1232
			cp = (*np->trans)(t, np, mf->serv, mf->rem, 0);
1233
			if(cp){
1234
				mf->replylen[mf->nreply] = strlen(cp);
1235
				mf->reply[mf->nreply++] = cp;
1236
				rv++;
1237
			}
1238
		}
1239
		ndbfree(nt);
1240
		return rv;
1241
	} else {
1242
		/*
1243
		 *  not a known network, don't translate host or service
1244
		 */
1245
		if(mf->serv)
1246
			snprint(reply, sizeof(reply), "%s/%s/clone %s!%s",
1247
				mntpt, mf->net, mf->host, mf->serv);
1248
		else
1249
			snprint(reply, sizeof(reply), "%s/%s/clone %s",
1250
				mntpt, mf->net, mf->host);
1251
		mf->reply[0] = strdup(reply);
1252
		mf->replylen[0] = strlen(reply);
1253
		mf->nreply = 1;
1254
		return 1;
1255
	}
1256
}
1257
 
1258
/*
1259
 *  translate an ip service name into a port number.  If it's a numeric port
1260
 *  number, look for restricted access.
1261
 *
1262
 *  the service '*' needs no translation.
1263
 */
1264
char*
1265
ipserv(Network *np, char *name, char *buf, int blen)
1266
{
1267
	char *p;
1268
	int alpha = 0;
1269
	int restr = 0;
1270
	char port[10];
1271
	Ndbtuple *t, *nt;
1272
	Ndbs s;
1273
 
1274
	/* '*' means any service */
1275
	if(strcmp(name, "*")==0){
1276
		strcpy(buf, name);
1277
		return buf;
1278
	}
1279
 
1280
	/*  see if it's numeric or symbolic */
1281
	port[0] = 0;
1282
	for(p = name; *p; p++){
1283
		if(isdigit(*p))
1284
			{}
1285
		else if(isalpha(*p) || *p == '-' || *p == '$')
1286
			alpha = 1;
1287
		else
1288
			return 0;
1289
	}
1290
	t = nil;
1291
	p = nil;
1292
	if(alpha){
1293
		p = ndbgetvalue(db, &s, np->net, name, "port", &t);
1294
		if(p == nil)
1295
			return 0;
1296
	} else {
1297
		/* look up only for tcp ports < 1024 to get the restricted
1298
		 * attribute
1299
		 */
1300
		if(atoi(name) < 1024 && strcmp(np->net, "tcp") == 0)
1301
			p = ndbgetvalue(db, &s, "port", name, "port", &t);
1302
		if(p == nil)
1303
			p = strdup(name);
1304
	}
1305
 
1306
	if(t){
1307
		for(nt = t; nt; nt = nt->entry)
1308
			if(strcmp(nt->attr, "restricted") == 0)
1309
				restr = 1;
1310
		ndbfree(t);
1311
	}
1312
	snprint(buf, blen, "%s%s", p, restr ? "!r" : "");
1313
	free(p);
1314
 
1315
	return buf;
1316
}
1317
 
1318
/*
1319
 *  lookup an ip attribute
1320
 */
1321
int
1322
ipattrlookup(Ndb *db, char *ipa, char *attr, char *val, int vlen)
1323
{
1324
 
1325
	Ndbtuple *t, *nt;
1326
	char *alist[2];
1327
 
1328
	alist[0] = attr;
1329
	t = ndbipinfo(db, "ip", ipa, alist, 1);
1330
	if(t == nil)
1331
		return 0;
1332
	for(nt = t; nt != nil; nt = nt->entry){
1333
		if(strcmp(nt->attr, attr) == 0){
1334
			nstrcpy(val, nt->val, vlen);
1335
			ndbfree(t);
1336
			return 1;
1337
		}
1338
	}
1339
 
1340
	/* we shouldn't get here */
1341
	ndbfree(t);
1342
	return 0;
1343
}
1344
 
1345
/*
1346
 *  lookup (and translate) an ip destination
1347
 */
1348
Ndbtuple*
1349
iplookup(Network *np, char *host, char *serv, int nolookup)
1350
{
1351
	char *attr, *dnsname;
1352
	Ndbtuple *t, *nt;
1353
	Ndbs s;
1354
	char ts[Maxservice];
1355
	char dollar[Maxhost];
1356
	uchar ip[IPaddrlen];
1357
	uchar net[IPaddrlen];
1358
	uchar tnet[IPaddrlen];
1359
	Ipifc *ifc;
1360
	Iplifc *lifc;
1361
 
1362
	USED(nolookup);
1363
 
1364
	/*
1365
	 *  start with the service since it's the most likely to fail
1366
	 *  and costs the least
1367
	 */
1368
	werrstr("can't translate address");
1369
	if(serv==0 || ipserv(np, serv, ts, sizeof ts) == 0){
1370
		werrstr("can't translate service");
1371
		return 0;
1372
	}
1373
 
1374
	/* for dial strings with no host */
1375
	if(strcmp(host, "*") == 0)
1376
		return ndbnew("ip", "*");
1377
 
1378
	/*
1379
	 *  hack till we go v6 :: = 0.0.0.0
1380
	 */
1381
	if(strcmp("::", host) == 0)
1382
		return ndbnew("ip", "*");
1383
 
1384
	/*
1385
	 *  '$' means the rest of the name is an attribute that we
1386
	 *  need to search for
1387
	 */
1388
	if(*host == '$'){
1389
		if(ipattrlookup(db, ipaddr, host+1, dollar, sizeof dollar))
1390
			host = dollar;
1391
	}
1392
 
1393
	/*
1394
	 *  turn '[ip address]' into just 'ip address'
1395
	 */
1396
	if(*host == '[' && host[strlen(host)-1] == ']'){
1397
		host++;
1398
		host[strlen(host)-1] = 0;
1399
	}
1400
 
1401
	/*
1402
	 *  just accept addresses
1403
	 */
1404
	attr = ipattr(host);
1405
	if(strcmp(attr, "ip") == 0)
1406
		return ndbnew("ip", host);
1407
 
1408
	/*
1409
	 *  give the domain name server the first opportunity to
1410
	 *  resolve domain names.  if that fails try the database.
1411
	 */
1412
	t = 0;
1413
	werrstr("can't translate address");
1414
	if(strcmp(attr, "dom") == 0)
1415
		t = dnsiplookup(host, &s);
1416
	if(t == 0)
1417
		free(ndbgetvalue(db, &s, attr, host, "ip", &t));
1418
	if(t == 0){
1419
		dnsname = ndbgetvalue(db, &s, attr, host, "dom", nil);
1420
		if(dnsname){
1421
			t = dnsiplookup(dnsname, &s);
1422
			free(dnsname);
1423
		}
1424
	}
1425
	if(t == 0)
1426
		t = dnsiplookup(host, &s);
1427
	if(t == 0)
1428
		return 0;
1429
 
1430
	/*
1431
	 *  reorder the tuple to have the matched line first and
1432
	 *  save that in the request structure.
1433
	 */
1434
	t = reorder(t, s.t);
1435
 
1436
	/*
1437
	 * reorder according to our interfaces
1438
	 */
1439
	lock(&ipifclock);
1440
	for(ifc = ipifcs; ifc != nil; ifc = ifc->next){
1441
		for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next){
1442
			maskip(lifc->ip, lifc->mask, net);
1443
			for(nt = t; nt; nt = nt->entry){
1444
				if(strcmp(nt->attr, "ip") != 0)
1445
					continue;
1446
				parseip(ip, nt->val);
1447
				maskip(ip, lifc->mask, tnet);
1448
				if(memcmp(net, tnet, IPaddrlen) == 0){
1449
					t = reorder(t, nt);
1450
					unlock(&ipifclock);
1451
					return t;
1452
				}
1453
			}
1454
		}
1455
	}
1456
	unlock(&ipifclock);
1457
 
1458
	return t;
1459
}
1460
 
1461
/*
1462
 *  translate an ip address
1463
 */
1464
char*
1465
iptrans(Ndbtuple *t, Network *np, char *serv, char *rem, int hack)
1466
{
1467
	char ts[Maxservice];
1468
	char reply[Maxreply];
1469
	char x[Maxservice];
1470
 
1471
	if(strcmp(t->attr, "ip") != 0)
1472
		return 0;
1473
 
1474
	if(serv == 0 || ipserv(np, serv, ts, sizeof ts) == 0){
1475
		werrstr("can't translate service");
1476
		return 0;
1477
	}
1478
	if(rem != nil)
1479
		snprint(x, sizeof(x), "!%s", rem);
1480
	else
1481
		*x = 0;
1482
 
1483
	if(*t->val == '*')
1484
		snprint(reply, sizeof(reply), "%s/%s/clone %s%s",
1485
			mntpt, np->net, ts, x);
1486
	else
1487
		snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s%s",
1488
			mntpt, np->net, t->val, ts, x, hack? "!fasttimeout": "");
1489
 
1490
	return strdup(reply);
1491
}
1492
 
1493
/*
1494
 *  lookup a telephone number
1495
 */
1496
Ndbtuple*
1497
telcolookup(Network *np, char *host, char *serv, int nolookup)
1498
{
1499
	Ndbtuple *t;
1500
	Ndbs s;
1501
 
1502
	USED(np, nolookup, serv);
1503
 
1504
	werrstr("can't translate address");
1505
	free(ndbgetvalue(db, &s, "sys", host, "telco", &t));
1506
	if(t == 0)
1507
		return ndbnew("telco", host);
1508
 
1509
	return reorder(t, s.t);
1510
}
1511
 
1512
/*
1513
 *  translate a telephone address
1514
 */
1515
char*
1516
telcotrans(Ndbtuple *t, Network *np, char *serv, char *rem, int)
1517
{
1518
	char reply[Maxreply];
1519
	char x[Maxservice];
1520
 
1521
	if(strcmp(t->attr, "telco") != 0)
1522
		return 0;
1523
 
1524
	if(rem != nil)
1525
		snprint(x, sizeof(x), "!%s", rem);
1526
	else
1527
		*x = 0;
1528
	if(serv)
1529
		snprint(reply, sizeof(reply), "%s/%s/clone %s!%s%s", mntpt, np->net,
1530
			t->val, serv, x);
1531
	else
1532
		snprint(reply, sizeof(reply), "%s/%s/clone %s%s", mntpt, np->net,
1533
			t->val, x);
1534
	return strdup(reply);
1535
}
1536
 
1537
/*
1538
 *  reorder the tuple to put x's line first in the entry
1539
 */
1540
Ndbtuple*
1541
reorder(Ndbtuple *t, Ndbtuple *x)
1542
{
1543
	Ndbtuple *nt;
1544
	Ndbtuple *line;
1545
 
1546
	/* find start of this entry's line */
1547
	for(line = x; line->entry == line->line; line = line->line)
1548
		;
1549
	line = line->line;
1550
	if(line == t)
1551
		return t;	/* already the first line */
1552
 
1553
	/* remove this line and everything after it from the entry */
1554
	for(nt = t; nt->entry != line; nt = nt->entry)
1555
		;
1556
	nt->entry = 0;
1557
 
1558
	/* make that the start of the entry */
1559
	for(nt = line; nt->entry; nt = nt->entry)
1560
		;
1561
	nt->entry = t;
1562
	return line;
1563
}
1564
 
1565
/*
1566
 *  create a slave process to handle a request to avoid one request blocking
1567
 *  another.  parent returns to job loop.
1568
 */
1569
void
1570
slave(char *host)
1571
{
1572
	if(*isslave)
1573
		return;		/* we're already a slave process */
1574
 
1575
	switch(rfork(RFPROC|RFNOTEG|RFMEM|RFNOWAIT)){
1576
	case -1:
1577
		break;
1578
	case 0:
1579
		if(debug)
1580
			syslog(0, logfile, "slave %d", getpid());
1581
		procsetname("%s", host);
1582
		*isslave = 1;
1583
		break;
1584
	default:
1585
		longjmp(masterjmp, 1);
1586
	}
1587
 
1588
}
1589
 
1590
static Ndbtuple*
1591
dnsip6lookup(char *mntpt, char *buf, Ndbtuple *t)
1592
{
1593
	Ndbtuple *t6, *tt;
1594
 
1595
	t6 = dnsquery(mntpt, buf, "ipv6");	/* lookup AAAA dns RRs */
1596
	if (t6 == nil)
1597
		return t;
1598
 
1599
	/* convert ipv6 attr to ip */
1600
	for (tt = t6; tt != nil; tt = tt->entry)
1601
		if (strcmp(tt->attr, "ipv6") == 0)
1602
			strncpy(tt->attr, "ip", sizeof tt->attr - 1);
1603
 
1604
	if (t == nil)
1605
		return t6;
1606
 
1607
	/* append t6 list to t list */
1608
	for (tt = t; tt->entry != nil; tt = tt->entry)
1609
		;
1610
	tt->entry = t6;
1611
	return t;
1612
}
1613
 
1614
/*
1615
 *  call the dns process and have it try to translate a name
1616
 */
1617
Ndbtuple*
1618
dnsiplookup(char *host, Ndbs *s)
1619
{
1620
	char buf[Maxreply];
1621
	Ndbtuple *t;
1622
 
1623
	unlock(&dblock);
1624
 
1625
	/* save the name before starting a slave */
1626
	snprint(buf, sizeof(buf), "%s", host);
1627
 
1628
	slave(host);
1629
 
1630
	if(strcmp(ipattr(buf), "ip") == 0)
1631
		t = dnsquery(mntpt, buf, "ptr");
1632
	else {
1633
		t = dnsquery(mntpt, buf, "ip");
1634
		/* special case: query ipv6 (AAAA dns RR) too */
1635
		if (ipv6lookups)
1636
			t = dnsip6lookup(mntpt, buf, t);
1637
	}
1638
	s->t = t;
1639
 
1640
	if(t == nil){
1641
		rerrstr(buf, sizeof buf);
1642
		if(strstr(buf, "exist"))
1643
			werrstr("can't translate address: %s", buf);
1644
		else if(strstr(buf, "dns failure"))
1645
			werrstr("temporary problem: %s", buf);
1646
	}
1647
 
1648
	lock(&dblock);
1649
	return t;
1650
}
1651
 
1652
int
1653
qmatch(Ndbtuple *t, char **attr, char **val, int n)
1654
{
1655
	int i, found;
1656
	Ndbtuple *nt;
1657
 
1658
	for(i = 1; i < n; i++){
1659
		found = 0;
1660
		for(nt = t; nt; nt = nt->entry)
1661
			if(strcmp(attr[i], nt->attr) == 0)
1662
				if(strcmp(val[i], "*") == 0
1663
				|| strcmp(val[i], nt->val) == 0){
1664
					found = 1;
1665
					break;
1666
				}
1667
		if(found == 0)
1668
			break;
1669
	}
1670
	return i == n;
1671
}
1672
 
1673
void
1674
qreply(Mfile *mf, Ndbtuple *t)
1675
{
1676
	Ndbtuple *nt;
1677
	String *s;
1678
 
1679
	s = s_new();
1680
	for(nt = t; mf->nreply < Nreply && nt; nt = nt->entry){
1681
		s_append(s, nt->attr);
1682
		s_append(s, "=");
1683
		s_append(s, nt->val);
1684
 
1685
		if(nt->line != nt->entry){
1686
			mf->replylen[mf->nreply] = s_len(s);
1687
			mf->reply[mf->nreply++] = strdup(s_to_c(s));
1688
			s_restart(s);
1689
		} else
1690
			s_append(s, " ");
1691
	}
1692
	s_free(s);
1693
}
1694
 
1695
enum
1696
{
1697
	Maxattr=	32,
1698
};
1699
 
1700
/*
1701
 *  generic query lookup.  The query is of one of the following
1702
 *  forms:
1703
 *
1704
 *  attr1=val1 attr2=val2 attr3=val3 ...
1705
 *
1706
 *  returns the matching tuple
1707
 *
1708
 *  ipinfo attr=val attr1 attr2 attr3 ...
1709
 *
1710
 *  is like ipinfo and returns the attr{1-n}
1711
 *  associated with the ip address.
1712
 */
1713
char*
1714
genquery(Mfile *mf, char *query)
1715
{
1716
	int i, n;
1717
	char *p;
1718
	char *attr[Maxattr];
1719
	char *val[Maxattr];
1720
	Ndbtuple *t;
1721
	Ndbs s;
1722
 
1723
	n = getfields(query, attr, nelem(attr), 1, " ");
1724
	if(n == 0)
1725
		return "bad query";
1726
 
1727
	if(strcmp(attr[0], "ipinfo") == 0)
1728
		return ipinfoquery(mf, attr, n);
1729
 
1730
	/* parse pairs */
1731
	for(i = 0; i < n; i++){
1732
		p = strchr(attr[i], '=');
1733
		if(p == 0)
1734
			return "bad query";
1735
		*p++ = 0;
1736
		val[i] = p;
1737
	}
1738
 
1739
	/* give dns a chance */
1740
	if((strcmp(attr[0], "dom") == 0 || strcmp(attr[0], "ip") == 0) && val[0]){
1741
		t = dnsiplookup(val[0], &s);
1742
		if(t){
1743
			if(qmatch(t, attr, val, n)){
1744
				qreply(mf, t);
1745
				ndbfree(t);
1746
				return 0;
1747
			}
1748
			ndbfree(t);
1749
		}
1750
	}
1751
 
1752
	/* first pair is always the key.  It can't be a '*' */
1753
	t = ndbsearch(db, &s, attr[0], val[0]);
1754
 
1755
	/* search is the and of all the pairs */
1756
	while(t){
1757
		if(qmatch(t, attr, val, n)){
1758
			qreply(mf, t);
1759
			ndbfree(t);
1760
			return 0;
1761
		}
1762
 
1763
		ndbfree(t);
1764
		t = ndbsnext(&s, attr[0], val[0]);
1765
	}
1766
 
1767
	return "no match";
1768
}
1769
 
1770
/*
1771
 *  resolve an ip address
1772
 */
1773
static Ndbtuple*
1774
ipresolve(char *attr, char *host)
1775
{
1776
	Ndbtuple *t, *nt, **l;
1777
 
1778
	t = iplookup(&network[Ntcp], host, "*", 0);
1779
	for(l = &t; *l != nil; ){
1780
		nt = *l;
1781
		if(strcmp(nt->attr, "ip") != 0){
1782
			*l = nt->entry;
1783
			nt->entry = nil;
1784
			ndbfree(nt);
1785
			continue;
1786
		}
1787
		strcpy(nt->attr, attr);
1788
		l = &nt->entry;
1789
	}
1790
	return t;
1791
}
1792
 
1793
char*
1794
ipinfoquery(Mfile *mf, char **list, int n)
1795
{
1796
	int i, nresolve;
1797
	int resolve[Maxattr];
1798
	Ndbtuple *t, *nt, **l;
1799
	char *attr, *val;
1800
 
1801
	/* skip 'ipinfo' */
1802
	list++; n--;
1803
 
1804
	if(n < 1)
1805
		return "bad query";
1806
 
1807
	/* get search attribute=value, or assume ip=myipaddr */
1808
	attr = *list;
1809
	if((val = strchr(attr, '=')) != nil){
1810
		*val++ = 0;
1811
		list++;
1812
		n--;
1813
	}else{
1814
		attr = "ip";
1815
		val = ipaddr;
1816
	}
1817
 
1818
	if(n < 1)
1819
		return "bad query";
1820
 
1821
	/*
1822
	 *  don't let ndbipinfo resolve the addresses, we're
1823
	 *  better at it.
1824
	 */
1825
	nresolve = 0;
1826
	for(i = 0; i < n; i++)
1827
		if(*list[i] == '@'){		/* @attr=val ? */
1828
			list[i]++;
1829
			resolve[i] = 1;		/* we'll resolve it */
1830
			nresolve++;
1831
		} else
1832
			resolve[i] = 0;
1833
 
1834
	t = ndbipinfo(db, attr, val, list, n);
1835
	if(t == nil)
1836
		return "no match";
1837
 
1838
	if(nresolve != 0){
1839
		for(l = &t; *l != nil;){
1840
			nt = *l;
1841
 
1842
			/* already an address? */
1843
			if(strcmp(ipattr(nt->val), "ip") == 0){
1844
				l = &(*l)->entry;
1845
				continue;
1846
			}
1847
 
1848
			/* user wants it resolved? */
1849
			for(i = 0; i < n; i++)
1850
				if(strcmp(list[i], nt->attr) == 0)
1851
					break;
1852
			if(i >= n || resolve[i] == 0){
1853
				l = &(*l)->entry;
1854
				continue;
1855
			}
1856
 
1857
			/* resolve address and replace entry */
1858
			*l = ipresolve(nt->attr, nt->val);
1859
			while(*l != nil)
1860
				l = &(*l)->entry;
1861
			*l = nt->entry;
1862
 
1863
			nt->entry = nil;
1864
			ndbfree(nt);
1865
		}
1866
	}
1867
 
1868
	/* make it all one line */
1869
	for(nt = t; nt != nil; nt = nt->entry){
1870
		if(nt->entry == nil)
1871
			nt->line = t;
1872
		else
1873
			nt->line = nt->entry;
1874
	}
1875
 
1876
	qreply(mf, t);
1877
 
1878
	return nil;
1879
}
1880
 
1881
void*
1882
emalloc(int size)
1883
{
1884
	void *x;
1885
 
1886
	x = malloc(size);
1887
	if(x == nil)
1888
		abort();
1889
	memset(x, 0, size);
1890
	return x;
1891
}
1892
 
1893
char*
1894
estrdup(char *s)
1895
{
1896
	int size;
1897
	char *p;
1898
 
1899
	size = strlen(s)+1;
1900
	p = malloc(size);
1901
	if(p == nil)
1902
		abort();
1903
	memmove(p, s, size);
1904
	return p;
1905
}