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