NetBSD/usr.sbin/ypserv/ypxfr/ypxfr.c

627 lines
13 KiB
C

/* $NetBSD: ypxfr.c,v 1.20 2011/04/25 22:51:26 wiz Exp $ */
/*
* Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: ypxfr.c,v 1.20 2011/04/25 22:51:26 wiz Exp $");
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <err.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <unistd.h>
#include <rpc/rpc.h>
#include <rpc/xdr.h>
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#include "yplib_host.h"
#include "ypdb.h"
#include "ypdef.h"
DBM *db;
static int yperr2yppush(int);
static int ypxfr_foreach(int, char *, int, char *, int, char *);
int main(int, char *[]);
int get_local_ordernum(char *, char *, u_int *);
int get_remote_ordernum(CLIENT *, char *, char *, u_int, u_int *);
void get_map(CLIENT *, char *, char *, struct ypall_callback *);
DBM *create_db(char *, char *, char *, size_t);
int install_db(char *, char *, char *);
int unlink_db(char *, char *, char *);
int add_order(DBM *, u_int);
int add_master(CLIENT *, char *, char *, DBM *);
int add_interdomain(CLIENT *, char *, char *, DBM *);
int add_secure(CLIENT *, char *, char *, DBM *);
int send_clear(CLIENT *);
int send_reply(CLIENT *, int, int);
int
main(int argc, char **argv)
{
int need_usage = 0, cflag = 0, fflag = 0, Cflag = 0;
int ch;
char *domain;
char *host = NULL;
char *srcdomain = NULL;
char *tid = NULL;
char *prog = NULL;
char *ipadd = NULL;
char *port = NULL;
char *map = NULL;
u_int ordernum, new_ordernum;
struct ypall_callback callback;
CLIENT *client;
char temp_map[MAXPATHLEN];
int status, xfr_status;
status = YPPUSH_SUCC;
client = NULL;
if (yp_get_default_domain(&domain))
errx(1, "can't get YP domain name");
while ((ch = getopt(argc, argv, "cd:fh:s:C:")) != -1) {
switch (ch) {
case 'c':
cflag = 1;
break;
case 'd':
domain = optarg;
break;
case 'f':
fflag = 1;
break;
case 'h':
host = optarg;
break;
case 's':
srcdomain = optarg;
break;
case 'C':
if (optind + 3 >= argc) {
need_usage = 1;
optind = argc;
break;
}
Cflag = 1;
tid = optarg;
prog = argv[optind++];
ipadd = argv[optind++];
port = argv[optind++];
break;
default:
need_usage = 1;
}
}
argc -= optind; argv += optind;
if (argc != 1)
need_usage = 1;
map = argv[0];
if (need_usage) {
status = YPPUSH_BADARGS;
fprintf(stderr, "usage: %s [-cf] [-C tid prog ipadd port] "
"[-d domain] [-h host] [-s domain] mapname\n",
getprogname());
exit(1);
}
#ifdef DEBUG
openlog("ypxfr", LOG_PID, LOG_DAEMON);
syslog(LOG_DEBUG, "ypxfr: Arguments:");
syslog(LOG_DEBUG, "YP clear to local: %s", (cflag) ? "no" : "yes");
syslog(LOG_DEBUG, " Force transfer: %s", (fflag) ? "yes" : "no");
syslog(LOG_DEBUG, " domain: %s", domain);
syslog(LOG_DEBUG, " host: %s", host);
syslog(LOG_DEBUG, " source domain: %s", srcdomain);
syslog(LOG_DEBUG, " transid: %s", tid);
syslog(LOG_DEBUG, " prog: %s", prog);
syslog(LOG_DEBUG, " port: %s", port);
syslog(LOG_DEBUG, " ipadd: %s", ipadd);
syslog(LOG_DEBUG, " map: %s", map);
#endif
if (fflag != 0)
ordernum = 0;
else {
status = get_local_ordernum(domain, map, &ordernum);
if (status < 0)
goto punt;
}
#ifdef DEBUG
syslog(LOG_DEBUG, "Get Master");
#endif
if (host == NULL) {
if (srcdomain == NULL)
status = yp_master(domain, map, &host);
else
status = yp_master(srcdomain, map, &host);
if (status == 0)
status = YPPUSH_SUCC;
else {
status = YPPUSH_MADDR;
goto punt;
}
}
#ifdef DEBUG
syslog(LOG_DEBUG, "Connect host: %s", host);
#endif
client = yp_bind_host(host, YPPROG, YPVERS, 0, 1);
status = get_remote_ordernum(client, domain, map, ordernum,
&new_ordernum);
if (status == YPPUSH_SUCC) {
/* Create temporary db */
db = create_db(domain, map, temp_map, sizeof(temp_map));
if (db == NULL)
status = YPPUSH_DBM;
/* Add ORDER */
if (status > 0)
status = add_order(db, new_ordernum);
/* Add MASTER */
if (status > 0)
status = add_master(client, domain, map, db);
/* Add INTERDOMAIN */
if (status > 0)
status = add_interdomain(client, domain, map, db);
/* Add SECURE */
if (status > 0)
status = add_secure(client, domain, map, db);
if (status > 0) {
callback.foreach = ypxfr_foreach;
get_map(client, domain, map, &callback);
}
/* Close db */
if (db != NULL)
ypdb_close(db);
/* Rename db */
if (status > 0)
status = install_db(domain, map, temp_map);
else
(void) unlink_db(domain, map, temp_map);
}
punt:
xfr_status = status;
if (client != NULL)
clnt_destroy(client);
/* YP_CLEAR */
if (!cflag) {
client = yp_bind_local(YPPROG, YPVERS);
status = send_clear(client);
clnt_destroy(client);
}
if (Cflag > 0) {
/* Send Response */
client = yp_bind_host(ipadd, atoi(prog), 1, atoi(port), 0);
status = send_reply(client, xfr_status, atoi(tid));
clnt_destroy(client);
}
exit (0);
}
/*
* yperr2yppush: convert error codes from functions like yp_order_host,
* yp_master_host, and yp_match_host into YPPUSH rpc status values.
*/
static int
yperr2yppush(int yperr) {
switch (yperr) {
case YPERR_DOMAIN:
return(YPPUSH_NODOM);
case YPERR_MAP:
return(YPPUSH_NOMAP);
case YPERR_KEY:
return(YPPUSH_YPERR);
case YPERR_BADDB:
return(YPPUSH_YPERR);
}
/*
* generic error status for the rest (BADARGS, RPC, YPERR, RESRC,
* NOMORE, PMAP, YPBIND, YPSERV, NODOM, VERS, ACCESS, BUSY)
*/
return(YPPUSH_XFRERR); /* generic error status */
}
static int
ypxfr_foreach(int status, char *keystr, int keylen, char *valstr,
int vallen, char *data)
{
datum key, val;
if (status == YP_NOMORE)
return (0);
keystr[keylen] = '\0';
valstr[vallen] = '\0';
key.dptr = keystr;
key.dsize = strlen(keystr);
val.dptr = valstr;
val.dsize = strlen(valstr);
/* XXX: suspect... ignoring return value here */
ypdb_store(db, key, val, YPDB_INSERT);
return (0);
}
int
get_local_ordernum(char *domain, char *map, u_int *lordernum)
{
char map_path[1024];
char order_key[] = YP_LAST_KEY;
char order[MAX_LAST_LEN+1];
struct stat finfo;
DBM *ldb;
datum k, v;
unsigned int status;
status = YPPUSH_SUCC;
snprintf(map_path, sizeof(map_path), "%s/%s", YP_DB_PATH, domain);
/* Make sure we serve the domain. */
if ((stat(map_path, &finfo)) != 0 ||
(S_ISDIR(finfo.st_mode) == 0)) {
warnx("domain `%s' not found locally", domain);
status = YPPUSH_NODOM;
goto out;
}
/* Make sure we serve the map. */
snprintf(map_path, sizeof(map_path), "%s/%s/%s%s",
YP_DB_PATH, domain, map, YPDB_SUFFIX);
if (stat(map_path, &finfo) != 0) {
status = YPPUSH_NOMAP;
goto out;
}
/* Open the map file. */
snprintf(map_path, sizeof(map_path), "%s/%s/%s",
YP_DB_PATH, domain, map);
ldb = ypdb_open(map_path);
if (ldb == NULL) {
status = YPPUSH_DBM;
goto out;
}
k.dptr = (char *)&order_key;
k.dsize = YP_LAST_LEN;
v = ypdb_fetch(ldb, k);
if (v.dptr == NULL)
*lordernum = 0;
else {
strncpy(order, v.dptr, v.dsize);
order[v.dsize] = '\0';
*lordernum = (u_int)atoi((char *)&order);
}
ypdb_close(ldb);
out:
if ((status == YPPUSH_NOMAP) || (status == YPPUSH_DBM)) {
*lordernum = 0;
status = YPPUSH_SUCC;
}
return (status);
}
int
get_remote_ordernum(CLIENT *client, char *domain, char *map,
u_int lordernum, u_int *rordernum)
{
int status;
status = yp_order_host(client, domain, map, (int *)rordernum);
if (status == 0) {
if (*rordernum <= lordernum)
status = YPPUSH_AGE;
else
status = YPPUSH_SUCC;
} else {
status = yperr2yppush(status);
}
return status;
}
void
get_map(CLIENT *client, char *domain, char *map,
struct ypall_callback *incallback)
{
(void)yp_all_host(client, domain, map, incallback);
}
DBM *
create_db(char *domain, char *map, char *db_temp, size_t db_temp_len)
{
static const char template[] = "ypdbXXXXXX";
DBM *ldb;
snprintf(db_temp, db_temp_len, "%s/%s/%s",
YP_DB_PATH, domain, template);
ldb = ypdb_mktemp(db_temp);
return ldb;
}
int
install_db(char *domain, char *map, char *db_temp)
{
char db_name[MAXPATHLEN];
snprintf(db_name, sizeof(db_name), "%s/%s/%s%s",
YP_DB_PATH, domain, map, YPDB_SUFFIX);
if (rename(db_temp, db_name)) {
warn("can't rename `%s' -> `%s'", db_temp, db_name);
return YPPUSH_YPERR;
}
return YPPUSH_SUCC;
}
int
unlink_db(char *domain, char *map, char *db_temp)
{
if (unlink(db_temp)) {
warn("can't unlink `%s'", db_temp);
return YPPUSH_YPERR;
}
return YPPUSH_SUCC;
}
int
add_order(DBM *ldb, u_int ordernum)
{
char datestr[11];
datum key, val;
char keystr[] = YP_LAST_KEY;
int status;
snprintf(datestr, sizeof(datestr), "%010d", ordernum);
key.dptr = keystr;
key.dsize = strlen(keystr);
val.dptr = datestr;
val.dsize = strlen(datestr);
status = ypdb_store(ldb, key, val, YPDB_INSERT);
if(status >= 0)
status = YPPUSH_SUCC;
else
status = YPPUSH_DBM;
return (status);
}
int
add_master(CLIENT *client, char *domain, char *map, DBM *ldb)
{
char keystr[] = YP_MASTER_KEY;
char *master;
int status;
datum key, val;
master = NULL;
/* Get MASTER */
status = yp_master_host(client, domain, map, &master);
if (master != NULL) {
key.dptr = keystr;
key.dsize = strlen(keystr);
val.dptr = master;
val.dsize = strlen(master);
status = ypdb_store(ldb, key, val, YPDB_INSERT);
if (status >= 0)
status = YPPUSH_SUCC;
else
status = YPPUSH_DBM;
} else {
status = yperr2yppush(status);
}
return status;
}
int
add_interdomain(CLIENT *client, char *domain, char *map, DBM *ldb)
{
char keystr[] = YP_INTERDOMAIN_KEY;
char *value;
int vallen;
int status;
datum k, v;
/* Get INTERDOMAIN */
k.dptr = keystr;
k.dsize = strlen(keystr);
status = yp_match_host(client, domain, map,
k.dptr, k.dsize, &value, &vallen);
if (status == YPERR_KEY) {
/* this is an optional key/val, so it may not be present */
status = YPPUSH_SUCC;
} else if (status == 0 && value) {
v.dptr = value;
v.dsize = vallen;
if (v.dptr != NULL) {
status = ypdb_store(ldb, k, v, YPDB_INSERT);
if (status >= 0)
status = YPPUSH_SUCC;
else
status = YPPUSH_DBM;
}
} else {
status = yperr2yppush(status);
}
return status;
}
int
add_secure(CLIENT *client, char *domain, char *map, DBM *ldb)
{
char keystr[] = YP_SECURE_KEY;
char *value;
int vallen;
int status;
datum k, v;
/* Get SECURE */
k.dptr = keystr;
k.dsize = strlen(keystr);
status = yp_match_host(client, domain, map,
k.dptr, k.dsize, &value, &vallen);
if (status == YPERR_KEY) {
/* this is an optional key/val, so it may not be present */
status = YPPUSH_SUCC;
} else if (status == 0 && value != 0) {
v.dptr = value;
v.dsize = vallen;
if (v.dptr != NULL) {
status = ypdb_store(ldb, k, v, YPDB_INSERT);
if (status >= 0)
status = YPPUSH_SUCC;
else
status = YPPUSH_DBM;
}
} else {
status = yperr2yppush(status);
}
return status;
}
int
send_clear(CLIENT *client)
{
struct timeval tv;
int r;
int status;
status = YPPUSH_SUCC;
tv.tv_sec = 10;
tv.tv_usec = 0;
/* Send CLEAR */
r = clnt_call(client, YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, tv);
if (r != RPC_SUCCESS) {
clnt_perror(client, "yp_clear: clnt_call");
status = YPPUSH_RPC;
}
return status;
}
int
send_reply(CLIENT *client, int status, int tid)
{
struct timeval tv;
struct ypresp_xfr resp;
int r;
tv.tv_sec = 10;
tv.tv_usec = 0;
resp.transid = tid;
resp.xfrstat = status;
/* Send XFRRESP */
r = clnt_call(client, YPPUSHPROC_XFRRESP, xdr_ypresp_xfr, &resp,
xdr_void, 0, tv);
if (r != RPC_SUCCESS) {
clnt_perror(client, "yppushresp_xdr: clnt_call");
status = YPPUSH_RPC;
}
return status;
}