GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/ypbind/ypbind.c Lines: 0 536 0.0 %
Date: 2017-11-07 Branches: 0 281 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: ypbind.c,v 1.70 2017/06/12 18:56:13 millert Exp $ */
2
3
/*
4
 * Copyright (c) 1992, 1993, 1996, 1997, 1998 Theo de Raadt <deraadt@openbsd.org>
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 */
28
29
#include <sys/types.h>
30
#include <sys/socket.h>
31
#include <sys/stat.h>
32
#include <sys/file.h>
33
#include <sys/fcntl.h>
34
#include <sys/uio.h>
35
#include <sys/syslog.h>
36
#include <net/if.h>
37
#include <stdio.h>
38
#include <stdlib.h>
39
#include <limits.h>
40
#include <errno.h>
41
#include <ctype.h>
42
#include <netdb.h>
43
#include <string.h>
44
#include <dirent.h>
45
#include <rpc/rpc.h>
46
#include <rpc/xdr.h>
47
#include <arpa/inet.h>
48
#include <rpc/pmap_clnt.h>
49
#include <rpc/pmap_prot.h>
50
#include <rpc/pmap_rmt.h>
51
#include <unistd.h>
52
#include <err.h>
53
#include <rpcsvc/yp.h>
54
#include <rpcsvc/ypclnt.h>
55
#include <ifaddrs.h>
56
#include <poll.h>
57
58
#define SERVERSDIR	"/etc/yp"
59
#define BINDINGDIR	"/var/yp/binding"
60
#define YPBINDLOCK	"/var/run/ypbind.lock"
61
62
struct _dom_binding {
63
	struct _dom_binding *dom_pnext;
64
	char dom_domain[YPMAXDOMAIN + 1];
65
	struct sockaddr_in dom_server_addr;
66
	unsigned short int dom_server_port;
67
	int dom_socket;
68
	CLIENT *dom_client;
69
	long dom_vers;
70
	time_t dom_check_t;
71
	time_t dom_ask_t;
72
	int dom_lockfd;
73
	int dom_alive;
74
	u_int32_t dom_xid;
75
	char dom_servlist[PATH_MAX];
76
	FILE *dom_servlistfp;
77
};
78
79
void rpc_received(char *dom, struct sockaddr_in *raddrp, int force);
80
void checkwork(void);
81
enum clnt_stat handle_replies(void);
82
enum clnt_stat handle_ping(void);
83
int broadcast(struct _dom_binding *ypdb, char *, int);
84
int direct(struct _dom_binding *ypdb, char *, int);
85
int ping(struct _dom_binding *ypdb);
86
int pings(struct _dom_binding *ypdb);
87
88
char *domain;
89
90
struct _dom_binding *ypbindlist;
91
int check;
92
93
#define YPSET_NO	0
94
#define YPSET_LOCAL	1
95
#define YPSET_ALL	2
96
int ypsetmode = YPSET_NO;
97
int insecure = 0;
98
99
int rpcsock, pingsock;
100
struct rmtcallargs rmtca;
101
struct rmtcallres rmtcr;
102
bool_t rmtcr_outval;
103
u_long rmtcr_port;
104
SVCXPRT *udptransp, *tcptransp;
105
SVCXPRT *ludptransp, *ltcptransp;
106
107
struct _dom_binding *xid2ypdb(u_int32_t xid);
108
u_int32_t unique_xid(struct _dom_binding *ypdb);
109
110
/*
111
 * We name the local RPC functions ypbindproc_XXX_2x() instead
112
 * of ypbindproc_XXX_2() because we need to pass an additional
113
 * parameter. ypbindproc_setdom_2x() does a security check, and
114
 * hence needs the CLIENT *
115
 *
116
 * We are faced with either making ypbindprog_2() do the security
117
 * check before calling ypbindproc_setdom_2().. or we can simply
118
 * declare sun's interface insufficient and roll our own.
119
 */
120
121
/*ARGSUSED*/
122
static void *
123
ypbindproc_null_2x(SVCXPRT *transp, void *argp, CLIENT *clnt)
124
{
125
	static char res;
126
127
	memset(&res, 0, sizeof(res));
128
	return (void *)&res;
129
}
130
131
/*ARGSUSED*/
132
static struct ypbind_resp *
133
ypbindproc_domain_2x(SVCXPRT *transp, domainname *argp, CLIENT *clnt)
134
{
135
	static struct ypbind_resp res;
136
	struct _dom_binding *ypdb;
137
	char path[PATH_MAX];
138
	time_t now;
139
	int count = 0;
140
141
	if (strchr((char *)argp, '/'))
142
		return NULL;
143
144
	memset(&res, 0, sizeof(res));
145
	res.ypbind_status = YPBIND_FAIL_VAL;
146
147
	for (ypdb = ypbindlist; ypdb && count < 100; ypdb = ypdb->dom_pnext)
148
		count++;
149
	if (count >= 100)
150
		return NULL;	/* prevent DOS: sorry, you lose */
151
152
	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
153
		if (!strcmp(ypdb->dom_domain, *argp))
154
			break;
155
156
	if (ypdb == NULL) {
157
		ypdb = malloc(sizeof *ypdb);
158
		if (ypdb == NULL)
159
			return NULL;
160
		memset(ypdb, 0, sizeof *ypdb);
161
		strncpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain-1);
162
		ypdb->dom_domain[sizeof ypdb->dom_domain-1] = '\0';
163
		ypdb->dom_vers = YPVERS;
164
		ypdb->dom_alive = 0;
165
		ypdb->dom_lockfd = -1;
166
		snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
167
		    ypdb->dom_domain, (int)ypdb->dom_vers);
