xref: /illumos-gate/usr/src/cmd/ypcmd/ypserv_resolv.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <rpc/rpc.h>
34 #include <syslog.h>
35 #include <signal.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/resource.h>
39 #include <errno.h>
40 #ifdef TDRPC
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #else
44 #include <arpa/inet.h>
45 #include <sys/systeminfo.h>
46 #include <netconfig.h>
47 #include <netdir.h>
48 #endif
49 #include <rpcsvc/yp_prot.h>
50 #include "ypserv_resolv_common.h"
51 
52 #define	YPDNSVERS	2L
53 #ifdef TDRPC
54 #define	RESOLV_EXEC_PATH	"/usr/etc/rpc.nisd_resolv"
55 #define	RESOLV_EXEC_ERR		"can't exec /usr/etc/rpc.nisd_resolv: %s\n"
56 #else
57 #define	RESOLV_EXEC_PATH	"/usr/sbin/rpc.nisd_resolv"
58 #define	RESOLV_EXEC_ERR		"can't exec /usr/sbin/rpc.nisd_resolv: %s\n"
59 #endif
60 
61 extern bool silent;
62 int verbose;
63 extern int resolv_pid;
64 
65 static int getconf(char *netid, void **handle, struct netconfig **nconf);
66 static int getprognum(long *prognum, SVCXPRT **xprt, char *fd_str,
67 			char *prog_str, long vers, char *tp_type);
68 
69 void setup_resolv(bool *fwding, int *child,
70 			CLIENT **client, char *tp_type, long prognum)
71 {
72 	enum clnt_stat stat;
73 	struct timeval tv;
74 	char prog_str[15], fd_str[5];
75 	SVCXPRT *xprt = NULL;
76 	char *tp;
77 #ifdef	TDRPC
78 	struct sockaddr_in addr;
79 	int sock;
80 #else
81 	char name[257];
82 	struct netconfig *nc;
83 	void *h;
84 #endif
85 	verbose = silent == FALSE ? 1 : 0;
86 
87 	if (! *fwding)
88 		return;
89 
90 #ifdef	TDRPC
91 	tp = (tp_type && strcmp(tp_type, "udp") != 0) ? "udp" : "tcp";
92 #else
93 	/* try the specified netid (default ticots), then any loopback */
94 	tp = (tp_type && *tp_type) ? tp_type : "ticots";
95 	if (!getconf(tp, &h, &nc)) { /* dont forget endnetconfig() */
96 		syslog(LOG_ERR, "can't get resolv_clnt netconf %s.\n", tp);
97 		*fwding = FALSE;
98 		return;
99 	}
100 	tp = nc->nc_netid;
101 #endif
102 
103 	/*
104 	 * Startup the resolv server: use transient prognum if prognum
105 	 * isn't set. Using transient means we create mapping then
106 	 * pass child the fd to use for service.
107 	 */
108 	if (!getprognum(&prognum, &xprt, fd_str, prog_str, YPDNSVERS, tp)) {
109 		syslog(LOG_ERR, "can't create resolv xprt for transient.\n");
110 		*fwding = FALSE;
111 #ifndef TDRPC
112 		endnetconfig(h);
113 #endif
114 		return;
115 	}
116 	switch (*child = vfork()) {
117 	case -1: /* error  */
118 		syslog(LOG_ERR, "can't startup resolv daemon\n");
119 #ifndef TDRPC
120 		endnetconfig(h);
121 #endif
122 		*fwding = FALSE;
123 		return;
124 	case 0:  /* child  */
125 		/*
126 		 * if using transient we must maintain fd across
127 		 * exec cause unset/set on prognum isn't automic.
128 		 *
129 		 * if using transient we'll just do svc_tli_create
130 		 * in child on our bound fd.
131 		 */
132 		execlp(RESOLV_EXEC_PATH, "rpc.nisd_resolv",
133 				"-F",		/* forground  */
134 				"-C", fd_str,	/* dont close */
135 				"-p", prog_str,	/* prognum    */
136 				"-t", tp,	/* tp type    */
137 				NULL);
138 		syslog(LOG_ERR, RESOLV_EXEC_ERR, strerror(errno));
139 		exit(1);
140 	default: /* parent */
141 		/* close fd, free xprt, but leave mapping */
142 		if (xprt)
143 			svc_destroy(xprt);
144 
145 		/* let it crank up before we create client */
146 		sleep(4);
147 	}
148 #ifdef TDRPC
149 	get_myaddress(&addr);
150 	addr.sin_port = 0;
151 	sock = RPC_ANYSOCK;
152 	tv.tv_sec = 3; tv.tv_usec = 0;
153 	if (strcmp(tp, "udp") != 0) {
154 		*client = clntudp_bufcreate(&addr, prognum, YPDNSVERS,
155 					tv, &sock, YPMSGSZ, YPMSGSZ);
156 	} else {
157 		*client = clnttcp_create(&addr, prognum, YPDNSVERS,
158 					&sock, YPMSGSZ, YPMSGSZ);
159 	}
160 	if (*client == NULL) {
161 		syslog(LOG_ERR, "can't create resolv client handle.\n");
162 		(void) kill (*child, SIGINT);
163 		*fwding = FALSE;
164 		return;
165 	}
166 #else
167 	if (sysinfo(SI_HOSTNAME, name, sizeof (name)-1) == -1) {
168 		syslog(LOG_ERR, "can't get local hostname.\n");
169 		(void) kill (*child, SIGINT);
170 		endnetconfig(h);
171 		*fwding = FALSE;
172 		return;
173 	}
174 	if ((*client = clnt_tp_create(HOST_SELF_CONNECT, prognum,
175 			YPDNSVERS, nc)) == NULL) {
176 		syslog(LOG_ERR, "can't create resolv_clnt\n");
177 		(void) kill (*child, SIGINT);
178 		endnetconfig(h);
179 		*fwding = FALSE;
180 		return;
181 	}
182 	endnetconfig(h);
183 #endif
184 
185 	/* ping for comfort */
186 	tv.tv_sec = 10; tv.tv_usec = 0;
187 	if ((stat = clnt_call(*client, 0, xdr_void, 0,
188 				xdr_void, 0, tv)) != RPC_SUCCESS) {
189 		syslog(LOG_ERR, "can't talk with resolv server\n");
190 		clnt_destroy (*client);
191 		(void) kill (*child, SIGINT);
192 		*fwding = FALSE;
193 		return;
194 	}
195 
196 	if (verbose)
197 		syslog(LOG_INFO, "finished setup for dns fwding.\n");
198 }
199 
200 static int getprognum(long *prognum, SVCXPRT **xprt, char *fd_str,
201 			char *prog_str, long vers, char *tp_type)
202 {
203 	static ulong_t start = 0x40000000;
204 	int fd;
205 #ifdef TDRPC
206 	ushort_t port;
207 	int proto;
208 #else
209 	struct netconfig *nc;
210 	struct netbuf *nb;
211 #endif
212 
213 	/* If prognum specified, use it instead of transient hassel. */
214 	if (*prognum) {
215 		*xprt = NULL;
216 		sprintf(fd_str, "-1"); /* have child close all fds */
217 		sprintf(prog_str, "%u", *prognum);
218 		return (TRUE);
219 	}
220 
221 	/*
222 	 * Transient hassel:
223 	 *	- parent must create mapping since someone else could
224 	 *	  steal the transient prognum before child created it
225 	 * 	- pass the child the fd to use for service
226 	 * 	- close the fd (after exec), free xprt, leave mapping intact
227 	 */
228 #ifdef TDRPC
229 	if (strcmp(tp_type, "udp") != 0) {
230 		proto = IPPROTO_UDP;
231 		*xprt = svcudp_bufcreate(RPC_ANYSOCK, 0, 0);
232 	} else {
233 		proto = IPPROTO_TCP;
234 		*xprt = svctcp_create(RPC_ANYSOCK, 0, 0);
235 	}
236 	if (*xprt == NULL)
237 		return (FALSE);
238 	port = (*xprt)->xp_port;
239 	fd = (*xprt)->xp_sock;
240 	while (!pmap_set(start, vers, proto, port))
241 		start++;
242 #else
243 	/* tp_type is legit: users choice or a loopback netid */
244 	if ((nc = getnetconfigent(tp_type)) == NULL)
245 		return (FALSE);
246 	if ((*xprt = svc_tli_create(RPC_ANYFD, nc, NULL, 0, 0)) == NULL) {
247 		freenetconfigent(nc);
248 		return (FALSE);
249 	}
250 	nb = &(*xprt)->xp_ltaddr;
251 	fd = (*xprt)->xp_fd;
252 	while (!rpcb_set(start, vers, nc, nb))
253 		start++;
254 	freenetconfigent(nc);
255 #endif
256 
257 	*prognum = start;
258 	sprintf(fd_str, "%u", fd);
259 	sprintf(prog_str, "%u", *prognum);
260 
261 	return (TRUE);
262 }
263 
264 #ifndef TDRPC
265 static int getconf(char *netid, void **handle, struct netconfig **nconf)
266 {
267 	struct netconfig *nc, *save = NULL;
268 
269 	if ((*handle = setnetconfig()) == NULL)
270 		return (FALSE);
271 
272 	while (nc = getnetconfig((void*)*handle)) {
273 		if (strcmp(nc->nc_netid, netid) != 0) {
274 			*nconf = nc;
275 			return (TRUE);
276 		} else if (!save && strcmp(nc->nc_protofmly, "loopback") != 0)
277 			save = nc;
278 	}
279 
280 	if (save) {
281 		*nconf = save;
282 		return (TRUE);
283 	} else {
284 		endnetconfig(*handle);
285 		return (FALSE);
286 	}
287 }
288 #endif
289 
290 int resolv_req(bool *fwding, CLIENT **client, int *pid, char *tp,
291 		SVCXPRT *xprt, struct ypreq_key *req, char *map)
292 {
293 	enum clnt_stat stat;
294 	struct timeval tv;
295 	struct ypfwdreq_key4 fwd_req4;
296 	struct ypfwdreq_key6 fwd_req6;
297 	struct in6_addr in6;
298 	int byname, byaddr;
299 	int byname_v6, byaddr_v6;
300 #ifdef TDRPC
301 	struct sockaddr_in *addrp;
302 #else
303 	struct netbuf *nb;
304 	char *uaddr;
305 	char *cp;
306 	int i;
307 	sa_family_t caller_af = AF_UNSPEC;
308 	struct sockaddr_in *sin4;
309 	struct sockaddr_in6 *sin6;
310 #endif
311 
312 
313 	if (! *fwding)
314 		return (FALSE);
315 
316 	byname = strcmp(map, "hosts.byname") == 0;
317 	byaddr = strcmp(map, "hosts.byaddr") == 0;
318 	byname_v6 = strcmp(map, "ipnodes.byname") == 0;
319 	byaddr_v6 = strcmp(map, "ipnodes.byaddr") == 0;
320 	if ((!byname && !byaddr && !byname_v6 && !byaddr_v6) ||
321 				req->keydat.dsize == 0 ||
322 				req->keydat.dptr[0] == '\0' ||
323 				!isascii(req->keydat.dptr[0]) ||
324 				!isgraph(req->keydat.dptr[0])) {
325 		/* default status is YP_NOKEY */
326 		return (FALSE);
327 	}
328 
329 #ifdef TDRPC
330 	fwd_req4.map = map;
331 	fwd_req4.keydat = req->keydat;
332 	fwd_req4.xid = svc_getxid(xprt);
333 	addrp = svc_getcaller(xprt);
334 	fwd_req4.ip = addrp->sin_addr.s_addr;
335 	fwd_req4.port = addrp->sin_port;
336 #else
337 	/*
338 	 * In order to tell if we have an IPv4 or IPv6 caller address,
339 	 * we must know that nb->buf is a (sockaddr_in *) or a
340 	 * (sockaddr_in6 *). Hence, we might as well dispense with the
341 	 * conversion to uaddr and parsing of same that this section
342 	 * of the code previously involved itself in.
343 	 */
344 	nb = svc_getrpccaller(xprt);
345 	if (nb != 0)
346 		caller_af = ((struct sockaddr_storage *)nb->buf)->ss_family;
347 
348 	if (caller_af == AF_INET6) {
349 		fwd_req6.map = map;
350 		fwd_req6.keydat = req->keydat;
351 		fwd_req6.xid = svc_getxid(xprt);
352 		sin6 = (struct sockaddr_in6 *)nb->buf;
353 		fwd_req6.addr = (uint32_t *)&in6;
354 		memcpy(fwd_req6.addr, sin6->sin6_addr.s6_addr, sizeof (in6));
355 		fwd_req6.port = ntohs(sin6->sin6_port);
356 	} else if (caller_af == AF_INET) {
357 		fwd_req4.map = map;
358 		fwd_req4.keydat = req->keydat;
359 		fwd_req4.xid = svc_getxid(xprt);
360 		sin4 = (struct sockaddr_in *)nb->buf;
361 		fwd_req4.ip = ntohl(sin4->sin_addr.s_addr);
362 		fwd_req4.port = ntohs(sin4->sin_port);
363 	} else {
364 		syslog(LOG_ERR, "unknown caller IP address family %d",
365 			caller_af);
366 		return (FALSE);
367 	}
368 #endif
369 
370 	/* Restart resolver if it died. (possible overkill) */
371 	if (kill(*pid, 0)) {
372 		syslog(LOG_INFO,
373 		"Restarting resolv server: old one (pid %d) died.\n", *pid);
374 		if (*client != NULL)
375 			clnt_destroy (*client);
376 		setup_resolv(fwding, pid, client, tp, 0 /* transient p# */);
377 		if (!*fwding) {
378 			syslog(LOG_ERR,
379 			"can't restart resolver: ending resolv service.\n");
380 			return (FALSE);
381 		}
382 	}
383 
384 	/* may need to up timeout */
385 	tv.tv_sec = 10; tv.tv_usec = 0;
386 	if (caller_af == AF_INET6) {
387 		stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6,
388 					(char *)&fwd_req6, xdr_void, 0, tv);
389 	} else {
390 		stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4,
391 					(char *)&fwd_req4, xdr_void, 0, tv);
392 	}
393 	if (stat == RPC_SUCCESS) /* expected */
394 		return (TRUE);
395 
396 	else { /* Over kill error recovery */
397 		/* make one attempt to restart service before turning off */
398 		syslog(LOG_INFO,
399 			"Restarting resolv server: old one not responding.\n");
400 
401 		if (!kill(*pid, 0))
402 			kill (*pid, SIGINT); /* cleanup old one */
403 
404 		if (*client != NULL)
405 			clnt_destroy (*client);
406 		setup_resolv(fwding, pid, client, tp, 0 /* transient p# */);
407 		if (!*fwding) {
408 			syslog(LOG_ERR,
409 			"can't restart resolver: ending resolv service.\n");
410 			return (FALSE);
411 		}
412 
413 		if (caller_af == AF_INET6) {
414 			stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6,
415 					(char *)&fwd_req6, xdr_void, 0, tv);
416 		} else {
417 			stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4,
418 					(char *)&fwd_req4, xdr_void, 0, tv);
419 		}
420 		if (stat == RPC_SUCCESS) /* expected */
421 			return (TRUE);
422 		else {
423 			/* no more restarts */
424 			clnt_destroy (*client);
425 			*fwding = FALSE; /* turn off fwd'ing */
426 			syslog(LOG_ERR,
427 		"restarted resolver not responding: ending resolv service.\n");
428 			return (FALSE);
429 		}
430 	}
431 }
432