1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <unistd.h>
30#include <netconfig.h>
31#include <netdir.h>
32#include <rpc/rpc.h>
33#include <sys/file.h>
34#include <sys/param.h>
35#include "ypxfrd.h"
36#include <ndbm.h>
37#include <rpcsvc/yp_prot.h>
38#include <rpcsvc/nis.h>
39#include <strings.h>
40
41#include <sys/isa_defs.h>	/* for ENDIAN defines */
42
43#if defined(_LITTLE_ENDIAN)
44#define	DOSWAB 1
45#endif
46
47static struct timeval TIMEOUT = {25, 0};
48static	DBM	*db;
49
50extern bool secure_map;
51extern void logprintf(char *, ...);
52
53/* delete the dbm file with name file */
54static int
55dbm_deletefile(file)
56char *file;
57{
58	char	pag1[MAXPATHLEN];
59	char	dir1[MAXPATHLEN];
60	int err;
61	strcpy(pag1, file);
62	strcat(pag1, ".pag");
63	strcpy(dir1, file);
64	strcat(dir1, ".dir");
65	err = 0;
66	if (unlink(pag1) < 0) {
67		perror("unlinkpag");
68		err = -1;
69	}
70
71	if (unlink(dir1) < 0) {
72		perror("unlinkdir");
73		return (-1);
74	}
75	return (err);
76}
77
78/* xdr just the .pag file of a dbm file */
79static	bool_t
80xdr_pages(xdrs)
81	XDR	*xdrs;
82{
83	static struct pag res;
84	struct pag	*PAG;
85#ifdef DOSWAB
86	short	*s;
87	int		i;
88#endif
89	bool_t	more;
90	bool_t	goteof;
91	off64_t	where;
92	int	true = 1;
93
94	goteof = FALSE;
95	if (!xdr_pag(xdrs, &res))
96		return (FALSE);
97	PAG = &res;
98	while (true) {
99		if (PAG->status == OK) {
100#ifdef DOSWAB
101		s = (short *)PAG->pag_u.ok.blkdat;
102		s[0] = ntohs(s[0]);
103		for (i = 1; i <= s[0]; i++)
104			s[i] = ntohs(s[i]);
105#endif
106			errno = 0;
107			where = (((off64_t)PAG->pag_u.ok.blkno) * PBLKSIZ);
108			(void) lseek64(db->dbm_pagf, where, L_SET);
109			if (errno != 0) {
110				perror("seek");
111				exit(-1);
112			}
113			if (write(db->dbm_pagf,
114				PAG->pag_u.ok.blkdat, PBLKSIZ) < 0) {
115				perror("write");
116				exit(-1);
117			}
118		} else if (PAG->status == GETDBM_ERROR) {
119			(void) printf("clnt call getpag GETDBM_ERROR\n");
120			exit(-1);
121		} else if (PAG->status == GETDBM_EOF)
122			goteof = TRUE;
123		if (!xdr_bool(xdrs, &more))
124			return (FALSE);
125		if (more == FALSE)
126			return (goteof);
127		if (!xdr_pag(xdrs, &res))
128			return (FALSE);
129	}
130	/*NOTREACHED*/
131	return (TRUE);
132}
133/* xdr  just the .dir part of a dbm file */
134static	bool_t
135xdr_dirs(xdrs)
136	XDR	*xdrs;
137{
138	static	struct dir res;
139	struct	dir	*DIR;
140	bool_t	more;
141	bool_t	goteof;
142	off64_t	where;
143	int	true = 1;
144
145	goteof = FALSE;
146	if (!xdr_dir(xdrs, &res))
147		return (FALSE);
148	DIR = &res;
149	while (true) {
150		if (DIR->status == OK) {
151			errno = 0;
152			where = (((off64_t)DIR->dir_u.ok.blkno) * DBLKSIZ);
153			(void) lseek64(db->dbm_dirf, where, L_SET);
154			if (errno != 0) {
155				perror("seek");
156				exit(-1);
157			}
158			if (write(db->dbm_dirf,
159				DIR->dir_u.ok.blkdat, DBLKSIZ) < 0) {
160				perror("write");
161				exit(-1);
162			}
163		} else if (DIR->status == GETDBM_ERROR) {
164			(void) printf("clnt call getdir GETDBM_ERROR\n");
165			exit(-1);
166		} else if (DIR->status == GETDBM_EOF)
167			goteof = TRUE;
168		if (!xdr_bool(xdrs, &more))
169			return (FALSE);
170		if (more == FALSE)
171			return (goteof);
172		if (!xdr_dir(xdrs, &res))
173			return (FALSE);
174	}
175	/*NOTREACHED*/
176	return (TRUE);
177}
178
179/*
180 * xdr a dbm file from ypxfrd
181 * note that if the client or server do not support ndbm
182 * we may not use this optional protocol
183 */
184
185int
186xdr_myfyl(xdrs, objp)
187	XDR *xdrs;
188	int *objp;
189{
190	if (!xdr_answer(xdrs, (answer *)objp))
191		return (FALSE);
192
193	if (*objp != OK)
194		return (TRUE);
195
196	if (!xdr_pages(xdrs))
197		return (FALSE);
198
199	if (!xdr_dirs(xdrs))
200		return (FALSE);
201
202	return (TRUE);
203}
204
205int
206ypxfrd_getdbm(tempmap, master, domain, map)
207	char *tempmap;
208	char *master;
209	char *domain;
210	char *map;
211{
212	hosereq	rmap;
213	CLIENT	*clnt;
214	int		res;
215	int	recvsiz = 24 * 1024;
216	struct netconfig *nconf;
217	int fd;
218	struct netbuf *svcaddr;
219	struct t_bind *tbind;
220	char *netid[] = { "tcp6", "tcp" };
221	int i, lastnetid = (sizeof (netid)/sizeof (netid[0])) - 1;
222
223	for (i = 0; i <= lastnetid; i++) {
224		if ((nconf = getnetconfigent(netid[i])) == NULL) {
225			if (i != lastnetid)
226				continue;
227			logprintf("ypxfr: tcp transport not supported\n");
228			return (-1);
229		}
230		if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) {
231			freenetconfigent(nconf);
232			if (i != lastnetid)
233				continue;
234			logprintf("ypxfr: TLI problems\n");
235			return (-1);
236		}
237		if (secure_map == TRUE) {
238			if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd,
239					NULL) == -1) {
240				(void) close(fd);
241				freenetconfigent(nconf);
242				if (i != lastnetid)
243					continue;
244				logprintf(
245			"ypxfr: cannot bind to reserved port for %s\n%s\n",
246					netid[i], netdir_sperror());
247				return (-1);
248			}
249		}
250
251		/* LINTED pointer alignment */
252		if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) ==
253			NULL) {
254			(void) close(fd);
255			freenetconfigent(nconf);
256			if (i != lastnetid)
257				continue;
258			logprintf("ypxfr: TLI problems\n");
259			return (-1);
260		}
261		svcaddr = &(tbind->addr);
262		if (rpcb_getaddr(YPXFRD, 1, nconf, svcaddr, master)
263			== FALSE) {
264			(void) t_free((char *)tbind, T_BIND);
265			(void) close(fd);
266			freenetconfigent(nconf);
267			if (i != lastnetid)
268				continue;
269			logprintf("ypxfr: couldnot get %s address\n", master);
270			return (-1);
271		}
272		if ((clnt = __nis_clnt_create(fd, nconf, 0, svcaddr, 0,
273						YPXFRD, 1, recvsiz, 0)) == 0) {
274			(void) t_free((char *)tbind, T_BIND);
275			(void) close(fd);
276			freenetconfigent(nconf);
277			if (i != lastnetid)
278				continue;
279			clnt_pcreateerror(
280				"ypxfr (get_map) - TCP channel create failure");
281			return (-1);
282		}
283		(void) t_free((char *)tbind, T_BIND);
284		break;
285	}
286	(void) CLNT_CONTROL(clnt, CLSET_FD_CLOSE, (char *)NULL);
287
288	rmap.map = map;
289	rmap.domain = domain;
290	(void) memset((char *)&res, 0, sizeof (res));
291	db = dbm_open(tempmap, O_RDWR + O_CREAT + O_TRUNC, 0777);
292	if (db == NULL) {
293		logprintf("dbm_open failed %s\n", tempmap);
294		perror(tempmap);
295		return (-2);
296	}
297
298	if (clnt_call(clnt, getdbm, xdr_hosereq, (char *)&rmap, xdr_myfyl,
299		(char *)&res, TIMEOUT) != RPC_SUCCESS) {
300		logprintf("clnt call to ypxfrd getdbm failed.\n");
301		clnt_perror(clnt, "getdbm");
302		(void) dbm_deletefile(tempmap);
303		return (-3);
304	}
305	if (res != OK) {
306		logprintf("clnt call %s ypxfrd getdbm NOTOK %s %s code=%d\n",
307			master, domain, map, res);
308		(void) dbm_deletefile(tempmap);
309		return (-4);
310	}
311	return (0);
312
313}
314