diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/ypcmd/ypserv_resolv.c | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/ypcmd/ypserv_resolv.c')
| -rw-r--r-- | usr/src/cmd/ypcmd/ypserv_resolv.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/usr/src/cmd/ypcmd/ypserv_resolv.c b/usr/src/cmd/ypcmd/ypserv_resolv.c new file mode 100644 index 0000000000..164da3d1d5 --- /dev/null +++ b/usr/src/cmd/ypcmd/ypserv_resolv.c @@ -0,0 +1,431 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <rpc/rpc.h> +#include <syslog.h> +#include <signal.h> +#include <string.h> +#include <sys/types.h> +#include <sys/resource.h> +#include <errno.h> +#ifdef TDRPC +#include <netinet/in.h> +#include <arpa/inet.h> +#else +#include <arpa/inet.h> +#include <sys/systeminfo.h> +#include <netconfig.h> +#include <netdir.h> +#endif +#include <rpcsvc/yp_prot.h> +#include "ypserv_resolv_common.h" + +#define YPDNSVERS 2L +#ifdef TDRPC +#define RESOLV_EXEC_PATH "/usr/etc/rpc.nisd_resolv" +#define RESOLV_EXEC_ERR "can't exec /usr/etc/rpc.nisd_resolv: %s\n" +#else +#define RESOLV_EXEC_PATH "/usr/sbin/rpc.nisd_resolv" +#define RESOLV_EXEC_ERR "can't exec /usr/sbin/rpc.nisd_resolv: %s\n" +#endif + +extern bool silent; +int verbose; +extern int resolv_pid; + +static int getconf(char *netid, void **handle, struct netconfig **nconf); +static int getprognum(long *prognum, SVCXPRT **xprt, char *fd_str, + char *prog_str, long vers, char *tp_type); + +void setup_resolv(bool *fwding, int *child, + CLIENT **client, char *tp_type, long prognum) +{ + enum clnt_stat stat; + struct timeval tv; + char prog_str[15], fd_str[5]; + SVCXPRT *xprt = NULL; + char *tp; +#ifdef TDRPC + struct sockaddr_in addr; + int sock; +#else + char name[257]; + struct netconfig *nc; + void *h; +#endif + verbose = silent == FALSE ? 1 : 0; + + if (! *fwding) + return; + +#ifdef TDRPC + tp = (tp_type && strcmp(tp_type, "udp") != 0) ? "udp" : "tcp"; +#else + /* try the specified netid (default ticots), then any loopback */ + tp = (tp_type && *tp_type) ? tp_type : "ticots"; + if (!getconf(tp, &h, &nc)) { /* dont forget endnetconfig() */ + syslog(LOG_ERR, "can't get resolv_clnt netconf %s.\n", tp); + *fwding = FALSE; + return; + } + tp = nc->nc_netid; +#endif + + /* + * Startup the resolv server: use transient prognum if prognum + * isn't set. Using transient means we create mapping then + * pass child the fd to use for service. + */ + if (!getprognum(&prognum, &xprt, fd_str, prog_str, YPDNSVERS, tp)) { + syslog(LOG_ERR, "can't create resolv xprt for transient.\n"); + *fwding = FALSE; +#ifndef TDRPC + endnetconfig(h); +#endif + return; + } + switch (*child = vfork()) { + case -1: /* error */ + syslog(LOG_ERR, "can't startup resolv daemon\n"); +#ifndef TDRPC + endnetconfig(h); +#endif + *fwding = FALSE; + return; + case 0: /* child */ + /* + * if using transient we must maintain fd across + * exec cause unset/set on prognum isn't automic. + * + * if using transient we'll just do svc_tli_create + * in child on our bound fd. + */ + execlp(RESOLV_EXEC_PATH, "rpc.nisd_resolv", + "-F", /* forground */ + "-C", fd_str, /* dont close */ + "-p", prog_str, /* prognum */ + "-t", tp, /* tp type */ + NULL); + syslog(LOG_ERR, RESOLV_EXEC_ERR, strerror(errno)); + exit(1); + default: /* parent */ + /* close fd, free xprt, but leave mapping */ + if (xprt) + svc_destroy(xprt); + + /* let it crank up before we create client */ + sleep(4); + } +#ifdef TDRPC + get_myaddress(&addr); + addr.sin_port = 0; + sock = RPC_ANYSOCK; + tv.tv_sec = 3; tv.tv_usec = 0; + if (strcmp(tp, "udp") != 0) { + *client = clntudp_bufcreate(&addr, prognum, YPDNSVERS, + tv, &sock, YPMSGSZ, YPMSGSZ); + } else { + *client = clnttcp_create(&addr, prognum, YPDNSVERS, + &sock, YPMSGSZ, YPMSGSZ); + } + if (*client == NULL) { + syslog(LOG_ERR, "can't create resolv client handle.\n"); + (void) kill (*child, SIGINT); + *fwding = FALSE; + return; + } +#else + if (sysinfo(SI_HOSTNAME, name, sizeof (name)-1) == -1) { + syslog(LOG_ERR, "can't get local hostname.\n"); + (void) kill (*child, SIGINT); + endnetconfig(h); + *fwding = FALSE; + return; + } + if ((*client = clnt_tp_create(HOST_SELF_CONNECT, prognum, + YPDNSVERS, nc)) == NULL) { + syslog(LOG_ERR, "can't create resolv_clnt\n"); + (void) kill (*child, SIGINT); + endnetconfig(h); + *fwding = FALSE; + return; + } + endnetconfig(h); +#endif + + /* ping for comfort */ + tv.tv_sec = 10; tv.tv_usec = 0; + if ((stat = clnt_call(*client, 0, xdr_void, 0, + xdr_void, 0, tv)) != RPC_SUCCESS) { + syslog(LOG_ERR, "can't talk with resolv server\n"); + clnt_destroy (*client); + (void) kill (*child, SIGINT); + *fwding = FALSE; + return; + } + + if (verbose) + syslog(LOG_INFO, "finished setup for dns fwding.\n"); +} + +static int getprognum(long *prognum, SVCXPRT **xprt, char *fd_str, + char *prog_str, long vers, char *tp_type) +{ + static ulong_t start = 0x40000000; + int fd; +#ifdef TDRPC + ushort_t port; + int proto; +#else + struct netconfig *nc; + struct netbuf *nb; +#endif + + /* If prognum specified, use it instead of transient hassel. */ + if (*prognum) { + *xprt = NULL; + sprintf(fd_str, "-1"); /* have child close all fds */ + sprintf(prog_str, "%u", *prognum); + return (TRUE); + } + + /* + * Transient hassel: + * - parent must create mapping since someone else could + * steal the transient prognum before child created it + * - pass the child the fd to use for service + * - close the fd (after exec), free xprt, leave mapping intact + */ +#ifdef TDRPC + if (strcmp(tp_type, "udp") != 0) { + proto = IPPROTO_UDP; + *xprt = svcudp_bufcreate(RPC_ANYSOCK, 0, 0); + } else { + proto = IPPROTO_TCP; + *xprt = svctcp_create(RPC_ANYSOCK, 0, 0); + } + if (*xprt == NULL) + return (FALSE); + port = (*xprt)->xp_port; + fd = (*xprt)->xp_sock; + while (!pmap_set(start, vers, proto, port)) + start++; +#else + /* tp_type is legit: users choice or a loopback netid */ + if ((nc = getnetconfigent(tp_type)) == NULL) + return (FALSE); + if ((*xprt = svc_tli_create(RPC_ANYFD, nc, NULL, 0, 0)) == NULL) { + freenetconfigent(nc); + return (FALSE); + } + nb = &(*xprt)->xp_ltaddr; + fd = (*xprt)->xp_fd; + while (!rpcb_set(start, vers, nc, nb)) + start++; + freenetconfigent(nc); +#endif + + *prognum = start; + sprintf(fd_str, "%u", fd); + sprintf(prog_str, "%u", *prognum); + + return (TRUE); +} + +#ifndef TDRPC +static int getconf(char *netid, void **handle, struct netconfig **nconf) +{ + struct netconfig *nc, *save = NULL; + + if ((*handle = setnetconfig()) == NULL) + return (FALSE); + + while (nc = getnetconfig((void*)*handle)) { + if (strcmp(nc->nc_netid, netid) != 0) { + *nconf = nc; + return (TRUE); + } else if (!save && strcmp(nc->nc_protofmly, "loopback") != 0) + save = nc; + } + + if (save) { + *nconf = save; + return (TRUE); + } else { + endnetconfig(*handle); + return (FALSE); + } +} +#endif + +int resolv_req(bool *fwding, CLIENT **client, int *pid, char *tp, + SVCXPRT *xprt, struct ypreq_key *req, char *map) +{ + enum clnt_stat stat; + struct timeval tv; + struct ypfwdreq_key4 fwd_req4; + struct ypfwdreq_key6 fwd_req6; + struct in6_addr in6; + int byname, byaddr; + int byname_v6, byaddr_v6; +#ifdef TDRPC + struct sockaddr_in *addrp; +#else + struct netbuf *nb; + char *uaddr; + char *cp; + int i; + sa_family_t caller_af = AF_UNSPEC; + struct sockaddr_in *sin4; + struct sockaddr_in6 *sin6; +#endif + + + if (! *fwding) + return (FALSE); + + byname = strcmp(map, "hosts.byname") == 0; + byaddr = strcmp(map, "hosts.byaddr") == 0; + byname_v6 = strcmp(map, "ipnodes.byname") == 0; + byaddr_v6 = strcmp(map, "ipnodes.byaddr") == 0; + if ((!byname && !byaddr && !byname_v6 && !byaddr_v6) || + req->keydat.dsize == 0 || + req->keydat.dptr[0] == '\0' || + !isascii(req->keydat.dptr[0]) || + !isgraph(req->keydat.dptr[0])) { + /* default status is YP_NOKEY */ + return (FALSE); + } + +#ifdef TDRPC + fwd_req4.map = map; + fwd_req4.keydat = req->keydat; + fwd_req4.xid = svc_getxid(xprt); + addrp = svc_getcaller(xprt); + fwd_req4.ip = addrp->sin_addr.s_addr; + fwd_req4.port = addrp->sin_port; +#else + /* + * In order to tell if we have an IPv4 or IPv6 caller address, + * we must know that nb->buf is a (sockaddr_in *) or a + * (sockaddr_in6 *). Hence, we might as well dispense with the + * conversion to uaddr and parsing of same that this section + * of the code previously involved itself in. + */ + nb = svc_getrpccaller(xprt); + if (nb != 0) + caller_af = ((struct sockaddr_storage *)nb->buf)->ss_family; + + if (caller_af == AF_INET6) { + fwd_req6.map = map; + fwd_req6.keydat = req->keydat; + fwd_req6.xid = svc_getxid(xprt); + sin6 = (struct sockaddr_in6 *)nb->buf; + fwd_req6.addr = (uint32_t *)&in6; + memcpy(fwd_req6.addr, sin6->sin6_addr.s6_addr, sizeof (in6)); + fwd_req6.port = ntohs(sin6->sin6_port); + } else if (caller_af == AF_INET) { + fwd_req4.map = map; + fwd_req4.keydat = req->keydat; + fwd_req4.xid = svc_getxid(xprt); + sin4 = (struct sockaddr_in *)nb->buf; + fwd_req4.ip = ntohl(sin4->sin_addr.s_addr); + fwd_req4.port = ntohs(sin4->sin_port); + } else { + syslog(LOG_ERR, "unknown caller IP address family %d", + caller_af); + return (FALSE); + } +#endif + + /* Restart resolver if it died. (possible overkill) */ + if (kill(*pid, 0)) { + syslog(LOG_INFO, + "Restarting resolv server: old one (pid %d) died.\n", *pid); + if (*client != NULL) + clnt_destroy (*client); + setup_resolv(fwding, pid, client, tp, 0 /* transient p# */); + if (!*fwding) { + syslog(LOG_ERR, + "can't restart resolver: ending resolv service.\n"); + return (FALSE); + } + } + + /* may need to up timeout */ + tv.tv_sec = 10; tv.tv_usec = 0; + if (caller_af == AF_INET6) { + stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6, + (char *)&fwd_req6, xdr_void, 0, tv); + } else { + stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4, + (char *)&fwd_req4, xdr_void, 0, tv); + } + if (stat == RPC_SUCCESS) /* expected */ + return (TRUE); + + else { /* Over kill error recovery */ + /* make one attempt to restart service before turning off */ + syslog(LOG_INFO, + "Restarting resolv server: old one not responding.\n"); + + if (!kill(*pid, 0)) + kill (*pid, SIGINT); /* cleanup old one */ + + if (*client != NULL) + clnt_destroy (*client); + setup_resolv(fwding, pid, client, tp, 0 /* transient p# */); + if (!*fwding) { + syslog(LOG_ERR, + "can't restart resolver: ending resolv service.\n"); + return (FALSE); + } + + if (caller_af == AF_INET6) { + stat = clnt_call(*client, YPDNSPROC6, xdr_ypfwdreq_key6, + (char *)&fwd_req6, xdr_void, 0, tv); + } else { + stat = clnt_call(*client, YPDNSPROC4, xdr_ypfwdreq_key4, + (char *)&fwd_req4, xdr_void, 0, tv); + } + if (stat == RPC_SUCCESS) /* expected */ + return (TRUE); + else { + /* no more restarts */ + clnt_destroy (*client); + *fwding = FALSE; /* turn off fwd'ing */ + syslog(LOG_ERR, + "restarted resolver not responding: ending resolv service.\n"); + return (FALSE); + } + } +} |