168
		unlink(path);
169
		snprintf(ypdb->dom_servlist, sizeof ypdb->dom_servlist,
170
		    "%s/%s", SERVERSDIR, ypdb->dom_domain);
171
		ypdb->dom_servlistfp = fopen(ypdb->dom_servlist, "r");
172
		ypdb->dom_xid = unique_xid(ypdb);
173
		ypdb->dom_pnext = ypbindlist;
174
		ypbindlist = ypdb;
175
		check++;
176
		return NULL;
177
	}
178
179
	if (ypdb->dom_alive == 0)
180
		return NULL;
181
182
#ifdef HEURISTIC
183
	time(&now);
184
	if (now < ypdb->dom_ask_t + 5) {
185
		/*
186
		 * Hmm. More than 2 requests in 5 seconds have indicated
187
		 * that my binding is possibly incorrect.
188
		 * Ok, do an immediate poll of the server.
189
		 */
190
		if (ypdb->dom_check_t >= now) {
191
			/* don't flood it */
192
			ypdb->dom_check_t = 0;
193
			check++;
194
		}
195
	}
196
	ypdb->dom_ask_t = now;
197
#endif
198
199
	res.ypbind_status = YPBIND_SUCC_VAL;
200
	memmove(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
201
	    &ypdb->dom_server_addr.sin_addr,
202
	    sizeof(res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr));
203
	memmove(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
204
	    &ypdb->dom_server_port,
205
	    sizeof(res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port));
206
#ifdef DEBUG
207
	printf("domain %s at %s/%d\n", ypdb->dom_domain,
208
	    inet_ntoa(ypdb->dom_server_addr.sin_addr),
209
	    ntohs(ypdb->dom_server_addr.sin_port));
210
#endif
211
	return &res;
212
}
213
214
/*ARGSUSED*/
215
static bool_t *
216
ypbindproc_setdom_2x(SVCXPRT *transp, struct ypbind_setdom *argp, CLIENT *clnt)
217
{
218
	struct sockaddr_in *fromsin, bindsin;
219
	static bool_t res = 1;
220
221
	fromsin = svc_getcaller(transp);
222
223
	switch (ypsetmode) {
224
	case YPSET_LOCAL:
225
		if (transp != ludptransp && transp != ltcptransp) {
226
			syslog(LOG_WARNING, "attempted spoof of ypsetme");
227
			svcerr_weakauth(transp);
228
			return NULL;
229
		}
230
		if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
231
			svcerr_weakauth(transp);
232
			return NULL;
233
		}
234
		break;
235
	case YPSET_ALL:
236
		break;
237
	case YPSET_NO:
238
	default:
239
		svcerr_weakauth(transp);
240
		return NULL;
241
	}
242
243
	if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
244
		svcerr_weakauth(transp);
245
		return NULL;
246
	}
247
248
	if (argp->ypsetdom_vers != YPVERS) {
249
		svcerr_noprog(transp);
250
		return NULL;
251
	}
252
253
	memset(&bindsin, 0, sizeof bindsin);
254
	bindsin.sin_family = AF_INET;
255
	bindsin.sin_len = sizeof(bindsin);
256
	memcpy(&bindsin.sin_addr, &argp->ypsetdom_binding.ypbind_binding_addr,
257
	    sizeof(argp->ypsetdom_binding.ypbind_binding_addr));
258
	memcpy(&bindsin.sin_port, &argp->ypsetdom_binding.ypbind_binding_port,
259
	    sizeof(argp->ypsetdom_binding.ypbind_binding_port));
260
	rpc_received(argp->ypsetdom_domain, &bindsin, 1);
261
262
	return &res;
