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
setup_resolv(bool * fwding,int * child,CLIENT ** client,char * tp_type,long prognum)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
getprognum(long * prognum,SVCXPRT ** xprt,char * fd_str,char * prog_str,long vers,char * tp_type)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
getconf(char * netid,void ** handle,struct netconfig ** nconf)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
resolv_req(bool * fwding,CLIENT ** client,int * pid,char * tp,SVCXPRT * xprt,struct ypreq_key * req,char * map)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