1 /*
2  * Copyright (c) 2009, Sun Microsystems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of Sun Microsystems, Inc. nor the names of its
13  *   contributors may be used to endorse or promote products derived
14  *   from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Copyright (c) 1986-1996,1998 by Sun Microsystems, Inc.
31  * All rights reserved.
32  */
33 
34 /* NFSv4.1 client for Windows
35  * Copyright � 2012 The Regents of the University of Michigan
36  *
37  * Olga Kornievskaia <aglo@umich.edu>
38  * Casey Bodley <cbodley@umich.edu>
39  *
40  * This library is free software; you can redistribute it and/or modify it
41  * under the terms of the GNU Lesser General Public License as published by
42  * the Free Software Foundation; either version 2.1 of the License, or (at
43  * your option) any later version.
44  *
45  * This library is distributed in the hope that it will be useful, but
46  * without any warranty; without even the implied warranty of merchantability
47  * or fitness for a particular purpose.  See the GNU Lesser General Public
48  * License for more details.
49  *
50  * You should have received a copy of the GNU Lesser General Public License
51  * along with this library; if not, write to the Free Software Foundation,
52  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
53  */
54 
55 #include <wintirpc.h>
56 //#include <pthread.h>
57 #include <reentrant.h>
58 #include <sys/types.h>
59 //#include <sys/fcntl.h>
60 #include <fcntl.h>
61 //#include <sys/socket.h>
62 //#include <netinet/in.h>
63 //#include <netinet/tcp.h>
64 #include <stdio.h>
65 #include <errno.h>
66 #include <string.h>
67 #include <stdlib.h>
68 //#include <netdb.h>
69 //#include <syslog.h>
70 #include <rpc/rpc.h>
71 #include <rpc/nettype.h>
72 //#include <unistd.h>
73 #include "rpc_com.h"
74 
75 extern bool_t __rpc_is_local_host(const char *);
76 #if 0	/* WINDOWS */
77 int __rpc_raise_fd(int);
78 #endif
79 
80 #ifndef NETIDLEN
81 #define	NETIDLEN 32
82 #endif
83 
84 /*
85  * Generic client creation with version checking the value of
86  * vers_out is set to the highest server supported value
87  * vers_low <= vers_out <= vers_high  AND an error results
88  * if this can not be done.
89  *
90  * It calls clnt_create_vers_timed() with a NULL value for the timeout
91  * pointer, which indicates that the default timeout should be used.
92  */
93 CLIENT *
clnt_create_vers(const char * hostname,const rpcprog_t prog,rpcvers_t * vers_out,const rpcvers_t vers_low,const rpcvers_t vers_high,const char * nettype)94 clnt_create_vers(const char *hostname, const rpcprog_t prog, rpcvers_t *vers_out,
95 	const rpcvers_t vers_low, const rpcvers_t vers_high, const char *nettype)
96 {
97 
98 	return (clnt_create_vers_timed(hostname, prog, vers_out, vers_low,
99 				vers_high, nettype, NULL));
100 }
101 
102 /*
103  * This the routine has the same definition as clnt_create_vers(),
104  * except it takes an additional timeout parameter - a pointer to
105  * a timeval structure.  A NULL value for the pointer indicates
106  * that the default timeout value should be used.
107  */
108 CLIENT *
clnt_create_vers_timed(const char * hostname,const rpcprog_t prog,rpcvers_t * vers_out,const rpcvers_t vers_low_in,const rpcvers_t vers_high_in,const char * nettype,const struct timeval * tp)109 clnt_create_vers_timed(const char *hostname, const rpcprog_t prog,
110     rpcvers_t *vers_out, const rpcvers_t vers_low_in, const rpcvers_t vers_high_in,
111     const char *nettype, const struct timeval *tp)
112 {
113 	CLIENT *clnt;
114 	struct timeval to;
115 	enum clnt_stat rpc_stat;
116 	struct rpc_err rpcerr;
117 	rpcvers_t vers_high = vers_high_in;
118 	rpcvers_t vers_low = vers_low_in;
119 
120 	clnt = clnt_create_timed(hostname, prog, vers_high, nettype, tp);
121 	if (clnt == NULL) {
122 		return (NULL);
123 	}
124 	to.tv_sec = 10;
125 	to.tv_usec = 0;
126 	rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
127 			(char *)NULL, (xdrproc_t)xdr_void, (char *)NULL, to);
128 	if (rpc_stat == RPC_SUCCESS) {
129 		*vers_out = vers_high;
130 		return (clnt);
131 	}
132 	while (rpc_stat == RPC_PROGVERSMISMATCH && vers_high > vers_low) {
133 		unsigned int minvers, maxvers;
134 
135 		clnt_geterr(clnt, &rpcerr);
136 		minvers = rpcerr.re_vers.low;
137 		maxvers = rpcerr.re_vers.high;
138 		if (maxvers < vers_high)
139 			vers_high = maxvers;
140 		else
141 			vers_high--;
142 		if (minvers > vers_low)
143 			vers_low = minvers;
144 		if (vers_low > vers_high) {
145 			goto error;
146 		}
147 		CLNT_CONTROL(clnt, CLSET_VERS, (char *)&vers_high);
148 		rpc_stat = clnt_call(clnt, NULLPROC, (xdrproc_t)xdr_void,
149 				(char *)NULL, (xdrproc_t)xdr_void,
150 				(char *)NULL, to);
151 		if (rpc_stat == RPC_SUCCESS) {
152 			*vers_out = vers_high;
153 			return (clnt);
154 		}
155 	}
156 	clnt_geterr(clnt, &rpcerr);
157 
158 error:
159 	rpc_createerr.cf_stat = rpc_stat;
160 	rpc_createerr.cf_error = rpcerr;
161 	clnt_destroy(clnt);
162 	return (NULL);
163 }
164 
165 /*
166  * Top level client creation routine.
167  * Generic client creation: takes (servers name, program-number, nettype) and
168  * returns client handle. Default options are set, which the user can
169  * change using the rpc equivalent of _ioctl()'s.
170  *
171  * It tries for all the netids in that particular class of netid until
172  * it succeeds.
173  * XXX The error message in the case of failure will be the one
174  * pertaining to the last create error.
175  *
176  * It calls clnt_create_timed() with the default timeout.
177  */
178 CLIENT *
clnt_create(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const char * nettype)179 clnt_create(const char *hostname, const rpcprog_t prog, const rpcvers_t vers,
180     const char *nettype)
181 {
182 
183 	return (clnt_create_timed(hostname, prog, vers, nettype, NULL));
184 }
185 
186 /*
187  * This the routine has the same definition as clnt_create(),
188  * except it takes an additional timeout parameter - a pointer to
189  * a timeval structure.  A NULL value for the pointer indicates
190  * that the default timeout value should be used.
191  *
192  * This function calls clnt_tp_create_timed().
193  */
194 CLIENT *
clnt_create_timed(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const char * netclass,const struct timeval * tp)195 clnt_create_timed(const char *hostname, const rpcprog_t prog, const rpcvers_t vers,
196     const char *netclass, const struct timeval *tp)
197 {
198 	struct netconfig *nconf;
199 	CLIENT *clnt = NULL;
200 	void *handle;
201 	enum clnt_stat	save_cf_stat = RPC_SUCCESS;
202 	struct rpc_err	save_cf_error;
203 	char nettype_array[NETIDLEN];
204 	char *nettype = &nettype_array[0];
205 
206 	if (netclass == NULL)
207 		nettype = NULL;
208 	else {
209 		size_t len = strlen(netclass);
210 		if (len >= sizeof (nettype_array)) {
211 			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
212 			return (NULL);
213 		}
214 		strcpy(nettype, netclass);
215 	}
216 
217 	if ((handle = __rpc_setconf((char *)nettype)) == NULL) {
218 		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
219 		return (NULL);
220 	}
221 	rpc_createerr.cf_stat = RPC_SUCCESS;
222 	while (clnt == NULL) {
223 		if ((nconf = __rpc_getconf(handle)) == NULL) {
224 			if (rpc_createerr.cf_stat == RPC_SUCCESS)
225 				rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
226 			break;
227 		}
228 #ifdef CLNT_DEBUG
229 		printf("trying netid %s\n", nconf->nc_netid);
230 #endif
231 		clnt = clnt_tp_create_timed(hostname, prog, vers, nconf, tp);
232 		if (clnt)
233 			break;
234 		else {
235 			/*
236 			 *	Since we didn't get a name-to-address
237 			 *	translation failure here, we remember
238 			 *	this particular error.  The object of
239 			 *	this is to enable us to return to the
240 			 *	caller a more-specific error than the
241 			 *	unhelpful ``Name to address translation
242 			 *	failed'' which might well occur if we
243 			 *	merely returned the last error (because
244 			 *	the local loopbacks are typically the
245 			 *	last ones in /etc/netconfig and the most
246 			 *	likely to be unable to translate a host
247 			 *	name).  We also check for a more
248 			 *	meaningful error than ``unknown host
249 			 *	name'' for the same reasons.
250 			 */
251 			if (rpc_createerr.cf_stat != RPC_N2AXLATEFAILURE &&
252 			    rpc_createerr.cf_stat != RPC_UNKNOWNHOST) {
253 				save_cf_stat = rpc_createerr.cf_stat;
254 				save_cf_error = rpc_createerr.cf_error;
255 			}
256 		}
257 	}
258 
259 	/*
260 	 *	Attempt to return an error more specific than ``Name to address
261 	 *	translation failed'' or ``unknown host name''
262 	 */
263 	if ((rpc_createerr.cf_stat == RPC_N2AXLATEFAILURE ||
264 	     rpc_createerr.cf_stat == RPC_UNKNOWNHOST) &&
265 	    (save_cf_stat != RPC_SUCCESS)) {
266 		rpc_createerr.cf_stat = save_cf_stat;
267 		rpc_createerr.cf_error = save_cf_error;
268 	}
269 	__rpc_endconf(handle);
270 	return (clnt);
271 }
272 
273 /*
274  * Generic client creation: takes (servers name, program-number, netconf) and
275  * returns client handle. Default options are set, which the user can
276  * change using the rpc equivalent of _ioctl()'s : clnt_control()
277  * It finds out the server address from rpcbind and calls clnt_tli_create().
278  *
279  * It calls clnt_tp_create_timed() with the default timeout.
280  */
281 CLIENT *
clnt_tp_create(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const struct netconfig * nconf)282 clnt_tp_create(const char *hostname, const rpcprog_t prog, const rpcvers_t vers,
283     const struct netconfig *nconf)
284 {
285 	return (clnt_tp_create_timed(hostname, prog, vers, nconf, NULL));
286 }
287 
288 /*
289  * This has the same definition as clnt_tp_create(), except it
290  * takes an additional parameter - a pointer to a timeval structure.
291  * A NULL value for the timeout pointer indicates that the default
292  * value for the timeout should be used.
293  */
294 CLIENT *
clnt_tp_create_timed(const char * hostname,const rpcprog_t prog,const rpcvers_t vers,const struct netconfig * nconf,const struct timeval * tp)295 clnt_tp_create_timed(const char *hostname, const rpcprog_t prog, const rpcvers_t vers,
296     const struct netconfig *nconf, const struct timeval *tp)
297 {
298 	struct netbuf *svcaddr;			/* servers address */
299 	CLIENT *cl = NULL;			/* client handle */
300 
301 	if (nconf == NULL) {
302 		rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
303 		return (NULL);
304 	}
305 
306 	/*
307 	 * Get the address of the server
308 	 */
309 	if ((svcaddr = __rpcb_findaddr_timed(prog, vers,
310 			(struct netconfig *)nconf, (char *)hostname,
311 			&cl, (struct timeval *)tp)) == NULL) {
312 		/* appropriate error number is set by rpcbind libraries */
313 		return (NULL);
314 	}
315 	if (cl == NULL) {
316 		cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
317 					prog, vers, 0, 0, NULL, NULL, NULL);
318 	} else {
319 		/* Reuse the CLIENT handle and change the appropriate fields */
320 		if (CLNT_CONTROL(cl, CLSET_SVC_ADDR, (void *)svcaddr) == TRUE) {
321 			if (cl->cl_netid == NULL)
322 				cl->cl_netid = strdup(nconf->nc_netid);
323 			if (cl->cl_tp == NULL)
324 				cl->cl_tp = strdup(nconf->nc_device);
325 			(void) CLNT_CONTROL(cl, CLSET_PROG, (void *)&prog);
326 			(void) CLNT_CONTROL(cl, CLSET_VERS, (void *)&vers);
327 		} else {
328 			CLNT_DESTROY(cl);
329 			cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr,
330 					prog, vers, 0, 0, NULL, NULL, NULL);
331 		}
332 	}
333 	free(svcaddr->buf);
334 	free(svcaddr);
335 	return (cl);
336 }
337 
338 /*
339  * Generic client creation:  returns client handle.
340  * Default options are set, which the user can
341  * change using the rpc equivalent of _ioctl()'s : clnt_control().
342  * If fd is RPC_ANYFD, it will be opened using nconf.
343  * It will be bound if not so.
344  * If sizes are 0; appropriate defaults will be chosen.
345  */
346 CLIENT *
clnt_tli_create(const SOCKET fd_in,const struct netconfig * nconf,struct netbuf * svcaddr,const rpcprog_t prog,const rpcvers_t vers,const uint sendsz,const uint recvsz,int (* callback_xdr)(void *,void *),int (* callback_function)(void *,void *,void **),void * callback_args)347 clnt_tli_create(const SOCKET fd_in, const struct netconfig *nconf,
348 	struct netbuf *svcaddr, const rpcprog_t prog, const rpcvers_t vers,
349 	const uint sendsz, const uint recvsz,
350     int (*callback_xdr)(void *, void *),
351     int (*callback_function)(void *, void *, void **),
352     void *callback_args)
353 {
354 	CLIENT *cl;			/* client handle */
355 	bool_t madefd = FALSE;		/* whether fd opened here */
356 	long servtype;
357 	BOOL one = TRUE;
358 	struct __rpc_sockinfo si;
359 	extern int __rpc_minfd;
360 	SOCKET fd = fd_in;
361 
362 	if (fd == RPC_ANYFD) {
363 		if (nconf == NULL) {
364 			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
365 			return (NULL);
366 		}
367 
368 		fd = __rpc_nconf2fd(nconf);
369 
370 		if (fd == INVALID_SOCKET)
371 			goto err;
372 #if 0
373 		if (fd < __rpc_minfd)
374 			fd = __rpc_raise_fd(fd);
375 #endif
376 		madefd = TRUE;
377 		servtype = nconf->nc_semantics;
378 		bindresvport(fd, NULL);
379 		if (!__rpc_fd2sockinfo(fd, &si))
380 			goto err;
381 	} else {
382 		if (!__rpc_fd2sockinfo(fd, &si))
383 			goto err;
384 		servtype = __rpc_socktype2seman(si.si_socktype);
385 		if (servtype == -1) {
386 			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
387 			return (NULL);
388 		}
389 	}
390 
391 	if (si.si_af != ((struct sockaddr *)svcaddr->buf)->sa_family) {
392 		rpc_createerr.cf_stat = RPC_UNKNOWNHOST;	/* XXX */
393 		goto err1;
394 	}
395 
396 	switch (servtype) {
397 	case NC_TPI_COTS:
398 		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz,
399             callback_xdr, callback_function, callback_args);
400 		break;
401 	case NC_TPI_COTS_ORD:
402 		if (nconf &&
403 		    ((strcmp(nconf->nc_protofmly, "inet") == 0) ||
404 		     (strcmp(nconf->nc_protofmly, "inet6") == 0))) {
405 			setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&one,
406 			    sizeof (one));
407 		}
408 		cl = clnt_vc_create(fd, svcaddr, prog, vers, sendsz, recvsz,
409             callback_xdr, callback_function, callback_args);
410 		break;
411 	case NC_TPI_CLTS:
412 		cl = clnt_dg_create(fd, svcaddr, prog, vers, sendsz, recvsz);
413 		break;
414 	default:
415 		goto err;
416 	}
417 
418 	if (cl == NULL)
419 		goto err1; /* borrow errors from clnt_dg/vc creates */
420 	if (nconf) {
421 		cl->cl_netid = strdup(nconf->nc_netid);
422 		cl->cl_tp = strdup(nconf->nc_device);
423 	} else {
424 		cl->cl_netid = "";
425 		cl->cl_tp = "";
426 	}
427 	if (madefd) {
428 		(void) CLNT_CONTROL(cl, CLSET_FD_CLOSE, NULL);
429 /*		(void) CLNT_CONTROL(cl, CLSET_POP_TIMOD, NULL);  */
430 	};
431 
432 	return (cl);
433 
434 err:
435 	rpc_createerr.cf_stat = RPC_SYSTEMERROR;
436 	rpc_createerr.cf_error.re_errno = errno;
437 err1:	if (madefd)
438 		(void)closesocket(fd);
439 	return (NULL);
440 }
441 
442 #if 0	/* WINDOWS */
443 /*
444  *  To avoid conflicts with the "magic" file descriptors (0, 1, and 2),
445  *  we try to not use them.  The __rpc_raise_fd() routine will dup
446  *  a descriptor to a higher value.  If we fail to do it, we continue
447  *  to use the old one (and hope for the best).
448  */
449 int __rpc_minfd = 3;
450 
451 int
452 __rpc_raise_fd(int fd)
453 {
454 	int nfd;
455 
456 	if (fd >= __rpc_minfd)
457 		return (fd);
458 
459 	if ((nfd = fcntl(fd, F_DUPFD, __rpc_minfd)) == -1)
460 		return (fd);
461 
462 	if (fsync(nfd) == -1) {
463 		closesocket(nfd);
464 		return (fd);
465 	}
466 
467 	if (closesocket(fd) == -1) {
468 		/* this is okay, we will syslog an error, then use the new fd */
469 		(void) syslog(LOG_ERR,
470 			"could not close() fd %d; mem & fd leak", fd);
471 	}
472 
473 	return (nfd);
474 }
475 #endif
476