263
}
264
265
static void
266
ypbindprog_2(struct svc_req *rqstp, SVCXPRT *transp)
267
{
268
	union argument {
269
		domainname ypbindproc_domain_2_arg;
270
		struct ypbind_setdom ypbindproc_setdom_2_arg;
271
	} argument;
272
	struct authunix_parms *creds;
273
	char *result;
274
	xdrproc_t xdr_argument, xdr_result;
275
	char *(*local)(SVCXPRT *, union argument *, struct svc_req *);
276
277
	switch (rqstp->rq_proc) {
278
	case YPBINDPROC_NULL:
279
		xdr_argument = xdr_void;
280
		xdr_result = xdr_void;
281
		local = (char *(*)(SVCXPRT *, union argument *, struct svc_req *))
282
		    ypbindproc_null_2x;
283
		break;
284
285
	case YPBINDPROC_DOMAIN:
286
		xdr_argument = xdr_domainname;
287
		xdr_result = xdr_ypbind_resp;
288
		local = (char *(*)(SVCXPRT *, union argument *, struct svc_req *))
289
		    ypbindproc_domain_2x;
290
		break;
291
292
	case YPBINDPROC_SETDOM:
293
		switch (rqstp->rq_cred.oa_flavor) {
294
		case AUTH_UNIX:
295
			creds = (struct authunix_parms *)rqstp->rq_clntcred;
296
			if (creds->aup_uid != 0) {
297
				svcerr_auth(transp, AUTH_BADCRED);
298
				return;
299
			}
300
			break;
301
		default:
302
			svcerr_auth(transp, AUTH_TOOWEAK);
303
			return;
304
		}
305
306
		xdr_argument = xdr_ypbind_setdom;
307
		xdr_result = xdr_void;
308
		local = (char *(*)(SVCXPRT *, union argument *, struct svc_req *))
309
		    ypbindproc_setdom_2x;
310
		break;
311
312
	default:
313
		svcerr_noproc(transp);
314
		return;
315
	}
316
	memset(&argument, 0, sizeof(argument));
317
	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
318
		svcerr_decode(transp);
319
		return;
320
	}
321
	result = (*local)(transp, &argument, rqstp);
322
	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
323
		svcerr_systemerr(transp);
324
	}
325
	return;
326
}
327
328
static void
329
usage(void)
330
{
331
	fprintf(stderr, "usage: ypbind [-insecure] [-ypset] [-ypsetme]\n");
332
	exit(1);
333
}
334
335
int
336
main(int argc, char *argv[])
337
{
338
	char path[PATH_MAX];
339
	struct sockaddr_in sin;
340
	struct pollfd *pfd = NULL;
341
	int width = 0, nready, lockfd, lsock;
342
	socklen_t len;
343
	int evil = 0, one = 1;
344
	DIR *dirp;
345
	struct dirent *dent;
346
347
	yp_get_default_domain(&domain);
348
	if (domain[0] == '\0') {
349
		fprintf(stderr, "domainname not set. Aborting.\n");
350
		exit(1);
351
	}
352
353
	while (--argc) {
354
		++argv;
355
		if (!strcmp("-insecure", *argv))
356
			insecure = 1;
357
		else if (!strcmp("-ypset", *argv))
358
			ypsetmode = YPSET_ALL;
359
		else if (!strcmp("-ypsetme", *argv))
360
			ypsetmode = YPSET_LOCAL;
361
		else
362
			usage();
363
	}
364
365
	/* blow away everything in BINDINGDIR */
366
	dirp = opendir(BINDINGDIR);
367
	if (dirp) {
368
		while ((dent = readdir(dirp))) {
369
			if (!strcmp(dent->d_name, ".") ||
370
			    !strcmp(dent->d_name, ".."))
371
				continue;
372
			(void)unlinkat(dirfd(dirp), dent->d_name, 0);
373
		}
374
		closedir(dirp);
375
	} else {
376
		(void)mkdir(BINDINGDIR, 0755);
377
	}
378
379
#ifdef O_SHLOCK
380
	if ((lockfd = open(YPBINDLOCK, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC,
381
	    0644)) == -1) {
382
		fprintf(stderr, "ypbind: cannot create %s\n", YPBINDLOCK);
383
		exit(1);
384
	}
385
#else
386
	if ((lockfd = open(YPBINDLOCK, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
387
		fprintf(stderr, "ypbind: cannot create %s.\n", YPBINDLOCK);
388
		exit(1);
389
	}
390
	flock(lockfd, LOCK_SH);
391
#endif
392
393
	if (fchmod(lockfd, 0644) == -1)
394
		err(1, "fchmod");
395
396
	(void)pmap_unset(YPBINDPROG, YPBINDVERS);
397
398
	udptransp = svcudp_create(RPC_ANYSOCK);
399
	if (udptransp == NULL) {
400
		fprintf(stderr, "cannot create udp service.\n");
401
		exit(1);
402
	}
403
	if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
404
	    IPPROTO_UDP)) {
405
		fprintf(stderr,
406
		    "unable to register (YPBINDPROG, YPBINDVERS, udp).\n");
407
		exit(1);
408
	}
409
410
	tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
411
	if (tcptransp == NULL) {
412
		fprintf(stderr, "cannot create tcp service.\n");
413
		exit(1);
414
	}
415
	if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
416
	    IPPROTO_TCP)) {
417
		fprintf(stderr,
418
		    "unable to register (YPBINDPROG, YPBINDVERS, tcp).\n");
419
		exit(1);
420
	}
