diff options
Diffstat (limited to 'usr/src/cmd/ypcmd/ypxfr.c')
| -rw-r--r-- | usr/src/cmd/ypcmd/ypxfr.c | 1885 |
1 files changed, 1885 insertions, 0 deletions
diff --git a/usr/src/cmd/ypcmd/ypxfr.c b/usr/src/cmd/ypcmd/ypxfr.c new file mode 100644 index 0000000000..27ce80fa02 --- /dev/null +++ b/usr/src/cmd/ypcmd/ypxfr.c @@ -0,0 +1,1885 @@ +/* + * 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. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Portions of this source code were derived from Berkeley + * under license from the Regents of the University of + * California. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * This is a user command which gets a NIS data base from some running + * server, and gets it to the local site by using the normal NIS client + * enumeration functions. The map is copied to a temp name, then the real + * map is removed and the temp map is moved to the real name. ypxfr then + * sends a "YPPROC_CLEAR" message to the local server to insure that he will + * not hold a removed map open, so serving an obsolete version. + * + * ypxfr [ -h <host> ] [ -d <domainname> ] + * [ -s <domainname> ] [-f] [-c] [-C tid prot name] map + * + * If the host is ommitted, ypxfr will attempt to discover the master by + * using normal NIS services. If it can't get the record, it will use + * the address of the callback, if specified. If the host is specified + * as an internet address, no NIS services need to be locally available. + * + * If the domain is not specified, the default domain of the local machine + * is used. + * + * If the -f flag is used, the transfer will be done even if the master's + * copy is not newer than the local copy. + * + * The -c flag suppresses the YPPROC_CLEAR request to the local ypserv. It + * may be used if ypserv isn't currently running to suppress the error message. + * + * The -C flag is used to pass callback information to ypxfr when it is + * activated by ypserv. The callback information is used to send a + * yppushresp_xfr message with transaction id "tid" to a yppush process + * speaking a transient protocol number "prot". The yppush program is + * running on the host "name". + * + * The -s option is used to specify a source domain which may be + * different from the destination domain, for transfer of maps + * that are identical in different domains (e.g. services.byname) + * + */ + +#include <ndbm.h> +#undef NULL +#define DATUM + +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <ctype.h> +#include <netdb.h> +#include <netconfig.h> +#include <netdir.h> +#include <rpc/rpc.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <dirent.h> +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/yp_prot.h> +#include <unistd.h> +#include <stdlib.h> +#include <rpcsvc/nis.h> +#include "ypdefs.h" +#include "yp_b.h" +#include "shim.h" +#include "yptol.h" + +USE_YP_MASTER_NAME +USE_YP_SECURE +USE_YP_INTERDOMAIN +USE_YP_LAST_MODIFIED +USE_YPDBPATH +USE_DBM + +#define PARANOID 1 /* make sure maps have the right # entries */ + +#define CALLINTER_TRY 10 /* Seconds between callback tries */ +#define CALLTIMEOUT CALLINTER_TRY*6 /* Total timeout for callback */ + +DBM *db; + +/* ypxfr never uses N2L mode */ +bool_t yptol_mode = FALSE; + +int debug = FALSE; +int treepush = FALSE; +#define TREEPUSH 1 +int defwrite = TRUE; + +char *domain = NULL; +char *source = NULL; +char *map = NULL; +char *master = NULL; +char *pushhost = NULL; +/* + * The name of the xfer peer as specified as a + * -h option, -C name option or from querying the NIS + */ +struct dom_binding master_server; /* To talk to above */ +unsigned int master_prog_vers; /* YPVERS (barfs at YPOLDVERS !) */ +char *master_name = NULL; /* Map's master as contained in the map */ +unsigned *master_version = NULL; /* Order number as contained in the map */ +char *master_ascii_version; /* ASCII order number as contained in the map */ +bool fake_master_version = FALSE; +/* + * TRUE only if there's no order number in + * the map, and the user specified -f + */ +bool force = FALSE; /* TRUE iff user specified -f flag */ +bool logging = FALSE; /* TRUE iff no tty, but log file exists */ +bool check_count = FALSE; /* TRUE causes counts to be checked */ +bool send_clear = TRUE; /* FALSE iff user specified -c flag */ +bool callback = FALSE; +/* + * TRUE iff -C flag set. tid, proto and name + * will be set to point to the command line args. + */ +bool secure_map = FALSE; /* TRUE if there is yp_secure in the map */ +bool interdomain_map = FALSE; +/* + * TRUE if there is yp_interdomain in either + * the local or the master version of the map + */ +int interdomain_sz = 0; /* Size of the interdomain value */ +#define UDPINTER_TRY 10 /* Seconds between tries for udp */ +#define UDPTIMEOUT UDPINTER_TRY*4 /* Total timeout for udp */ +#define CALLINTER_TRY 10 /* Seconds between callback tries */ +#define CALLTIMEOUT CALLINTER_TRY*6 /* Total timeout for callback */ +struct timeval udp_timeout = { UDPTIMEOUT, 0}; +struct timeval tcp_timeout = { 180, 0}; /* Timeout for map enumeration */ + +char *interdomain_value; /* place to store the interdomain value */ +char *tid; +char *proto; +int entry_count; /* counts entries in the map */ +char logfile[] = "/var/yp/ypxfr.log"; +static char err_usage[] = +"Usage:\n\ +ypxfr [-f] [ -h host ] [ -d domainname ]\n\ + [ -s domainname ] [-c] [-C tid prot servname ] map\n\n\ +where\n\ + -f forces transfer even if the master's copy is not newer.\n\ + host is the server from where the map should be transfered\n\ + -d domainname is specified if other than the default domain\n\ + -s domainname is a source for the map that is same across domains\n\ + -c inhibits sending a \"Clear map\" message to the local ypserv.\n\ + -C is for use only by ypserv to pass callback information.\n"; +char err_bad_args[] = + "%s argument is bad.\n"; +char err_cant_get_kname[] = + "Can't get %s back from system call.\n"; +char err_null_kname[] = + "%s hasn't been set on this machine.\n"; +char err_bad_hostname[] = "hostname"; +char err_bad_mapname[] = "mapname"; +char err_bad_domainname[] = "domainname"; +char err_udp_failure[] = + "Can't set up a udp connection to ypserv on host %s.\n"; +char yptempname_prefix[] = "ypxfr_map."; +char ypbkupname_prefix[] = "ypxfr_bkup."; + +void get_command_line_args(); +bool bind_to_server(); +bool ping_server(); +bool get_private_recs(); +bool get_order(); +bool get_v1order(); +bool get_v2order(); +bool get_misc_recs(); +bool get_master_name(); +bool get_v1master_name(); +bool get_v2master_name(); +void find_map_master(); +bool move_map(); +unsigned get_local_version(); +void mkfilename(); +void mk_tmpname(); +bool get_map(); +bool add_private_entries(); +bool new_mapfiles(); +void del_mapfiles(); +void set_output(); +void logprintf(); +bool send_ypclear(); +void xfr_exit(); +void send_callback(); +int ypall_callback(); +int map_yperr_to_pusherr(); +extern CLIENT *__yp_clnt_create_rsvdport(); + +bool_t is_yptol_mode(); + +extern int errno; + + +/* + * This is the mainline for the ypxfr process. + */ + +void +main(argc, argv) + int argc; + char **argv; + +{ + + static char default_domain_name[YPMAXDOMAIN]; + static unsigned big = 0xffffffff; + int status; + + set_output(); + + /* + * Inter-process lock synchronization structure. Since slave servers + * get their maps from another NIS server rather than LDAP they can + * never run in N2L mode. We thus do not have to init the update + * locking mechanism. + */ + if (init_lock_map() == FALSE) { + exit(1); + } + + get_command_line_args(argc, argv); + + if (!domain) { + + if (!getdomainname(default_domain_name, YPMAXDOMAIN)) { + domain = default_domain_name; + } else { + logprintf(err_cant_get_kname, + err_bad_domainname); + xfr_exit(YPPUSH_RSRC); + } + + if (strlen(domain) == 0) { + logprintf(err_null_kname, + err_bad_domainname); + xfr_exit(YPPUSH_RSRC); + } + } + if (!source) + source = domain; + + if (!master) { + find_map_master(); + } + /* + * if we were unable to get the master name, either from + * the -h option or from -C "name" option or from NIS, + * we are doomed ! + */ + if (!master) { + xfr_exit(YPPUSH_MADDR); + } + + if (!bind_to_server(master, &master_server, + &master_prog_vers, &status)) { + xfr_exit(status); + } + + if (!get_private_recs(&status)) { + xfr_exit(status); + } + + if (!master_version) { + + if (force) { + master_version = &big; + fake_master_version = TRUE; + } else { + logprintf( +"Can't get order number for map %s from server at %s: use the -f flag.\n", + map, master); + xfr_exit(YPPUSH_FORCE); + } + } + + if (!move_map(&status)) { + xfr_exit(status); + } + + if (send_clear && !send_ypclear(&status)) { + xfr_exit(status); + } + + if (logging) { + logprintf("Transferred map %s from %s (%d entries).\n", + map, master, entry_count); + } + + xfr_exit(YPPUSH_SUCC); + /* NOTREACHED */ +} + +/* + * This decides whether we're being run interactively or not, and, if not, + * whether we're supposed to be logging or not. If we are logging, it sets + * up stderr to point to the log file, and sets the "logging" + * variable. If there's no logging, the output goes in the bit bucket. + * Logging output differs from interactive output in the presence of a + * timestamp, present only in the log file. stderr is reset, too, because it + * it's used by various library functions, including clnt_perror. + */ +void +set_output() +{ + if (!isatty(1)) { + if (access(logfile, W_OK)) { + (void) freopen("/dev/null", "w", stderr); + } else { + (void) freopen(logfile, "a", stderr); + logging = TRUE; + } + } +} +/* + * This constructs a logging record. + */ +void +logprintf(arg1, arg2, arg3, arg4, arg5, arg6, arg7) +/*VARARGS*/ +{ + struct timeval t; + + fseek(stderr, 0, 2); + if (logging) { + (void) gettimeofday(&t, NULL); + (void) fprintf(stderr, "%19.19s: ", ctime(&t.tv_sec)); + } + (void) fprintf(stderr, (char *)arg1, arg2, arg3, arg4, arg5, + arg6, arg7); + fflush(stderr); +} + +/* + * This does the command line argument processing. + */ +void +get_command_line_args(argc, argv) + int argc; + char **argv; + +{ + argv++; + + if (argc < 2) { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } + + while (--argc) { + + if ((*argv)[0] == '-') { + + switch ((*argv)[1]) { + + case 'f': { + force = TRUE; + argv++; + break; + } + + case 'D': { + debug = TRUE; + argv++; + break; + } + + case 'T': { + treepush = TRUE; + argv++; + break; + } + case 'P': { + check_count = TRUE; + argv++; + break; + } + case 'W': { + defwrite = FALSE; + argv++; + break; + } + case 'c': { + send_clear = FALSE; + argv++; + break; + } + + case 'h': { + + if (argc > 1) { + argv++; + argc--; + master = *argv; + argv++; + + if (strlen(master) > 256) { + logprintf( + err_bad_args, + err_bad_hostname); + xfr_exit(YPPUSH_BADARGS); + } + + } else { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } + + break; + } + + case 'd': + if (argc > 1) { + argv++; + argc--; + domain = *argv; + argv++; + + if (strlen(domain) > YPMAXDOMAIN) { + logprintf( + err_bad_args, + err_bad_domainname); + xfr_exit(YPPUSH_BADARGS); + } + + } else { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } + break; + + case 's': + if (argc > 1) { + argv++; + argc--; + source = *argv; + argv++; + + if (strlen(source) > YPMAXDOMAIN) { + logprintf( + err_bad_args, + err_bad_domainname); + xfr_exit(YPPUSH_BADARGS); + } + + } else { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } + break; + + case 'C': + if (argc > 3) { + callback = TRUE; + tid = *(++argv); + proto = *(++argv); + pushhost = *(++argv); + if (strlen(pushhost) > 256) { + logprintf(err_bad_args, err_bad_hostname); + + xfr_exit(YPPUSH_BADARGS); + } + argc -= 3; + argv++; + } else { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } + break; + + case 'b': { + interdomain_map = TRUE; + interdomain_value = ""; + interdomain_sz = 0; + argv++; + break; + } + + + default: { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } + + } + + } else { + + if (!map) { + map = *argv; + argv++; + + if (strlen(map) > YPMAXMAP) { + logprintf(err_bad_args, + err_bad_mapname); + xfr_exit(YPPUSH_BADARGS); + } + + } else { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } + } + } + + if (!map) { + logprintf(err_usage); + xfr_exit(YPPUSH_BADARGS); + } +} + +/* + * This tries to get the master name for the named map, from any + * server's version, using the vanilla NIS client interface. If we get a + * name back, the global "master" gets pointed to it. + */ +void +find_map_master() +{ + int err; + + if (err = __yp_master_rsvdport(source, map, &master)) { + logprintf("Can't get master of %s. Reason: %s.\n", map, + yperr_string(err)); + } + + yp_unbind(source); +} + +#ifdef TREEPUSH +chk_treepush(name) +char *name; +{ + char inmap[256]; + char inkey[256]; + int inkeylen; + char *outval; + int outvallen; + int err; + outval = NULL; + inkey[0] = 0; + strcpy(inmap, "ypslaves."); + strcat(inmap, name); + gethostname(inkey, 256); + inkeylen = strlen(inkey); + + err = yp_match(source, inmap, inkey, inkeylen, &outval, &outvallen); + yp_unbind(source); + return (err); +} +#endif + +/* + * This sets up a udp connection to speak the correct program and version + * to a NIS server. vers is set to YPVERS, doesn't give a damn about + * YPOLDVERS. + */ +bool +bind_to_server(host, pdomb, vers, status) + char *host; + struct dom_binding *pdomb; + unsigned int *vers; + int *status; +{ + if (ping_server(host, pdomb, YPVERS, status)) { + *vers = YPVERS; + return (TRUE); + } else + return (FALSE); +} + +/* + * This sets up a UDP channel to a server which is assumed to speak an input + * version of YPPROG. The channel is tested by pinging the server. In all + * error cases except "Program Version Number Mismatch", the error is + * reported, and in all error cases, the client handle is destroyed and the + * socket associated with the channel is closed. + */ +bool +ping_server(host, pdomb, vers, status) + char *host; + struct dom_binding *pdomb; + unsigned int vers; + int *status; +{ + enum clnt_stat rpc_stat; + + if ((pdomb->dom_client = __yp_clnt_create_rsvdport(host, YPPROG, vers, + 0, 0, 0)) != 0) { + + /* + * if we are on a c2 system, we should only accept data + * from a server which is on a reserved port. + */ + /* + * NUKE this for 5.0DR. + * + * if (issecure() && + * (pdomb->dom_server_addr.sin_family != AF_INET || + * pdomb->dom_server_addr.sin_port >= IPPORT_RESERVED)) { + * clnt_destroy(pdomb->dom_client); + * close(pdomb->dom_socket); + * (void) logprintf("bind_to_server: \ + * server is not using a privileged port\n"); + * *status = YPPUSH_YPERR; + * return (FALSE); + * } + */ + + rpc_stat = clnt_call(pdomb->dom_client, YPBINDPROC_NULL, + xdr_void, 0, xdr_void, 0, udp_timeout); + + if (rpc_stat == RPC_SUCCESS) { + return (TRUE); + } else { + clnt_destroy(pdomb->dom_client); + if (rpc_stat != RPC_PROGVERSMISMATCH) { + (void) clnt_perror(pdomb->dom_client, + "ypxfr: bind_to_server clnt_call error"); + } + + *status = YPPUSH_RPC; + return (FALSE); + } + } else { + logprintf("bind_to_server __clnt_create_rsvd error"); + (void) clnt_pcreateerror(""); + fflush(stderr); + *status = YPPUSH_RPC; + return (FALSE); + } +} + +/* + * This gets values for the YP_LAST_MODIFIED and YP_MASTER_NAME keys from the + * master server's version of the map. Values are held in static variables + * here. In the success cases, global pointer variables are set to point at + * the local statics. + */ +bool +get_private_recs(pushstat) + int *pushstat; +{ + static char anumber[20]; + static unsigned number; + static char name[YPMAXPEER + 1]; + int status; + + status = 0; + + if (get_order(anumber, &number, &status)) { + master_version = &number; + master_ascii_version = anumber; + if (debug) fprintf(stderr, + "ypxfr: Master Version is %s\n", master_ascii_version); + } else { + + if (status != 0) { + *pushstat = status; + if (debug) fprintf(stderr, + "ypxfr: Couldn't get map's master version number, \ + status was %d\n", status); + return (FALSE); + } + } + + if (get_master_name(name, &status)) { + master_name = name; + if (debug) fprintf(stderr, + "ypxfr: Maps master is '%s'\n", master_name); + } else { + + if (status != 0) { + *pushstat = status; + if (debug) fprintf(stderr, + "ypxfr: Couldn't get map's master name, status was %d\n", + status); + return (FALSE); + } + master_name = master; + } + + if (debug) + fprintf(stderr, + "ypxfr: Getting private records from master.\n"); + if (get_misc_recs(&status)) { + if (debug) + fprintf(stderr, + "ypxfr: Masters map %s secure and %s an interdomain map.\n", + (secure_map) ? "is" : "is not", + (interdomain_map) ? "is" : "is not"); + } else { + if (status != 0) { + *pushstat = status; + if (debug) + fprintf(stderr, + "ypxfr: Couldn't get state of secure and interdomain flags in map.\n"); + return (FALSE); + } + } + + return (TRUE); +} + +/* + * This gets the map's order number from the master server + */ +bool +get_order(an, n, pushstat) + char *an; + unsigned *n; + int *pushstat; +{ + if (master_prog_vers == YPVERS) { + return (get_v2order(an, n, pushstat)); + } else + return (FALSE); +} + +bool +get_v2order(an, n, pushstat) + char *an; + unsigned *n; + int *pushstat; +{ + struct ypreq_nokey req; + struct ypresp_order resp; + int retval; + + req.domain = source; + req.map = map; + + /* + * Get the map''s order number, null-terminate it and store it, + * and convert it to binary and store it again. + */ + retval = FALSE; + + if ((enum clnt_stat) clnt_call(master_server.dom_client, + YPPROC_ORDER, (xdrproc_t)xdr_ypreq_nokey, (char *)&req, + (xdrproc_t)xdr_ypresp_order, (char *)&resp, + udp_timeout) == RPC_SUCCESS) { + + if (resp.status == YP_TRUE) { + sprintf(an, "%d", resp.ordernum); + *n = resp.ordernum; + retval = TRUE; + } else if (resp.status != YP_BADDB) { + *pushstat = ypprot_err(resp.status); + + if (!logging) { + logprintf( + "(info) Can't get order number from ypserv at %s. Reason: %s.\n", + master, yperr_string( + ypprot_err(resp.status))); + } + } + + CLNT_FREERES(master_server.dom_client, + (xdrproc_t)xdr_ypresp_order, + (char *)&resp); + } else { + *pushstat = YPPUSH_RPC; + logprintf("ypxfr(get_v2order) RPC call to %s failed", master); + clnt_perror(master_server.dom_client, ""); + } + + return (retval); +} + +/* + * Pick up the state of the YP_SECURE and YP_INTERDOMAIN records from the + * master. Only works on 4.0 V2 masters that will match a YP_ private key + * when asked to explicitly. + */ +bool +get_misc_recs(pushstat) + int *pushstat; +{ + struct ypreq_key req; + struct ypresp_val resp; + int retval; + + req.domain = source; + req.map = map; + req.keydat.dptr = yp_secure; + req.keydat.dsize = yp_secure_sz; + + resp.valdat.dptr = NULL; + resp.valdat.dsize = 0; + + /* + * Get the value of the IS_SECURE key in the map. + */ + retval = FALSE; + + if (debug) + fprintf(stderr, "ypxfr: Checking masters secure key.\n"); + if ((enum clnt_stat) clnt_call(master_server.dom_client, + YPPROC_MATCH, (xdrproc_t)xdr_ypreq_key, (char *)&req, + (xdrproc_t)xdr_ypresp_val, (char *)&resp, + udp_timeout) == RPC_SUCCESS) { + if (resp.status == YP_TRUE) { + if (debug) + fprintf(stderr, "ypxfr: SECURE\n"); + secure_map = TRUE; + retval = TRUE; + } else if ((resp.status != YP_NOKEY) && + (resp.status != YP_VERS) && + (resp.status != YP_NOMORE)) { + *pushstat = ypprot_err(resp.status); + + if (!logging) { + logprintf( + "(info) Can't get secure flag from ypserv at %s. Reason: %s.\n", + master, yperr_string( + ypprot_err(resp.status))); + } + } + + CLNT_FREERES(master_server.dom_client, + (xdrproc_t)xdr_ypresp_val, + (char *)&resp); + } else { + *pushstat = YPPUSH_RPC; + logprintf("ypxfr(get_misc_recs) RPC call to %s failed", master); + clnt_perror(master_server.dom_client, ""); + } + + if (debug) + fprintf(stderr, "ypxfr: Checking masters INTERDOMAIN key.\n"); + req.keydat.dptr = yp_interdomain; + req.keydat.dsize = yp_interdomain_sz; + + resp.valdat.dptr = NULL; + resp.valdat.dsize = 0; + + /* + * Get the value of the INTERDOMAIN key in the map. + */ + + if ((enum clnt_stat) clnt_call(master_server.dom_client, + YPPROC_MATCH, (xdrproc_t)xdr_ypreq_key, (char *)&req, + (xdrproc_t)xdr_ypresp_val, (char *)&resp, + udp_timeout) == RPC_SUCCESS) { + if (resp.status == YP_TRUE) { + if (debug) + fprintf(stderr, "ypxfr: INTERDOMAIN\n"); + interdomain_map = TRUE; + interdomain_value = (char *)malloc(resp.valdat.dsize+1); + (void) memmove(interdomain_value, resp.valdat.dptr, + resp.valdat.dsize); + *(interdomain_value+resp.valdat.dsize) = '\0'; + interdomain_sz = resp.valdat.dsize; + retval = TRUE; + } else if ((resp.status != YP_NOKEY) && + (resp.status != YP_VERS) && + (resp.status != YP_NOMORE)) { + *pushstat = ypprot_err(resp.status); + + if (!logging) { + logprintf( + "(info) Can't get interdomain flag from ypserv at %s. Reason: %s.\n", + master, yperr_string( + ypprot_err(resp.status))); + } + } + + CLNT_FREERES(master_server.dom_client, + (xdrproc_t)xdr_ypresp_val, + (char *)&resp); + } else { + *pushstat = YPPUSH_RPC; + logprintf("ypxfr(get_misc_recs) RPC call to %s failed", master); + clnt_perror(master_server.dom_client, ""); + } + + + return (retval); +} + +/* + * This gets the map's master name from the master server + */ +bool +get_master_name(name, pushstat) + char *name; + int *pushstat; +{ + if (master_prog_vers == YPVERS) { + return (get_v2master_name(name, pushstat)); + } else + return (FALSE); +} + +bool +get_v2master_name(name, pushstat) + char *name; + int *pushstat; +{ + struct ypreq_nokey req; + struct ypresp_master resp; + int retval; + + req.domain = source; + req.map = map; + resp.master = NULL; + retval = FALSE; + + if ((enum clnt_stat) clnt_call(master_server.dom_client, + YPPROC_MASTER, (xdrproc_t)xdr_ypreq_nokey, (char *)&req, + (xdrproc_t)xdr_ypresp_master, (char *)&resp, + udp_timeout) == RPC_SUCCESS) { + + if (resp.status == YP_TRUE) { + strcpy(name, resp.master); + retval = TRUE; + } else if (resp.status != YP_BADDB) { + *pushstat = ypprot_err(resp.status); + + if (!logging) { + logprintf( +"(info) Can't get master name from ypserv at %s. Reason: %s.\n", + master, yperr_string( + ypprot_err(resp.status))); + } + } + + CLNT_FREERES(master_server.dom_client, + (xdrproc_t)xdr_ypresp_master, + (char *)&resp); + } else { + *pushstat = YPPUSH_RPC; + logprintf( + "ypxfr(get_v2master_name) RPC call to %s failed", master); + clnt_perror(master_server.dom_client, ""); + } + + return (retval); +} + +/* + * This does the work of transferring the map. + */ +bool +move_map(pushstat) + int *pushstat; +{ + unsigned local_version; + char map_name[MAXNAMLEN + 1]; + char tmp_name[MAXNAMLEN + 1]; + char bkup_name[MAXNAMLEN + 1]; + char an[11]; + unsigned n; + datum key; + datum val; + int hgstatus; + + mkfilename(map_name); + + if (!force) { + local_version = get_local_version(map_name); + if (debug) fprintf(stderr, + "ypxfr: Local version of map '%s' is %d\n", + map_name, local_version); + + if (local_version >= *master_version) { + logprintf( + "Map %s at %s is not more recent than local.\n", + map, master); + *pushstat = YPPUSH_AGE; + return (FALSE); + } + } + + mk_tmpname(yptempname_prefix, tmp_name); + + if (!new_mapfiles(tmp_name)) { + logprintf( + "Can't create temp map %s.\n", tmp_name); + *pushstat = YPPUSH_FILE; + return (FALSE); + } + + if ((hgstatus = ypxfrd_getdbm(tmp_name, master, source, map)) < 0) + { + logprintf( +"(info) %s %s %s ypxfrd getdbm failed (reason = %d) -- using ypxfr\n", + master, domain, map, hgstatus); + + db = dbm_open(tmp_name, O_RDWR + O_CREAT + O_TRUNC, 0644); + if (db == NULL) { + logprintf( + "Can't dbm init temp map %s.\n", tmp_name); + del_mapfiles(tmp_name); + *pushstat = YPPUSH_DBM; + return (FALSE); + } + if (defwrite) dbm_setdefwrite(db); + + if (!get_map(tmp_name, pushstat)) { + del_mapfiles(tmp_name); + return (FALSE); + } + + if (!add_private_entries(tmp_name)) { + del_mapfiles(tmp_name); + *pushstat = YPPUSH_DBM; + return (FALSE); + } + + /* + * Decide whether the map just transferred is a secure map. + * If we already know the local version was secure, we do not + * need to check this version. + */ + if (!secure_map) { + key.dptr = yp_secure; + key.dsize = yp_secure_sz; + val = dbm_fetch(db, key); + if (val.dptr != NULL) { + secure_map = TRUE; + } + } + + if (dbm_close_status(db) < 0) { + logprintf( + "Can't do dbm close operation on temp map %s.\n", + tmp_name); + del_mapfiles(tmp_name); + *pushstat = YPPUSH_DBM; + return (FALSE); + } + + if (!get_order(an, &n, pushstat)) { + return (FALSE); + } + if (n != *master_version) { + logprintf( + "Version skew at %s while transferring map %s.\n", + master, map); + del_mapfiles(tmp_name); + *pushstat = YPPUSH_SKEW; + return (FALSE); + } + + if (check_count) + if (!count_mismatch(tmp_name, entry_count)) { + del_mapfiles(tmp_name); + *pushstat = YPPUSH_DBM; + return (FALSE); + } + } else { + /* touch up the map */ + db = dbm_open(tmp_name, 2, 0644); + if (db == NULL) { + logprintf( + "Can't dbm init temp map %s.\n", tmp_name); + del_mapfiles(tmp_name); + *pushstat = YPPUSH_DBM; + return (FALSE); + } + + + if (!add_private_entries(tmp_name)) { + del_mapfiles(tmp_name); + *pushstat = YPPUSH_DBM; + return (FALSE); + } + + /* + * Decide whether the map just transferred is a secure map. + * If we already know the local version was secure, we do not + * need to check this version. + */ + if (!secure_map) { + key.dptr = yp_secure; + key.dsize = yp_secure_sz; + val = dbm_fetch(db, key); + if (val.dptr != NULL) { + secure_map = TRUE; + } + } + + if (dbm_close_status(db) < 0) { + logprintf( + "Can't do dbm close operation on temp map %s.\n", + tmp_name); + del_mapfiles(tmp_name); + *pushstat = YPPUSH_DBM; + return (FALSE); + } + + } + + if (lock_map(map_name) == 0) { + del_mapfiles(tmp_name); + logprintf("Lock error on %s\n", map_name); + *pushstat = YPPUSH_FILE; + return (FALSE); + } + if (!check_map_existence(map_name)) { + + if (!rename_map(tmp_name, map_name, secure_map)) { + del_mapfiles(tmp_name); + logprintf( + "Rename error: couldn't mv %s to %s.\n", + tmp_name, map_name); + *pushstat = YPPUSH_FILE; + unlock_map(map_name); + return (FALSE); + } + + } else { + mk_tmpname(ypbkupname_prefix, bkup_name); + + if (!rename_map(map_name, bkup_name, secure_map)) { + (void) rename_map(bkup_name, map_name, secure_map); + logprintf( + "Rename error: check that old %s is still intact.\n", + map_name); + del_mapfiles(tmp_name); + *pushstat = YPPUSH_FILE; + unlock_map(map_name); + return (FALSE); + } + + if (rename_map(tmp_name, map_name, secure_map)) { + del_mapfiles(bkup_name); + } else { + del_mapfiles(tmp_name); + (void) rename_map(bkup_name, map_name, secure_map); + logprintf( + "Rename error: check that old %s is still intact.\n", + map_name); + *pushstat = YPPUSH_FILE; + unlock_map(map_name); + return (FALSE); + } + } + if (unlock_map(map_name) == 0) + return (FALSE); + + return (TRUE); +} + +/* + * This tries to get the order number out of the local version of the map. + * If the attempt fails for any version, the function will return "0" + */ +unsigned +get_local_version(name) + char *name; +{ + datum key; + datum val; + char number[11]; + DBM *db; + + if (!check_map_existence(name)) { + return (0); + } + if (debug) fprintf(stderr, + "ypxfr: Map does exist, checking version now.\n"); + + if ((db = dbm_open(name, 0, 0)) == 0) { + return (0); + } + + key.dptr = yp_last_modified; + key.dsize = yp_last_modified_sz; + val = dbm_fetch(db, key); + if (!val.dptr) { /* Check to see if dptr is NULL */ + return (0); + } + if (val.dsize == 0 || val.dsize > 10) { + return (0); + } + /* Now save this value while we have it available */ + (void) memmove(number, val.dptr, val.dsize); + number[val.dsize] = '\0'; + + /* + * Now check to see if it is 'secure'. If we haven't already + * determined that it is secure in get_private_recs() then we check + * the local map here. + */ + if (!secure_map) { + key.dptr = yp_secure; + key.dsize = yp_secure_sz; + val = dbm_fetch(db, key); + secure_map = (val.dptr != NULL); + } + + /* + * Now check to see if interdomain requests are made of the local + * map. Keep the value around if they are. + */ + if (!interdomain_map) { + key.dptr = yp_interdomain; + key.dsize = yp_interdomain_sz; + val = dbm_fetch(db, key); + if (interdomain_map = (val.dptr != NULL)) { + interdomain_value = (char *)malloc(val.dsize+1); + (void) memmove(interdomain_value, val.dptr, val.dsize); + *(interdomain_value+val.dsize) = '\0'; + interdomain_sz = val.dsize; + } + } + + /* finish up */ + (void) dbm_close_status(db); + + return ((unsigned)atoi(number)); +} + +/* + * This constructs a file name for a map, minus its dbm_dir + * or dbm_pag extensions + */ +void +mkfilename(ppath) + char *ppath; +{ + bool_t yptol_mode; + int len; + + /* Work out if we are in yptol mode */ + yptol_mode = is_yptol_mode(); + + len = strlen(domain) + strlen(map) + strlen(ypdbpath) + 3; + if (yptol_mode) + len += strlen(NTOL_PREFIX); + + if (len > (MAXNAMLEN + 1)) { + logprintf("Map name string too long.\n"); + } + + (void) strcpy(ppath, ypdbpath); + (void) strcat(ppath, "/"); + (void) strcat(ppath, domain); + (void) strcat(ppath, "/"); + if (yptol_mode) + (void) strcat(ppath, NTOL_PREFIX); + (void) strcat(ppath, map); +} + +/* + * This returns a temporary name for a map transfer minus its dbm_dir or + * dbm_pag extensions. + */ +void +mk_tmpname(prefix, xfr_name) + char *prefix; + char *xfr_name; +{ + char xfr_anumber[10]; + long xfr_number; + + if (!xfr_name) { + return; + } + + xfr_number = getpid(); + (void) sprintf(xfr_anumber, "%d", xfr_number); + + (void) strcpy(xfr_name, ypdbpath); + (void) strcat(xfr_name, "/"); + (void) strcat(xfr_name, domain); + (void) strcat(xfr_name, "/"); + (void) strcat(xfr_name, prefix); + (void) strcat(xfr_name, map); + (void) strcat(xfr_name, "."); + (void) strcat(xfr_name, xfr_anumber); +} + +/* + * This deletes the .pag and .dir files which implement a map. + * + * Note: No error checking is done here for a garbage input file name or for + * failed unlink operations. + */ +void +del_mapfiles(basename) + char *basename; +{ + char dbfilename[MAXNAMLEN + 1]; + + if (!basename) { + return; + } + + strcpy(dbfilename, basename); + strcat(dbfilename, dbm_pag); + unlink(dbfilename); + strcpy(dbfilename, basename); + strcat(dbfilename, dbm_dir); + unlink(dbfilename); +} + +/* + * This creates <pname>.dir and <pname>.pag + */ +bool +new_mapfiles(pname) + char *pname; +{ + char dbfile[MAXNAMLEN + 1]; + int f; + int len; + + if (!pname || ((len = strlen(pname)) == 0) || + (len + 5) > (MAXNAMLEN + 1)) { + return (FALSE); + } + + errno = 0; + (void) strcpy(dbfile, pname); + (void) strcat(dbfile, dbm_dir); + + if ((f = open(dbfile, (O_WRONLY | O_CREAT | O_TRUNC), 0600)) >= 0) { + (void) close(f); + (void) strcpy(dbfile, pname); + (void) strcat(dbfile, dbm_pag); + + if ((f = open(dbfile, (O_WRONLY | O_CREAT | O_TRUNC), + 0600)) >= 0) { + (void) close(f); + return (TRUE); + } else { + return (FALSE); + } + + } else { + return (FALSE); + } +} + +count_callback(status) + int status; +{ + if (status != YP_TRUE) { + + if (status != YP_NOMORE) { + logprintf( + "Error from ypserv on %s (ypall_callback) = %s.\n", + master, yperr_string(ypprot_err(status))); + } + + return (TRUE); + } + + entry_count++; + return (FALSE); +} + +/* + * This counts the entries in the dbm file after the transfer to + * make sure that the dbm file was built correctly. + * Returns TRUE if everything is OK, FALSE if they mismatch. + */ +count_mismatch(pname, oldcount) + char *pname; + int oldcount; +{ + datum key; + DBM *db; +#ifdef REALLY_PARANOID + struct ypall_callback cbinfo; + struct ypreq_nokey allreq; + enum clnt_stat s; + struct dom_binding domb; + datum value; +#endif /* REALLY_PARANOID */ + + entry_count = 0; + db = dbm_open(pname, 0, 0); + if (db) { + for (key = dbm_firstkey(db); + key.dptr != NULL; key = dbm_nextkey(db)) + entry_count++; + dbm_close_status(db); + } + + if (oldcount != entry_count) { + logprintf( + "*** Count mismatch in dbm file %s: old=%d, new=%d ***\n", + map, oldcount, entry_count); + return (FALSE); + } + +#ifdef REALLY_PARANOID + + if ((domb.dom_client = __yp_clnt_create_rsvdport(master, YPPROG, + master_prog_vers, + "tcp6", 0, 0)) == 0 && + (domb.dom_client = __yp_clnt_create_rsvdport(master, YPPROG, + master_prog_vers, + "tcp", 0, 0)) == 0) { + clnt_pcreateerror("ypxfr (mismatch) - TCP channel " + "create failure"); + return (FALSE); + } + + if (master_prog_vers == YPVERS) { + int tmpstat; + + allreq.domain = source; + allreq.map = map; + cbinfo.foreach = count_callback; + tmpstat = 0; + cbinfo.data = (char *)&tmpstat; + + entry_count = 0; + s = clnt_call(domb.dom_client, YPPROC_ALL, xdr_ypreq_nokey, + &allreq, xdr_ypall, &cbinfo, tcp_timeout); + + if (tmpstat == 0) { + if (s == RPC_SUCCESS) { + } else { + clnt_perror(domb.dom_client, + "ypxfr (get_map/all) - RPC clnt_call (TCP) failure"); + return (FALSE); + } + + } else { + return (FALSE); + } + + } else { + logprintf("Wrong version number!\n"); + return (FALSE); + } + clnt_destroy(domb.dom_client); + close(domb.dom_socket); + entry_count += 2; /* add in YP_entries */ + if (oldcount != entry_count) { + logprintf( + "*** Count mismatch after enumerate %s: old=%d, new=%d ***\n", + map, oldcount, entry_count); + return (FALSE); + } +#endif /* REALLY_PARANOID */ + + return (TRUE); +} + +/* + * This sets up a TCP connection to the master server, and either gets + * ypall_callback to do all the work of writing it to the local dbm file + * (if the ypserv is current version), or does it itself for an old ypserv. + */ +bool +get_map(pname, pushstat) + char *pname; + int *pushstat; +{ + struct dom_binding domb; + enum clnt_stat s; + struct ypreq_nokey allreq; + struct ypall_callback cbinfo; + bool retval = FALSE; + int tmpstat; + int recvsiz = 24 * 1024; + struct netconfig *nconf; + int fd; + struct netbuf *svcaddr; + char *netid[] = { "tcp6", "tcp" }; + int i, lastnetid = (sizeof (netid)/sizeof (netid[0])) - 1; + + svcaddr = (struct netbuf *)calloc(1, sizeof (struct netbuf)); + if (! svcaddr) + return (FALSE); + svcaddr->maxlen = 32; + svcaddr->len = 32; + svcaddr->buf = (char *)malloc(32); + if (! svcaddr->buf) { + free(svcaddr); + return (FALSE); + } + + for (i = 0; i <= lastnetid; i++) { + fd = RPC_ANYFD; + if ((nconf = getnetconfigent(netid[i])) == NULL) { + if (i != lastnetid) + continue; + logprintf("ypxfr: tcp transport not supported\n"); + free(svcaddr->buf); + free(svcaddr); + return (FALSE); + } + if (rpcb_getaddr(YPPROG, master_prog_vers, nconf, svcaddr, + master) == FALSE) { + freenetconfigent(nconf); + if (i != lastnetid) + continue; + logprintf("ypxfr: could not get %s address\n", master); + free(svcaddr->buf); + free(svcaddr); + return (FALSE); + } + if ((domb.dom_client = __nis_clnt_create(fd, nconf, 0, svcaddr, + 0, YPPROG, master_prog_vers, recvsiz, 0)) == 0) { + freenetconfigent(nconf); + if (i != lastnetid) + continue; + clnt_pcreateerror( + "ypxfr (get_map) - TCP channel create failure"); + *pushstat = YPPUSH_RPC; + free(svcaddr->buf); + free(svcaddr); + return (FALSE); + } + break; + } + + entry_count = 0; + if (master_prog_vers == YPVERS) { + allreq.domain = source; + allreq.map = map; + cbinfo.foreach = ypall_callback; + tmpstat = 0; + cbinfo.data = (char *)&tmpstat; + + s = clnt_call(domb.dom_client, YPPROC_ALL, + (xdrproc_t)xdr_ypreq_nokey, + (char *)&allreq, (xdrproc_t)xdr_ypall, (char *)&cbinfo, + tcp_timeout); + + if (tmpstat == 0) { + + if (s == RPC_SUCCESS) { + retval = TRUE; + } else { + clnt_perror(domb.dom_client, + "ypxfr (get_map/all) - RPC clnt_call (TCP) failure"); + *pushstat = YPPUSH_RPC; + } + + } else { + *pushstat = tmpstat; + } + + } else + retval = FALSE; /* barf again at YPOLDVERS */ +cleanup: + clnt_destroy(domb.dom_client); + return (retval); +} + +/* + * This sticks each key-value pair into the current map. It returns FALSE as + * long as it wants to keep getting called back, and TRUE on error conditions + * and "No more k-v pairs". + */ +int +ypall_callback(status, key, kl, val, vl, pushstat) + int status; + char *key; + int kl; + char *val; + int vl; + int *pushstat; +{ + datum keydat; + datum valdat; + datum test; + + if (status != YP_TRUE) { + + if (status != YP_NOMORE) { + logprintf( + "Error from ypserv on %s (ypall_callback) = %s.\n", + master, yperr_string(ypprot_err(status))); + *pushstat = map_yperr_to_pusherr(status); + } + + return (TRUE); + } + + keydat.dptr = key; + keydat.dsize = kl; + valdat.dptr = val; + valdat.dsize = vl; + entry_count++; +/* way too many fetches */ + +#ifdef PARANOID + test = dbm_fetch(db, keydat); + if (test.dptr != NULL) { + logprintf("Duplicate key %s in map %s\n", key, map); + *pushstat = YPPUSH_DBM; + return (TRUE); + } +#endif /* PARANOID */ + if (dbm_store(db, keydat, valdat, 0) < 0) { + logprintf( + "Can't do dbm store into temp map %s.\n", map); + *pushstat = YPPUSH_DBM; + return (TRUE); + } +#ifdef PARANOID + test = dbm_fetch(db, keydat); + if (test.dptr == NULL) { + logprintf("Key %s was not inserted into dbm file %s\n", + key, map); + *pushstat = YPPUSH_DBM; + return (TRUE); + } +#endif /* PARANOID */ + + if (dbm_error(db)) { + logprintf("Key %s dbm_error raised in file %s\n", + key, map); + *pushstat = YPPUSH_DBM; + return (TRUE); + } + return (FALSE); +} + +/* + * This maps a YP_xxxx error code into a YPPUSH_xxxx error code + */ +int +map_yperr_to_pusherr(yperr) + int yperr; +{ + int reason; + + switch (yperr) { + + case YP_NOMORE: + reason = YPPUSH_SUCC; + break; + + case YP_NOMAP: + reason = YPPUSH_NOMAP; + break; + + case YP_NODOM: + reason = YPPUSH_NODOM; + break; + + case YP_NOKEY: + reason = YPPUSH_YPERR; + break; + + case YP_BADARGS: + reason = YPPUSH_BADARGS; + break; + + case YP_BADDB: + reason = YPPUSH_YPERR; + break; + + default: + reason = YPPUSH_XFRERR; + break; + } + + return (reason); +} + +/* + * This writes the last-modified and master entries into the new dbm file + */ +bool +add_private_entries(pname) + char *pname; +{ + datum key; + datum val; + + if (!fake_master_version) { + key.dptr = yp_last_modified; + key.dsize = yp_last_modified_sz; + val.dptr = master_ascii_version; + val.dsize = strlen(master_ascii_version); + + if (dbm_store(db, key, val, 1) < 0) { + logprintf( + "Can't do dbm store into temp map %s.\n", + pname); + return (FALSE); + } + entry_count++; + } + + if (master_name) { + key.dptr = yp_master_name; + key.dsize = yp_master_name_sz; + val.dptr = master_name; + val.dsize = strlen(master_name); + if (dbm_store(db, key, val, 1) < 0) { + logprintf( + "Can't do dbm store into temp map %s.\n", + pname); + return (FALSE); + } + entry_count++; + } + + if (interdomain_map) { + key.dptr = yp_interdomain; + key.dsize = yp_interdomain_sz; + val.dptr = interdomain_value; + val.dsize = interdomain_sz; + if (dbm_store(db, key, val, 1) < 0) { + logprintf( + "Can't do dbm store into temp map %s.\n", + pname); + return (FALSE); + } + entry_count++; + } + + if (secure_map) { + key.dptr = yp_secure; + key.dsize = yp_secure_sz; + val.dptr = yp_secure; + val.dsize = yp_secure_sz; + if (dbm_store(db, key, val, 1) < 0) { + logprintf( + "Can't do dbm store into temp map %s.\n", + pname); + return (FALSE); + } + entry_count++; + } + + return (TRUE); +} + + +/* + * This sends a YPPROC_CLEAR message to the local ypserv process. + */ +bool +send_ypclear(pushstat) + int *pushstat; +{ + struct dom_binding domb; + char local_host_name[256]; + unsigned int progvers; + int status; + + if (gethostname(local_host_name, 256)) { + logprintf("Can't get local machine name.\n"); + *pushstat = YPPUSH_RSRC; + return (FALSE); + } + + if (!bind_to_server(local_host_name, &domb, + &progvers, &status)) { + *pushstat = YPPUSH_CLEAR; + return (FALSE); + } + + if ((enum clnt_stat) clnt_call(domb.dom_client, + YPPROC_CLEAR, xdr_void, 0, xdr_void, 0, + udp_timeout) != RPC_SUCCESS) { + logprintf( + "Can't send ypclear message to ypserv on the local machine.\n"); + xfr_exit(YPPUSH_CLEAR); + } + + return (TRUE); +} + +/* + * This decides if send_callback has to get called, and does the process exit. + */ +void +xfr_exit(status) + int status; +{ + if (callback) { + send_callback(&status); + } + + if (status == YPPUSH_SUCC) { +#ifdef TREEPUSH + if (treepush) { + if (debug) + execlp("./yppush", "yppush", "-T", map, 0); + execlp("/usr/etc/yp/yppush", "yppush", "-T", map, 0); + perror("yppush"); + } +#endif + exit(0); + } else { + exit(1); + } +} + +/* + * This sets up a UDP connection to the yppush process which contacted our + * parent ypserv, and sends him a status on the requested transfer. + */ +void +send_callback(status) + int *status; +{ + struct yppushresp_xfr resp; + struct dom_binding domb; + + resp.transid = (unsigned long) atoi(tid); + resp.status = (unsigned long) *status; + + udp_timeout.tv_sec = CALLTIMEOUT; + + if ((domb.dom_client = __yp_clnt_create_rsvdport(pushhost, + (ulong_t)atoi(proto), + YPPUSHVERS, + 0, 0, 0)) == NULL) { + *status = YPPUSH_RPC; + return; + } + + if ((enum clnt_stat) clnt_call(domb.dom_client, + YPPUSHPROC_XFRRESP, (xdrproc_t)xdr_yppushresp_xfr, + (char *)&resp, xdr_void, 0, + udp_timeout) != RPC_SUCCESS) { + *status = YPPUSH_RPC; + return; + } +} + +/* + * FUNCTION: is_yptol_mode(); + * + * DESCRIPTION: Determines if we should run in N2L or traditional mode based + * on the presence of the N2L mapping file. + * + * This is a copy of a function from libnisdb. If more than this + * one function become required it may be worth linking the + * entire lib. + * + * INPUTS: Nothing + * + * OUTPUTS: TRUE = Run in N2L mode + * FALSE = Run in traditional mode. + */ +bool_t +is_yptol_mode() +{ + struct stat filestat; + + if (stat(NTOL_MAP_FILE, &filestat) != -1) + return (TRUE); + + return (FALSE); +} |
