- Fix main() to use two separated sockets for the two transports

when "-P port" is specified.  It invoked svc{tcp,udp}_create()
  for only one of the two allocated sockets, and prevented the
  TCP socket from binding to as the result.

- Use TI-RPC functions and handle sockets in a
  transport-independent way.  At this moment only AF_INET ("udp"
  and "tcp") is supported because others need rewrites of ACL
  handling and yp clients.

- Add '-h addr' to specify addresses to bind to.

- Convert _msgout() to use variable argument lists and remove
  asprintf() for error strings.

- Remove register storage class specifier.

Discussed with:	kuriyama
MFC after:	1 week
This commit is contained in:
Hiroki Sato 2009-12-13 15:19:01 +00:00
parent de0bd6f76b
commit f96264e375
2 changed files with 300 additions and 116 deletions

View File

@ -40,25 +40,30 @@ __FBSDID("$FreeBSD$");
* rpcgen.new, and later modified. * rpcgen.new, and later modified.
*/ */
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include "yp.h" #include "yp.h"
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <memory.h> #include <memory.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
#include <stdarg.h>
#include <stdlib.h> /* getenv, exit */ #include <stdlib.h> /* getenv, exit */
#include <string.h> /* strcmp */ #include <string.h> /* strcmp */
#include <syslog.h> #include <syslog.h>
#include <unistd.h> #include <unistd.h>
#include <rpc/pmap_clnt.h> /* for pmap_unset */
#ifdef __cplusplus #ifdef __cplusplus
#include <sysent.h> /* getdtablesize, open */ #include <sysent.h> /* getdtablesize, open */
#endif /* __cplusplus */ #endif /* __cplusplus */
#include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/wait.h> #include <netdb.h>
#include "yp_extern.h" #include "yp_extern.h"
#include <netconfig.h>
#include <rpc/rpc.h> #include <rpc/rpc.h>
#include <rpc/rpc_com.h>
#ifndef SIG_PF #ifndef SIG_PF
#define SIG_PF void(*)(int) #define SIG_PF void(*)(int)
@ -68,14 +73,17 @@ __FBSDID("$FreeBSD$");
int _rpcpmstart; /* Started by a port monitor ? */ int _rpcpmstart; /* Started by a port monitor ? */
static int _rpcfdtype; static int _rpcfdtype;
/* Whether Stream or Datagram ? */ /* Whether Stream or Datagram ? */
static int _rpcaf;
static int _rpcfd;
/* States a server can be in wrt request */ /* States a server can be in wrt request */
#define _IDLE 0 #define _IDLE 0
#define _SERVED 1 #define _SERVED 1
#define _SERVING 2 #define _SERVING 2
extern void ypprog_1(struct svc_req *, register SVCXPRT *); extern void ypprog_1(struct svc_req *, SVCXPRT *);
extern void ypprog_2(struct svc_req *, register SVCXPRT *); extern void ypprog_2(struct svc_req *, SVCXPRT *);
extern int _rpc_dtablesize(void); extern int _rpc_dtablesize(void);
extern int _rpcsvcstate; /* Set when a request is serviced */ extern int _rpcsvcstate; /* Set when a request is serviced */
char *progname = "ypserv"; char *progname = "ypserv";
@ -84,26 +92,37 @@ char *yp_dir = _PATH_YP;
int do_dns = 0; int do_dns = 0;
int resfd; int resfd;
struct socktype { struct socklistent {
const char *st_name; int sle_sock;
int st_type; struct sockaddr_storage sle_ss;
SLIST_ENTRY(socklistent) sle_next;
}; };
static struct socktype stlist[] = { static SLIST_HEAD(, socklistent) sle_head =
{ "tcp", SOCK_STREAM }, SLIST_HEAD_INITIALIZER(&sle_head);
{ "udp", SOCK_DGRAM },
{ NULL, 0 } struct bindaddrlistent {
const char *ble_hostname;
SLIST_ENTRY(bindaddrlistent) ble_next;
}; };
static SLIST_HEAD(, bindaddrlistent) ble_head =
SLIST_HEAD_INITIALIZER(&ble_head);
static char *servname = "0";
static static
void _msgout(char* msg) void _msgout(char* msg, ...)
{ {
va_list ap;
va_start(ap, msg);
if (debug) { if (debug) {
if (_rpcpmstart) if (_rpcpmstart)
syslog(LOG_ERR, "%s", msg); vsyslog(LOG_ERR, msg, ap);
else else
warnx("%s", msg); vwarnx(msg, ap);
} else } else
syslog(LOG_ERR, "%s", msg); vsyslog(LOG_ERR, msg, ap);
va_end(ap);
} }
pid_t yp_pid; pid_t yp_pid;
@ -162,8 +181,8 @@ yp_svc_run(void)
static void static void
unregister(void) unregister(void)
{ {
(void) pmap_unset(YPPROG, YPVERS); (void)svc_unreg(YPPROG, YPVERS);
(void) pmap_unset(YPPROG, YPOLDVERS); (void)svc_unreg(YPPROG, YPOLDVERS);
} }
static void static void
@ -231,24 +250,222 @@ closedown(int sig)
(void) alarm(_RPCSVC_CLOSEDOWN/2); (void) alarm(_RPCSVC_CLOSEDOWN/2);
} }
static int
create_service(const int sock, const struct netconfig *nconf,
const struct __rpc_sockinfo *si)
{
int error;
SVCXPRT *transp;
struct addrinfo hints, *res, *res0;
struct socklistent *slep;
struct bindaddrlistent *blep;
struct netbuf svcaddr;
SLIST_INIT(&sle_head);
memset(&hints, 0, sizeof(hints));
memset(&svcaddr, 0, sizeof(svcaddr));
hints.ai_family = si->si_af;
hints.ai_socktype = si->si_socktype;
hints.ai_protocol = si->si_proto;
/*
* Build socketlist from bindaddrlist.
*/
if (sock == RPC_ANYFD) {
SLIST_FOREACH(blep, &ble_head, ble_next) {
if (blep->ble_hostname == NULL)
hints.ai_flags = AI_PASSIVE;
else
hints.ai_flags = 0;
error = getaddrinfo(blep->ble_hostname, servname,
&hints, &res0);
if (error) {
_msgout("getaddrinfo(): %s",
gai_strerror(error));
return -1;
}
for (res = res0; res; res = res->ai_next) {
int s;
s = __rpc_nconf2fd(nconf);
if (s < 0) {
if (errno == EPROTONOSUPPORT)
_msgout("unsupported"
" transport: %s",
nconf->nc_netid);
else
_msgout("cannot create"
" %s socket: %s",
nconf->nc_netid,
strerror(errno));
freeaddrinfo(res0);
return -1;
}
if (bind(s, res->ai_addr,
res->ai_addrlen) == -1) {
_msgout("cannot bind %s socket: %s",
nconf->nc_netid, strerror(errno));
freeaddrinfo(res0);
close(sock);
return -1;
}
if (nconf->nc_semantics != NC_TPI_CLTS)
listen(s, SOMAXCONN);
slep = malloc(sizeof(*slep));
if (slep == NULL) {
_msgout("malloc failed: %s",
strerror(errno));
freeaddrinfo(res0);
close(s);
return -1;
}
memset(slep, 0, sizeof(*slep));
memcpy(&slep->sle_ss,
(struct sockaddr *)(res->ai_addr),
sizeof(res->ai_addr));
slep->sle_sock = s;
SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
/*
* If servname == "0", redefine it by using
* the bound socket.
*/
if (strncmp("0", servname, 1) == 0) {
struct sockaddr *sap;
socklen_t slen;
char *sname;
sname = malloc(NI_MAXSERV);
if (sname == NULL) {
_msgout("malloc(): %s",
strerror(errno));
freeaddrinfo(res0);
close(s);
return -1;
}
memset(sname, 0, NI_MAXSERV);
sap = (struct sockaddr *)&slep->sle_ss;
slen = sizeof(*sap);
error = getsockname(s, sap, &slen);
if (error) {
_msgout("getsockname(): %s",
strerror(errno));
freeaddrinfo(res0);
close(s);
return -1;
}
error = getnameinfo(sap, slen,
NULL, 0,
sname, NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV);
if (error) {
_msgout("getnameinfo(): %s",
strerror(errno));
freeaddrinfo(res0);
close(s);
return -1;
}
servname = sname;
}
}
freeaddrinfo(res0);
}
} else {
slep = malloc(sizeof(*slep));
if (slep == NULL) {
_msgout("malloc failed: %s", strerror(errno));
return -1;
}
memset(slep, 0, sizeof(*slep));
slep->sle_sock = sock;
SLIST_INSERT_HEAD(&sle_head, slep, sle_next);
}
/*
* Traverse socketlist and create rpc service handles for each socket.
*/
SLIST_FOREACH(slep, &sle_head, sle_next) {
if (nconf->nc_semantics == NC_TPI_CLTS)
transp = svc_dg_create(slep->sle_sock, 0, 0);
else
transp = svc_vc_create(slep->sle_sock, RPC_MAXDATASIZE,
RPC_MAXDATASIZE);
if (transp == NULL) {
_msgout("unable to create service: %s",
nconf->nc_netid);
continue;
}
if (!svc_reg(transp, YPPROG, YPOLDVERS, ypprog_1, NULL)) {
svc_destroy(transp);
close(slep->sle_sock);
_msgout("unable to register (YPPROG, YPOLDVERS, %s):"
" %s", nconf->nc_netid, strerror(errno));
continue;
}
if (!svc_reg(transp, YPPROG, YPVERS, ypprog_2, NULL)) {
svc_destroy(transp);
close(slep->sle_sock);
_msgout("unable to register (YPPROG, YPVERS, %s): %s",
nconf->nc_netid, strerror(errno));
continue;
}
}
while(!(SLIST_EMPTY(&sle_head)))
SLIST_REMOVE_HEAD(&sle_head, sle_next);
/*
* Register RPC service to rpcbind by using AI_PASSIVE address.
*/
hints.ai_flags = AI_PASSIVE;
error = getaddrinfo(NULL, servname, &hints, &res0);
if (error) {
_msgout("getaddrinfo(): %s", gai_strerror(error));
return -1;
}
svcaddr.buf = res0->ai_addr;
svcaddr.len = res0->ai_addrlen;
if (si->si_af == AF_INET) {
/* XXX: ignore error intentionally */
rpcb_set(YPPROG, YPOLDVERS, nconf, &svcaddr);
}
/* XXX: ignore error intentionally */
rpcb_set(YPPROG, YPVERS, nconf, &svcaddr);
freeaddrinfo(res0);
return 0;
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
register SVCXPRT *transp = NULL;
int sock;
int proto = 0;
struct sockaddr_in saddr;
socklen_t asize = sizeof (saddr);
int ch; int ch;
in_port_t yp_port = 0; int error;
char *errstr;
struct socktype *st;
while ((ch = getopt(argc, argv, "hdnp:P:")) != -1) { void *nc_handle;
struct netconfig *nconf;
struct __rpc_sockinfo si;
struct bindaddrlistent *blep;
memset(&si, 0, sizeof(si));
SLIST_INIT(&ble_head);
while ((ch = getopt(argc, argv, "dh:np:P:")) != -1) {
switch (ch) { switch (ch) {
case 'd': case 'd':
debug = ypdb_debug = 1; debug = ypdb_debug = 1;
break; break;
case 'h':
blep = malloc(sizeof(*blep));
if (blep == NULL)
err(1, "malloc() failed: -h %s", optarg);
blep->ble_hostname = optarg;
SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
break;
case 'n': case 'n':
do_dns = 1; do_dns = 1;
break; break;
@ -256,121 +473,79 @@ main(int argc, char *argv[])
yp_dir = optarg; yp_dir = optarg;
break; break;
case 'P': case 'P':
yp_port = (in_port_t)strtonum(optarg, 1, 65535, servname = optarg;
(const char **)&errstr);
if (yp_port == 0 && errstr != NULL) {
_msgout("invalid port number provided");
exit(1);
}
break; break;
case 'h':
default: default:
usage(); usage();
} }
} }
/*
* Add "anyaddr" entry if no -h is specified.
*/
if (SLIST_EMPTY(&ble_head)) {
blep = malloc(sizeof(*blep));
if (blep == NULL)
err(1, "malloc() failed");
memset(blep, 0, sizeof(*blep));
SLIST_INSERT_HEAD(&ble_head, blep, ble_next);
}
load_securenets(); load_securenets();
yp_init_resolver(); yp_init_resolver();
#ifdef DB_CACHE #ifdef DB_CACHE
yp_init_dbs(); yp_init_dbs();
#endif #endif
if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) { nc_handle = setnetconfig();
int ssize = sizeof (int); if (nc_handle == NULL)
err(1, "cannot read %s", NETCONFIG);
if (saddr.sin_family != AF_INET) if (__rpc_fd2sockinfo(0, &si) != 0) {
exit(1); /* invoked from inetd */
if (getsockopt(0, SOL_SOCKET, SO_TYPE,
(char *)&_rpcfdtype, &ssize) == -1)
exit(1);
sock = 0;
_rpcpmstart = 1; _rpcpmstart = 1;
proto = 0; _rpcfdtype = si.si_socktype;
_rpcaf = si.si_af;
_rpcfd = 0;
openlog("ypserv", LOG_PID, LOG_DAEMON); openlog("ypserv", LOG_PID, LOG_DAEMON);
} else { } else {
/* standalone mode */
if (!debug) { if (!debug) {
if (daemon(0,0)) { if (daemon(0,0)) {
err(1,"cannot fork"); err(1,"cannot fork");
} }
openlog("ypserv", LOG_PID, LOG_DAEMON); openlog("ypserv", LOG_PID, LOG_DAEMON);
} }
sock = RPC_ANYSOCK; _rpcpmstart = 0;
(void) pmap_unset(YPPROG, YPVERS); _rpcaf = AF_INET;
(void) pmap_unset(YPPROG, YPOLDVERS); _rpcfd = RPC_ANYFD;
unregister();
} }
/* /*
* Initialize TCP/UDP sockets. * Create RPC service for each transport.
*/ */
memset((char *)&saddr, 0, sizeof(saddr)); while((nconf = getnetconfig(nc_handle))) {
saddr.sin_family = AF_INET; if ((nconf->nc_flag & NC_VISIBLE)) {
saddr.sin_addr.s_addr = htonl(INADDR_ANY); if (__rpc_nconf2sockinfo(nconf, &si) == 0) {
saddr.sin_port = htons(yp_port); _msgout("cannot get information for %s",
for (st = stlist; st->st_name != NULL; st++) { nconf->nc_netid);
/* Do not bind the socket if the user didn't specify a port */ exit(1);
if (yp_port == 0) }
break; if (_rpcpmstart) {
if (si.si_socktype != _rpcfdtype ||
sock = socket(AF_INET, st->st_type, 0); si.si_af != _rpcaf)
if (sock == -1) { continue;
if ((asprintf(&errstr, "cannot create a %s socket", } else if (si.si_af != _rpcaf)
st->st_name)) == -1) continue;
err(1, "unexpected failure in asprintf()"); error = create_service(_rpcfd, nconf, &si);
_msgout(errstr); if (error) {
free((void *)errstr); endnetconfig(nc_handle);
exit(1); exit(1);
} }
if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr))
== -1) {
if ((asprintf(&errstr, "cannot bind %s socket",
st->st_name)) == -1)
err(1, "unexpected failure in asprintf()");
_msgout(errstr);
free((void *)errstr);
exit(1);
}
errstr = NULL;
}
if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
transp = svcudp_create(sock);
if (transp == NULL) {
_msgout("cannot create udp service");
exit(1);
}
if (!_rpcpmstart)
proto = IPPROTO_UDP;
if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
_msgout("unable to register (YPPROG, YPOLDVERS, udp)");
exit(1);
}
if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
_msgout("unable to register (YPPROG, YPVERS, udp)");
exit(1);
} }
} }
endnetconfig(nc_handle);
while(!(SLIST_EMPTY(&ble_head)))
SLIST_REMOVE_HEAD(&ble_head, ble_next);
if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
transp = svctcp_create(sock, 0, 0);
if (transp == NULL) {
_msgout("cannot create tcp service");
exit(1);
}
if (!_rpcpmstart)
proto = IPPROTO_TCP;
if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
_msgout("unable to register (YPPROG, YPOLDVERS, tcp)");
exit(1);
}
if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
_msgout("unable to register (YPPROG, YPVERS, tcp)");
exit(1);
}
}
if (transp == (SVCXPRT *)NULL) {
_msgout("could not create a handle");
exit(1);
}
if (_rpcpmstart) { if (_rpcpmstart) {
(void) signal(SIGALRM, (SIG_PF) closedown); (void) signal(SIGALRM, (SIG_PF) closedown);
(void) alarm(_RPCSVC_CLOSEDOWN/2); (void) alarm(_RPCSVC_CLOSEDOWN/2);

View File

@ -30,7 +30,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd June 25, 2009 .Dd December 13, 2009
.Dt YPSERV 8 .Dt YPSERV 8
.Os .Os
.Sh NAME .Sh NAME
@ -408,6 +408,15 @@ in subprocesses, allowing the parent server process to go on handling
other requests.) other requests.)
This makes it easier to trace the server with This makes it easier to trace the server with
a debugging tool. a debugging tool.
.It Fl h Ar addr
Specify a specific address to bind to for requests. This option may be
specified multiple times. If no
.Fl h
option is specified,
.Nm
will bind to default passive address
.Pq e.g. INADDR_ANY for IPv4
for each transport.
.It Fl P Ar port .It Fl P Ar port
Force ypserv to bind to a specific TCP/UDP port, rather than selecting Force ypserv to bind to a specific TCP/UDP port, rather than selecting
its own. its own.