421
422
	if (ypsetmode == YPSET_LOCAL) {
423
		/* build UDP local port */
424
		if ((lsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
425
			syslog(LOG_ERR, "cannot create local udp socket: %m");
426
			exit(1);
427
		}
428
		(void)setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one,
429
		    (socklen_t)sizeof one);
430
		len = sizeof(sin);
431
		if (getsockname(udptransp->xp_sock, (struct sockaddr *)&sin,
432
		    &len) == -1) {
433
			syslog(LOG_ERR, "cannot getsockname local udp: %m");
434
			exit(1);
435
		}
436
		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
437
		sin.sin_port = htons(udptransp->xp_port);
438
		if (bind(lsock, (struct sockaddr *)&sin, len) != 0) {
439
			syslog(LOG_ERR, "cannot bind local udp: %m");
440
			exit(1);
441
		}
442
		if ((ludptransp = svcudp_create(lsock)) == NULL) {
443
			fprintf(stderr, "cannot create udp service.\n");
444
			exit(1);
445
		}
446
447
		/* build TCP local port */
448
		if ((lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
449
			syslog(LOG_ERR, "cannot create udp socket: %m");
450
			exit(1);
451
		}
452
		(void)setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one,
453
		    (socklen_t)sizeof one);
454
		len = sizeof(sin);
455
		if (getsockname(tcptransp->xp_sock, (struct sockaddr *)&sin,
456
		    &len) == -1) {
457
			syslog(LOG_ERR, "cannot getsockname udp: %m");
458
			exit(1);
459
		}
460
		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
461
		sin.sin_port = htons(tcptransp->xp_port);
462
		if (bind(lsock, (struct sockaddr *)&sin, len) == -1) {
463
			syslog(LOG_ERR, "cannot bind local tcp: %m");
464
			exit(1);
465
		}
466
		if ((ltcptransp = svctcp_create(lsock, 0, 0)) == NULL) {
467
			fprintf(stderr, "cannot create tcp service.\n");
468
			exit(1);
469
		}
470
	}
471
472
	if ((rpcsock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) < 0) {
473
		perror("socket");
474
		return -1;
475
	}
476
	memset(&sin, 0, sizeof sin);
477
	sin.sin_family = AF_INET;
478
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
479
	sin.sin_port = 0;
480
	bindresvport(rpcsock, &sin);
481
482
	if ((pingsock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) < 0) {
483
		perror("socket");
484
		return -1;
485
	}
486
	memset(&sin, 0, sizeof sin);
487
	sin.sin_family = AF_INET;
488
	sin.sin_addr.s_addr = htonl(INADDR_ANY);
489
	sin.sin_port = 0;
490
	bindresvport(pingsock, &sin);
491
492
	setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one,
493
	    (socklen_t)sizeof(one));
494
	rmtca.prog = YPPROG;
495
	rmtca.vers = YPVERS;
496
	rmtca.proc = YPPROC_DOMAIN_NONACK;
497
	rmtca.xdr_args = NULL;		/* set at call time */
498
	rmtca.args_ptr = NULL;		/* set at call time */
499
	rmtcr.port_ptr = &rmtcr_port;
500
	rmtcr.xdr_results = xdr_bool;
501
	rmtcr.results_ptr = (caddr_t)&rmtcr_outval;
502
503
	if (strchr(domain, '/'))
504
		errx(1, "bad domainname %s", domain);
505
506
	/* build initial domain binding, make it "unsuccessful" */
507
	ypbindlist = malloc(sizeof *ypbindlist);
508
	if (ypbindlist == NULL)
509
		errx(1, "no memory");
510
	memset(ypbindlist, 0, sizeof *ypbindlist);
511
	strncpy(ypbindlist->dom_domain, domain, sizeof ypbindlist->dom_domain-1);
512
	ypbindlist->dom_domain[sizeof (ypbindlist->dom_domain)-1] = '\0';
513
	ypbindlist->dom_vers = YPVERS;
514
	snprintf(ypbindlist->dom_servlist, sizeof ypbindlist->dom_servlist,
515
	    "%s/%s", SERVERSDIR, ypbindlist->dom_domain);
516
	ypbindlist->dom_servlistfp = fopen(ypbindlist->dom_servlist, "r");
517
	ypbindlist->dom_alive = 0;
518
	ypbindlist->dom_lockfd = -1;
519
	ypbindlist->dom_xid = unique_xid(ypbindlist);
520
	snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
521
	    ypbindlist->dom_domain, (int)ypbindlist->dom_vers);
522
	(void)unlink(path);
523
524
	checkwork();
