#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
tp = (tp_type && *tp_type) ? tp_type : "ticots";
if (!getconf(tp, &h, &nc)) {
syslog(LOG_ERR, "can't get resolv_clnt netconf %s.\n", tp);
*fwding = FALSE;
return;
}
tp = nc->nc_netid;
#endif
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:
syslog(LOG_ERR, "can't startup resolv daemon\n");
#ifndef TDRPC
endnetconfig(h);
#endif
*fwding = FALSE;
return;
case 0:
execlp(RESOLV_EXEC_PATH, "rpc.nisd_resolv",
"-F",
"-C", fd_str,
"-p", prog_str,
"-t", tp,
NULL);
syslog(LOG_ERR, RESOLV_EXEC_ERR, strerror(errno));
exit(1);
default:
if (xprt)
svc_destroy(xprt);
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
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) {
*xprt = NULL;
sprintf(fd_str, "-1");
sprintf(prog_str, "%u", *prognum);
return (TRUE);
}
#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
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])) {
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
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
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 );
if (!*fwding) {
syslog(LOG_ERR,
"can't restart resolver: ending resolv service.\n");
return (FALSE);
}
}
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)
return (TRUE);
else {
syslog(LOG_INFO,
"Restarting resolv server: old one not responding.\n");
if (!kill(*pid, 0))
kill (*pid, SIGINT);
if (*client != NULL)
clnt_destroy (*client);
setup_resolv(fwding, pid, client, tp, 0 );
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)
return (TRUE);
else {
clnt_destroy (*client);
*fwding = FALSE;
syslog(LOG_ERR,
"restarted resolver not responding: ending resolv service.\n");
return (FALSE);
}
}
}