525
526
	while (1) {
527
		if (pfd == NULL || width != svc_max_pollfd + 2) {
528
			width = svc_max_pollfd + 2;
529
			pfd = reallocarray(pfd, width, sizeof *pfd);
530
			if (pfd == NULL)
531
				err(1, NULL);
532
		}
533
534
		pfd[0].fd = rpcsock;
535
		pfd[0].events = POLLIN;
536
		pfd[1].fd = pingsock;
537
		pfd[1].events = POLLIN;
538
		memcpy(pfd + 2, svc_pollfd, sizeof(*pfd) * svc_max_pollfd);
539
540
		nready = poll(pfd, width, 1000);
541
		switch (nready) {
542
		case 0:
543
			checkwork();
544
			break;
545
		case -1:
546
			if (errno != EINTR)
547
				perror("poll");
548
			break;
549
		default:
550
			/* No need to check for POLLHUP on UDP sockets. */
551
			if (pfd[0].revents & POLLIN) {
552
				handle_replies();
553
				nready--;
554
			}
555
			if (pfd[1].revents & POLLIN) {
556
				handle_ping();
557
				nready--;
558
			}
559
			svc_getreq_poll(pfd + 2, nready);
560
			if (check)
561
				checkwork();
562
			break;
563
		}
564
565
#ifdef DAEMON
566
		if (!evil && ypbindlist->dom_alive) {
567
			evil = 1;
568
			daemon(0, 0);
569
		}
570
#endif
571
	}
572
}
573
574
/*
575
 * State transition is done like this:
576
 *
577
 * STATE	EVENT		ACTION			NEWSTATE	TIMEOUT
578
 * no binding	timeout		broadcast		no binding	5 sec
579
 * no binding	answer		--			binding		60 sec
580
 * binding	timeout		ping server		checking	5 sec
581
 * checking	timeout		ping server + broadcast	checking	5 sec
582
 * checking	answer		--			binding		60 sec
583
 */
584
void
585
checkwork(void)
586
{
587
	struct _dom_binding *ypdb;
588
	time_t t;
589
590
	time(&t);
591
	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
592
		if (ypdb->dom_check_t < t) {
593
			if (ypdb->dom_alive == 1)
594
				ping(ypdb);
595
			else
596
				pings(ypdb);
597
			time(&t);
598
			ypdb->dom_check_t = t + 5;
599
		}
600
	}
601
	check = 0;
602
}
603
604
int
605
ping(struct _dom_binding *ypdb)
606
{
607
	domainname dom = ypdb->dom_domain;
608
	struct rpc_msg msg;
609
	char buf[1400];
610
	enum clnt_stat st;
611
	int outlen;
612
	AUTH *rpcua;
613
	XDR xdr;
614
615
	memset(&xdr, 0, sizeof xdr);
616
	memset(&msg, 0, sizeof msg);
617
618
	rpcua = authunix_create_default();
619
	if (rpcua == (AUTH *)NULL) {
620
		/*printf("cannot get unix auth\n");*/
621
		return RPC_SYSTEMERROR;
622
	}
623
	msg.rm_direction = CALL;
624
	msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
625
	msg.rm_call.cb_prog = YPPROG;
626
	msg.rm_call.cb_vers = YPVERS;
627
	msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK;
628
	msg.rm_call.cb_cred = rpcua->ah_cred;
629
	msg.rm_call.cb_verf = rpcua->ah_verf;
630
631
	msg.rm_xid = ypdb->dom_xid;
632
	xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
633
	if (!xdr_callmsg(&xdr, &msg)) {
634
		st = RPC_CANTENCODEARGS;
635
		AUTH_DESTROY(rpcua);
636
		return st;
637
	}
638
	if (!xdr_domainname(&xdr, &dom)) {
639
		st = RPC_CANTENCODEARGS;
640
		AUTH_DESTROY(rpcua);
641
		return st;
642
	}
643
	outlen = (int)xdr_getpos(&xdr);
644
	xdr_destroy(&xdr);
645
	if (outlen < 1) {
646
		st = RPC_CANTENCODEARGS;
647
		AUTH_DESTROY(rpcua);
648
		return st;
649
	}
650
	AUTH_DESTROY(rpcua);
651
652
	ypdb->dom_alive = 2;
653
	if (sendto(pingsock, buf, outlen, 0,
654
	    (struct sockaddr *)&ypdb->dom_server_addr,
655
	    (socklen_t)sizeof ypdb->dom_server_addr) < 0)
656
		perror("sendto");
657
	return 0;
658
659
}
660
661
int
662
pings(struct _dom_binding *ypdb)
663
{
664
	domainname dom = ypdb->dom_domain;
665
	struct rpc_msg msg;
666
	struct sockaddr_in bindsin;
667
	char buf[1400];
668
	char path[PATH_MAX];
669
	enum clnt_stat st;
670
	int outlen;
671
	AUTH *rpcua;
672
	XDR xdr;
673
674
	rmtca.xdr_args = xdr_domainname;
675
	rmtca.args_ptr = (char *)&dom;
676
677
	memset(&xdr, 0, sizeof xdr);
678
	memset(&msg, 0, sizeof msg);
679
680
	rpcua = authunix_create_default();
681
	if (rpcua == (AUTH *)NULL) {
682
		/*printf("cannot get unix auth\n");*/
683
		return RPC_SYSTEMERROR;
684
	}
685
	msg.rm_direction = CALL;
686
	msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
687
	msg.rm_call.cb_prog = PMAPPROG;
688
	msg.rm_call.cb_vers = PMAPVERS;
689
	msg.rm_call.cb_proc = PMAPPROC_CALLIT;
690
	msg.rm_call.cb_cred = rpcua->ah_cred;
691
	msg.rm_call.cb_verf = rpcua->ah_verf;
692
693
	msg.rm_xid = ypdb->dom_xid;
694
	xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
695
	if (!xdr_callmsg(&xdr, &msg)) {
696
		st = RPC_CANTENCODEARGS;
697
		AUTH_DESTROY(rpcua);
698
		return st;
699
	}
700
	if (!xdr_rmtcall_args(&xdr, &rmtca)) {
701
		st = RPC_CANTENCODEARGS;
702
		AUTH_DESTROY(rpcua);
703
		return st;
704
	}
705
	outlen = (int)xdr_getpos(&xdr);
706
	xdr_destroy(&xdr);
707
	if (outlen < 1) {
708
		st = RPC_CANTENCODEARGS;
709
		AUTH_DESTROY(rpcua);
710
		return st;
711
	}
712
	AUTH_DESTROY(rpcua);
713
714
	if (ypdb->dom_lockfd != -1) {
715
		close(ypdb->dom_lockfd);
716
		ypdb->dom_lockfd = -1;
717
		snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
718
		    ypdb->dom_domain, (int)ypdb->dom_vers);
719
		unlink(path);
720
	}
721
722
	if (ypdb->dom_alive == 2) {
723
		/*
724
		 * This resolves the following situation:
725
		 * ypserver on other subnet was once bound,
726
		 * but rebooted and is now using a different port
727
		 */
728
		memset(&bindsin, 0, sizeof bindsin);
729
		bindsin.sin_family = AF_INET;
730
		bindsin.sin_len = sizeof(bindsin);
731
		bindsin.sin_port = htons(PMAPPORT);
732
		bindsin.sin_addr = ypdb->dom_server_addr.sin_addr;
733
		if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
734
		    (socklen_t)sizeof bindsin) < 0)
735
			perror("sendto");
736
	}
737
	if (ypdb->dom_servlistfp)
738
		return direct(ypdb, buf, outlen);
739
	return broadcast(ypdb, buf, outlen);
740
}
741
742
int
743
broadcast(struct _dom_binding *ypdb, char *buf, int outlen)
744
{
745
	struct ifaddrs *ifap, *ifa;
746
	struct sockaddr_in bindsin;
747
	struct in_addr in;
748
749
	memset(&bindsin, 0, sizeof bindsin);
750
	bindsin.sin_family = AF_INET;
751
	bindsin.sin_len = sizeof(bindsin);
752
	bindsin.sin_port = htons(PMAPPORT);
753
754
	if (getifaddrs(&ifap) != 0) {
755
		perror("getifaddrs");
756
		return -1;
757
	}
758
	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
759
		if (ifa->ifa_addr->sa_family != AF_INET)
760
			continue;
761
		if ((ifa->ifa_flags & IFF_UP) == 0)
762
			continue;
763
764
		switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) {
765
		case IFF_BROADCAST:
766
			if (!ifa->ifa_broadaddr)
767
				continue;
768
			if (ifa->ifa_broadaddr->sa_family != AF_INET)
769
				continue;
770
			in = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr;
771
			break;
772
		case IFF_LOOPBACK:
773
			in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
774
			break;
775
		default:
776
			continue;
777
		}
778
779
		bindsin.sin_addr = in;
780
		if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
781
		    (socklen_t)bindsin.sin_len) < 0)
782
			perror("sendto");
783
	}
784
	freeifaddrs(ifap);
785
	return 0;
786
}
787
788
int
789
direct(struct _dom_binding *ypdb, char *buf, int outlen)
790
{
791
	char line[1024], *p;
792
	struct hostent *hp;
793
	struct sockaddr_in bindsin;
794
	int i, c;
795
	struct stat fst, st;
796
797
	if (fstat(fileno(ypdb->dom_servlistfp), &fst) != -1 &&
798
	    stat(ypdb->dom_servlist, &st) != -1 &&
799
	    (st.st_dev != fst.st_dev || st.st_ino != fst.st_ino)) {
800
		FILE *fp;
801
802
		fp = fopen(ypdb->dom_servlist, "r");
803
		if (fp) {
804
			fclose(ypdb->dom_servlistfp);
805
			ypdb->dom_servlistfp = fp;
806
		}
807
	}
808
	(void) rewind(ypdb->dom_servlistfp);
809
810
	memset(&bindsin, 0, sizeof bindsin);
811
	bindsin.sin_family = AF_INET;
812
	bindsin.sin_len = sizeof(bindsin);
813
	bindsin.sin_port = htons(PMAPPORT);
814
815
	while (fgets(line, sizeof(line), ypdb->dom_servlistfp) != NULL) {
816
		/* skip lines that are too big */
817
		p = strchr(line, '\n');
818
		if (p == NULL) {
819
			while ((c = getc(ypdb->dom_servlistfp)) != '\n' && c != EOF)
820
				;
821
			continue;
822
		}
823
		*p = '\0';
824
		p = line;
825
		while (isspace((unsigned char)*p))
826
			p++;
827
		if (*p == '#')
828
			continue;
829
		hp = gethostbyname(p);
830
		if (!hp)
831
			continue;
832
		/* step through all addresses in case first is unavailable */
833
		for (i = 0; hp->h_addr_list[i]; i++) {
834
			memmove(&bindsin.sin_addr, hp->h_addr_list[0],
835
			    hp->h_length);
836
			if (sendto(rpcsock, buf, outlen, 0,
837
			    (struct sockaddr *)&bindsin,
838
			    (socklen_t)sizeof bindsin) < 0) {
839
				perror("sendto");
840
				continue;
841
			}
842
		}
843
	}
844
	return 0;
845
}
846
847
enum clnt_stat
848
handle_replies(void)
849
{
850
	char buf[1400];
851
	int inlen;
852
	socklen_t fromlen;
853
	struct _dom_binding *ypdb;
854
	struct sockaddr_in raddr;
855
	struct rpc_msg msg;
856
	XDR xdr;
857
858
recv_again:
859
	memset(&xdr, 0, sizeof(xdr));
860
	memset(&msg, 0, sizeof(msg));
861
	msg.acpted_rply.ar_verf = _null_auth;
862
	msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr;
863
	msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
864
865
try_again:
866
	fromlen = sizeof (struct sockaddr);
867
	inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
868
	    (struct sockaddr *)&raddr, &fromlen);
869
	if (inlen < 0) {
870
		if (errno == EINTR)
871
			goto try_again;
872
		return RPC_CANTRECV;
873
	}
874
	if (inlen < sizeof(u_int32_t))
875
		goto recv_again;
876
877
	/*
878
	 * see if reply transaction id matches sent id.
879
	 * If so, decode the results.
880
	 */
881
	xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
882
	if (xdr_replymsg(&xdr, &msg)) {
883
		if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
884
		    (msg.acpted_rply.ar_stat == SUCCESS)) {
885
			raddr.sin_port = htons((u_short)rmtcr_port);
886
			ypdb = xid2ypdb(msg.rm_xid);
887
			if (ypdb)
888
				rpc_received(ypdb->dom_domain, &raddr, 0);
889
		}
890
	}
891
	xdr.x_op = XDR_FREE;
892
	msg.acpted_rply.ar_results.proc = xdr_void;
893
	xdr_destroy(&xdr);
894
895
	return RPC_SUCCESS;
896
}
897
898
enum clnt_stat
899
handle_ping(void)
900
{
901
	char buf[1400];
902
	int inlen;
903
	socklen_t fromlen;
904
	struct _dom_binding *ypdb;
905
	struct sockaddr_in raddr;
906
	struct rpc_msg msg;
907
	XDR xdr;
908
	bool_t res;
909
910
recv_again:
911
	memset(&xdr, 0, sizeof(xdr));
912
	memset(&msg, 0, sizeof(msg));
913
	msg.acpted_rply.ar_verf = _null_auth;
914
	msg.acpted_rply.ar_results.where = (caddr_t)&res;
915
	msg.acpted_rply.ar_results.proc = xdr_bool;
916
917
try_again:
918
	fromlen = sizeof (struct sockaddr);
919
	inlen = recvfrom(pingsock, buf, sizeof buf, 0,
920
	    (struct sockaddr *)&raddr, &fromlen);
921
	if (inlen < 0) {
922
		if (errno == EINTR)
923
			goto try_again;
924
		return RPC_CANTRECV;
925
	}
926
	if (inlen < sizeof(u_int32_t))
927
		goto recv_again;
928
929
	/*
930
	 * see if reply transaction id matches sent id.
931
	 * If so, decode the results.
932
	 */
933
	xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
934
	if (xdr_replymsg(&xdr, &msg)) {
935
		if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
936
		    (msg.acpted_rply.ar_stat == SUCCESS)) {
937
			ypdb = xid2ypdb(msg.rm_xid);
938
			if (ypdb)
939
				rpc_received(ypdb->dom_domain, &raddr, 0);
940
		}
941
	}
942
	xdr.x_op = XDR_FREE;
943
	msg.acpted_rply.ar_results.proc = xdr_void;
944
	xdr_destroy(&xdr);
945
946
	return RPC_SUCCESS;
947
}
948
949
/*
950
 * We prefer loopback connections.
951
 */
952
void
953
rpc_received(char *dom, struct sockaddr_in *raddrp, int force)
954
{
955
	struct _dom_binding *ypdb;
956
	struct iovec iov[2];
957
	struct ypbind_resp ybr;
958
	char path[PATH_MAX];
959
	int fd;
960
961
	if (strchr(dom, '/'))
962
		return;
963
964
#ifdef DEBUG
965
	printf("returned from %s about %s\n", inet_ntoa(raddrp->sin_addr), dom);
966
#endif
967
968
	if (dom == NULL)
969
		return;
970
971
	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
972
		if (!strcmp(ypdb->dom_domain, dom))
973
			break;
974
975
	if (ypdb == NULL) {
976
		if (force == 0)
977
			return;
978
		ypdb = malloc(sizeof *ypdb);
979
		if (ypdb == NULL)
980
			return;
981
		memset(ypdb, 0, sizeof *ypdb);
982
		strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain-1);
983
		ypdb->dom_domain[sizeof (ypdb->dom_domain)-1] = '\0';
984
		ypdb->dom_lockfd = -1;
985
		ypdb->dom_xid = unique_xid(ypdb);
986
		ypdb->dom_pnext = ypbindlist;
987
		ypbindlist = ypdb;
988
	}
989
990
	/* we do not support sunos 3.0 insecure servers */
991
	if (insecure == 0 && ntohs(raddrp->sin_port) >= IPPORT_RESERVED)
992
		return;
993
994
	/* soft update, alive */
995
	if (ypdb->dom_alive == 1 && force == 0) {
996
		if (!memcmp(&ypdb->dom_server_addr, raddrp,
997
		    sizeof ypdb->dom_server_addr)) {
998
			ypdb->dom_alive = 1;
999
			/* recheck binding in 60 sec */
1000
			ypdb->dom_check_t = time(NULL) + 60;
1001
		}
1002
		if (raddrp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
1003
			/*
1004
			 * we are alive and already have a binding, but
1005
			 * after a broadcast we prefer the localhost
1006
			 */
1007
			memcpy(&ypdb->dom_server_addr, raddrp,
1008
			    sizeof ypdb->dom_server_addr);
1009
		}
1010
		return;
1011
	}
1012
1013
	memcpy(&ypdb->dom_server_addr, raddrp, sizeof ypdb->dom_server_addr);
1014
	/* recheck binding in 60 seconds */
1015
	ypdb->dom_check_t = time(NULL) + 60;
1016
	ypdb->dom_vers = YPVERS;
1017
	ypdb->dom_alive = 1;
1018
1019
	if (ypdb->dom_lockfd != -1)
1020
		close(ypdb->dom_lockfd);
1021
1022
	snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
1023
	    ypdb->dom_domain, (int)ypdb->dom_vers);
1024
#ifdef O_SHLOCK
1025
	if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
1026
		(void)mkdir(BINDINGDIR, 0755);
1027
		if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC,
1028
		    0644)) == -1)
1029
			return;
1030
	}
1031
#else
1032
	if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
1033
		(void)mkdir(BINDINGDIR, 0755);
1034
		if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
1035
			return;
1036
	}
1037
	flock(fd, LOCK_SH);
1038
#endif
1039
1040
	if (fchmod(fd, 0644) == -1)
1041
		err(1, "fchmod");
1042
1043
	/*
1044
	 * ok, if BINDINGDIR exists, and we can create the binding file,
1045
	 * then write to it..
1046
	 */
1047
	ypdb->dom_lockfd = fd;
1048
1049
	iov[0].iov_base = (caddr_t)&(udptransp->xp_port);
1050
	iov[0].iov_len = sizeof udptransp->xp_port;
1051
	iov[1].iov_base = (caddr_t)&ybr;
1052
	iov[1].iov_len = sizeof ybr;
1053
1054
	memset(&ybr, 0, sizeof ybr);
1055
	ybr.ypbind_status = YPBIND_SUCC_VAL;
1056
	memmove(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
1057
	    &raddrp->sin_addr,
1058
	    sizeof(ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr));
1059
	memmove(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
1060
	    &raddrp->sin_port,
1061
	    sizeof(ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port));
1062
1063
	if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
1064
		perror("write");
1065
		close(ypdb->dom_lockfd);
1066
		unlink(path);
1067
		ypdb->dom_lockfd = -1;
1068
		return;
1069
	}
1070
}
1071
1072
struct _dom_binding *
1073
xid2ypdb(u_int32_t xid)
1074
{
1075
	struct _dom_binding *ypdb;
1076
1077
	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
1078
		if (ypdb->dom_xid == xid)
1079
			break;
1080
	return (ypdb);
1081
}
1082
1083
u_int32_t
1084
unique_xid(struct _dom_binding *ypdb)
1085
{
1086
	u_int32_t xid;
1087
1088
	xid = arc4random();
1089
	while (xid2ypdb(xid) != NULL)
1090
		xid++;
1091
1092
	return (xid);
1093
}