rpcb_clnt.c revision 1.22 1 /* $NetBSD: rpcb_clnt.c,v 1.22 2008/04/25 17:44:44 christos Exp $ */
2
3 /*
4 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
5 * unrestricted use provided that this legend is included on all tape
6 * media and as a part of the software program in whole or part. Users
7 * may copy or modify Sun RPC without charge, but are not authorized
8 * to license or distribute it to anyone else except as part of a product or
9 * program developed by the user.
10 *
11 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
12 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
13 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14 *
15 * Sun RPC is provided with no support and without any obligation on the
16 * part of Sun Microsystems, Inc. to assist in its use, correction,
17 * modification or enhancement.
18 *
19 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
20 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
21 * OR ANY PART THEREOF.
22 *
23 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
24 * or profits or other special, indirect and consequential damages, even if
25 * Sun has been advised of the possibility of such damages.
26 *
27 * Sun Microsystems, Inc.
28 * 2550 Garcia Avenue
29 * Mountain View, California 94043
30 */
31 /*
32 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
33 */
34
35 /* #ident "@(#)rpcb_clnt.c 1.27 94/04/24 SMI" */
36
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 #if 0
40 static char sccsid[] = "@(#)rpcb_clnt.c 1.30 89/06/21 Copyr 1988 Sun Micro";
41 #else
42 __RCSID("$NetBSD: rpcb_clnt.c,v 1.22 2008/04/25 17:44:44 christos Exp $");
43 #endif
44 #endif
45
46 /*
47 * rpcb_clnt.c
48 * interface to rpcbind rpc service.
49 *
50 * Copyright (C) 1988, Sun Microsystems, Inc.
51 */
52
53 #include "namespace.h"
54 #include "reentrant.h"
55 #include <sys/types.h>
56 #include <sys/socket.h>
57 #include <sys/un.h>
58 #include <sys/utsname.h>
59 #include <rpc/rpc.h>
60 #include <rpc/rpcb_prot.h>
61 #include <rpc/nettype.h>
62 #include <netconfig.h>
63 #ifdef PORTMAP
64 #include <netinet/in.h> /* FOR IPPROTO_TCP/UDP definitions */
65 #include <rpc/pmap_prot.h>
66 #endif
67 #include <assert.h>
68 #include <errno.h>
69 #include <netdb.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <syslog.h>
74 #include <unistd.h>
75
76 #include "rpc_internal.h"
77
78 #ifdef __weak_alias
79 __weak_alias(rpcb_set,_rpcb_set)
80 __weak_alias(rpcb_unset,_rpcb_unset)
81 __weak_alias(rpcb_getmaps,_rpcb_getmaps)
82 __weak_alias(rpcb_rmtcall,_rpcb_rmtcall)
83 __weak_alias(rpcb_gettime,_rpcb_gettime)
84 __weak_alias(rpcb_taddr2uaddr,_rpcb_taddr2uaddr)
85 __weak_alias(rpcb_uaddr2taddr,_rpcb_uaddr2taddr)
86 #endif
87
88 static struct timeval tottimeout = { 60, 0 };
89 static const struct timeval rmttimeout = { 3, 0 };
90
91 static const char nullstring[] = "\000";
92
93 #define CACHESIZE 6
94
95 struct address_cache {
96 char *ac_host;
97 char *ac_netid;
98 char *ac_uaddr;
99 struct netbuf *ac_taddr;
100 struct address_cache *ac_next;
101 };
102
103 static struct address_cache *front;
104 static int cachesize;
105
106 #define CLCR_GET_RPCB_TIMEOUT 1
107 #define CLCR_SET_RPCB_TIMEOUT 2
108
109
110 extern int __rpc_lowvers;
111
112 static struct address_cache *check_cache __P((const char *, const char *));
113 static void delete_cache __P((struct netbuf *));
114 static void add_cache __P((const char *, const char *, struct netbuf *,
115 char *));
116 static CLIENT *getclnthandle __P((const char *, const struct netconfig *,
117 char **));
118 static CLIENT *local_rpcb __P((void));
119 static struct netbuf *got_entry __P((rpcb_entry_list_ptr,
120 const struct netconfig *));
121
122 /*
123 * This routine adjusts the timeout used for calls to the remote rpcbind.
124 * Also, this routine can be used to set the use of portmapper version 2
125 * only when doing rpc_broadcasts
126 * These are private routines that may not be provided in future releases.
127 */
128 bool_t
129 __rpc_control(request, info)
130 int request;
131 void *info;
132 {
133
134 _DIAGASSERT(info != NULL);
135
136 switch (request) {
137 case CLCR_GET_RPCB_TIMEOUT:
138 *(struct timeval *)info = tottimeout;
139 break;
140 case CLCR_SET_RPCB_TIMEOUT:
141 tottimeout = *(struct timeval *)info;
142 break;
143 case CLCR_SET_LOWVERS:
144 __rpc_lowvers = *(int *)info;
145 break;
146 case CLCR_GET_LOWVERS:
147 *(int *)info = __rpc_lowvers;
148 break;
149 default:
150 return (FALSE);
151 }
152 return (TRUE);
153 }
154
155 /*
156 * It might seem that a reader/writer lock would be more reasonable here.
157 * However because getclnthandle(), the only user of the cache functions,
158 * may do a delete_cache() operation if a check_cache() fails to return an
159 * address useful to clnt_tli_create(), we may as well use a mutex.
160 */
161 /*
162 * As it turns out, if the cache lock is *not* a reader/writer lock, we will
163 * block all clnt_create's if we are trying to connect to a host that's down,
164 * since the lock will be held all during that time.
165 */
166 #ifdef _REENTRANT
167 extern rwlock_t rpcbaddr_cache_lock;
168 #endif
169
170 /*
171 * The routines check_cache(), add_cache(), delete_cache() manage the
172 * cache of rpcbind addresses for (host, netid).
173 */
174
175 static struct address_cache *
176 check_cache(host, netid)
177 const char *host, *netid;
178 {
179 struct address_cache *cptr;
180
181 _DIAGASSERT(host != NULL);
182 _DIAGASSERT(netid != NULL);
183
184 /* READ LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
185
186 for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
187 if (!strcmp(cptr->ac_host, host) &&
188 !strcmp(cptr->ac_netid, netid)) {
189 #ifdef ND_DEBUG
190 fprintf(stderr, "Found cache entry for %s: %s\n",
191 host, netid);
192 #endif
193 return (cptr);
194 }
195 }
196 return NULL;
197 }
198
199 static void
200 delete_cache(addr)
201 struct netbuf *addr;
202 {
203 struct address_cache *cptr, *prevptr = NULL;
204
205 _DIAGASSERT(addr != NULL);
206
207 /* WRITE LOCK HELD ON ENTRY: rpcbaddr_cache_lock */
208 for (cptr = front; cptr != NULL; cptr = cptr->ac_next) {
209 if (!memcmp(cptr->ac_taddr->buf, addr->buf, addr->len)) {
210 free(cptr->ac_host);
211 free(cptr->ac_netid);
212 free(cptr->ac_taddr->buf);
213 free(cptr->ac_taddr);
214 if (cptr->ac_uaddr)
215 free(cptr->ac_uaddr);
216 if (prevptr)
217 prevptr->ac_next = cptr->ac_next;
218 else
219 front = cptr->ac_next;
220 free(cptr);
221 cachesize--;
222 break;
223 }
224 prevptr = cptr;
225 }
226 }
227
228 static void
229 add_cache(host, netid, taddr, uaddr)
230 const char *host, *netid;
231 char *uaddr;
232 struct netbuf *taddr;
233 {
234 struct address_cache *ad_cache, *cptr, *prevptr;
235
236 _DIAGASSERT(host != NULL);
237 _DIAGASSERT(netid != NULL);
238 /* uaddr may be NULL */
239 /* taddr may be NULL ??? */
240
241 ad_cache = malloc(sizeof(*ad_cache));
242 if (!ad_cache) {
243 return;
244 }
245 ad_cache->ac_host = strdup(host);
246 ad_cache->ac_netid = strdup(netid);
247 ad_cache->ac_uaddr = uaddr ? strdup(uaddr) : NULL;
248 ad_cache->ac_taddr = malloc(sizeof(*ad_cache->ac_taddr));
249 if (!ad_cache->ac_host || !ad_cache->ac_netid || !ad_cache->ac_taddr ||
250 (uaddr && !ad_cache->ac_uaddr)) {
251 goto out;
252 }
253 ad_cache->ac_taddr->len = ad_cache->ac_taddr->maxlen = taddr->len;
254 ad_cache->ac_taddr->buf = malloc(taddr->len);
255 if (ad_cache->ac_taddr->buf == NULL) {
256 out:
257 if (ad_cache->ac_host)
258 free(ad_cache->ac_host);
259 if (ad_cache->ac_netid)
260 free(ad_cache->ac_netid);
261 if (ad_cache->ac_uaddr)
262 free(ad_cache->ac_uaddr);
263 if (ad_cache->ac_taddr)
264 free(ad_cache->ac_taddr);
265 free(ad_cache);
266 return;
267 }
268 memcpy(ad_cache->ac_taddr->buf, taddr->buf, taddr->len);
269 #ifdef ND_DEBUG
270 fprintf(stderr, "Added to cache: %s : %s\n", host, netid);
271 #endif
272
273 /* VARIABLES PROTECTED BY rpcbaddr_cache_lock: cptr */
274
275 rwlock_wrlock(&rpcbaddr_cache_lock);
276 if (cachesize < CACHESIZE) {
277 ad_cache->ac_next = front;
278 front = ad_cache;
279 cachesize++;
280 } else {
281 /* Free the last entry */
282 cptr = front;
283 prevptr = NULL;
284 while (cptr->ac_next) {
285 prevptr = cptr;
286 cptr = cptr->ac_next;
287 }
288
289 #ifdef ND_DEBUG
290 fprintf(stderr, "Deleted from cache: %s : %s\n",
291 cptr->ac_host, cptr->ac_netid);
292 #endif
293 free(cptr->ac_host);
294 free(cptr->ac_netid);
295 free(cptr->ac_taddr->buf);
296 free(cptr->ac_taddr);
297 if (cptr->ac_uaddr)
298 free(cptr->ac_uaddr);
299
300 if (prevptr) {
301 prevptr->ac_next = NULL;
302 ad_cache->ac_next = front;
303 front = ad_cache;
304 } else {
305 front = ad_cache;
306 ad_cache->ac_next = NULL;
307 }
308 free(cptr);
309 }
310 rwlock_unlock(&rpcbaddr_cache_lock);
311 }
312
313 /*
314 * This routine will return a client handle that is connected to the
315 * rpcbind. Returns NULL on error and free's everything.
316 */
317 static CLIENT *
318 getclnthandle(host, nconf, targaddr)
319 const char *host;
320 const struct netconfig *nconf;
321 char **targaddr;
322 {
323 CLIENT *client;
324 struct netbuf *addr, taddr;
325 struct netbuf addr_to_delete;
326 struct __rpc_sockinfo si;
327 struct addrinfo hints, *res, *tres;
328 struct address_cache *ad_cache;
329 char *tmpaddr;
330
331 _DIAGASSERT(host != NULL);
332 _DIAGASSERT(nconf != NULL);
333 /* targaddr may be NULL */
334
335 /* VARIABLES PROTECTED BY rpcbaddr_cache_lock: ad_cache */
336
337 /* Get the address of the rpcbind. Check cache first */
338 client = NULL;
339 addr_to_delete.len = 0;
340 rwlock_rdlock(&rpcbaddr_cache_lock);
341 ad_cache = check_cache(host, nconf->nc_netid);
342 if (ad_cache != NULL) {
343 addr = ad_cache->ac_taddr;
344 client = clnt_tli_create(RPC_ANYFD, nconf, addr,
345 (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
346 if (client != NULL) {
347 if (targaddr)
348 *targaddr = ad_cache->ac_uaddr;
349 rwlock_unlock(&rpcbaddr_cache_lock);
350 return (client);
351 }
352 addr_to_delete.len = addr->len;
353 addr_to_delete.buf = malloc(addr->len);
354 if (addr_to_delete.buf == NULL) {
355 addr_to_delete.len = 0;
356 } else {
357 memcpy(addr_to_delete.buf, addr->buf, addr->len);
358 }
359 }
360 rwlock_unlock(&rpcbaddr_cache_lock);
361 if (addr_to_delete.len != 0) {
362 /*
363 * Assume this may be due to cache data being
364 * outdated
365 */
366 rwlock_wrlock(&rpcbaddr_cache_lock);
367 delete_cache(&addr_to_delete);
368 rwlock_unlock(&rpcbaddr_cache_lock);
369 free(addr_to_delete.buf);
370 }
371 if (!__rpc_nconf2sockinfo(nconf, &si)) {
372 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
373 return NULL;
374 }
375
376 memset(&hints, 0, sizeof hints);
377 hints.ai_family = si.si_af;
378 hints.ai_socktype = si.si_socktype;
379 hints.ai_protocol = si.si_proto;
380
381 #ifdef CLNT_DEBUG
382 printf("trying netid %s family %d proto %d socktype %d\n",
383 nconf->nc_netid, si.si_af, si.si_proto, si.si_socktype);
384 #endif
385
386 if (getaddrinfo(host, "sunrpc", &hints, &res) != 0) {
387 rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
388 return NULL;
389 }
390
391 for (tres = res; tres != NULL; tres = tres->ai_next) {
392 taddr.buf = tres->ai_addr;
393 taddr.len = taddr.maxlen = tres->ai_addrlen;
394
395 #ifdef ND_DEBUG
396 {
397 char *ua;
398
399 ua = taddr2uaddr(nconf, &taddr);
400 fprintf(stderr, "Got it [%s]\n", ua);
401 free(ua);
402 }
403 #endif
404
405 #ifdef ND_DEBUG
406 {
407 int i;
408
409 fprintf(stderr, "\tnetbuf len = %d, maxlen = %d\n",
410 taddr.len, taddr.maxlen);
411 fprintf(stderr, "\tAddress is ");
412 for (i = 0; i < taddr.len; i++)
413 fprintf(stderr, "%u.", ((char *)(taddr.buf))[i]);
414 fprintf(stderr, "\n");
415 }
416 #endif
417 client = clnt_tli_create(RPC_ANYFD, nconf, &taddr,
418 (rpcprog_t)RPCBPROG, (rpcvers_t)RPCBVERS4, 0, 0);
419 #ifdef ND_DEBUG
420 if (! client) {
421 clnt_pcreateerror("rpcbind clnt interface");
422 }
423 #endif
424
425 if (client) {
426 tmpaddr = targaddr ? taddr2uaddr(nconf, &taddr) : NULL;
427 add_cache(host, nconf->nc_netid, &taddr, tmpaddr);
428 if (targaddr)
429 *targaddr = tmpaddr;
430 break;
431 }
432 }
433 freeaddrinfo(res);
434 return (client);
435 }
436
437 /* XXX */
438 #define IN4_LOCALHOST_STRING "127.0.0.1"
439 #define IN6_LOCALHOST_STRING "::1"
440
441 /*
442 * This routine will return a client handle that is connected to the local
443 * rpcbind. Returns NULL on error and free's everything.
444 */
445 static CLIENT *
446 local_rpcb()
447 {
448 CLIENT *client;
449 static struct netconfig *loopnconf;
450 static const char *hostname;
451 #ifdef _REENTRANT
452 extern mutex_t loopnconf_lock;
453 #endif
454 int sock;
455 size_t tsize;
456 struct netbuf nbuf;
457 struct sockaddr_un sun;
458
459 /*
460 * Try connecting to the local rpcbind through a local socket
461 * first. If this doesn't work, try all transports defined in
462 * the netconfig file.
463 */
464 memset(&sun, 0, sizeof sun);
465 sock = socket(AF_LOCAL, SOCK_STREAM, 0);
466 if (sock < 0)
467 goto try_nconf;
468 sun.sun_family = AF_LOCAL;
469 strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
470 nbuf.len = sun.sun_len = SUN_LEN(&sun);
471 nbuf.maxlen = sizeof (struct sockaddr_un);
472 nbuf.buf = &sun;
473
474 tsize = __rpc_get_t_size(AF_LOCAL, 0, 0);
475 client = clnt_vc_create(sock, &nbuf, (rpcprog_t)RPCBPROG,
476 (rpcvers_t)RPCBVERS, tsize, tsize);
477
478 if (client != NULL) {
479 /* XXX - mark the socket to be closed in destructor */
480 (void) CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
481 return client;
482 }
483
484 /* XXX - nobody needs this socket anymore, free the descriptor */
485 close(sock);
486
487 try_nconf:
488
489 /* VARIABLES PROTECTED BY loopnconf_lock: loopnconf */
490 mutex_lock(&loopnconf_lock);
491 if (loopnconf == NULL) {
492 struct netconfig *nconf, *tmpnconf = NULL;
493 void *nc_handle;
494 int fd;
495
496 nc_handle = setnetconfig();
497 if (nc_handle == NULL) {
498 /* fails to open netconfig file */
499 syslog (LOG_ERR, "rpc: failed to open " NETCONFIG);
500 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
501 mutex_unlock(&loopnconf_lock);
502 return (NULL);
503 }
504 while ((nconf = getnetconfig(nc_handle)) != NULL) {
505 #ifdef INET6
506 if ((strcmp(nconf->nc_protofmly, NC_INET6) == 0 ||
507 #else
508 if ((
509 #endif
510 strcmp(nconf->nc_protofmly, NC_INET) == 0) &&
511 (nconf->nc_semantics == NC_TPI_COTS ||
512 nconf->nc_semantics == NC_TPI_COTS_ORD)) {
513 fd = __rpc_nconf2fd(nconf);
514 /*
515 * Can't create a socket, assume that
516 * this family isn't configured in the kernel.
517 */
518 if (fd < 0)
519 continue;
520 close(fd);
521 tmpnconf = nconf;
522 if (!strcmp(nconf->nc_protofmly, NC_INET))
523 hostname = IN4_LOCALHOST_STRING;
524 else
525 hostname = IN6_LOCALHOST_STRING;
526 }
527 }
528 if (tmpnconf == NULL) {
529 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
530 mutex_unlock(&loopnconf_lock);
531 return (NULL);
532 }
533 loopnconf = getnetconfigent(tmpnconf->nc_netid);
534 /* loopnconf is never freed */
535 endnetconfig(nc_handle);
536 }
537 mutex_unlock(&loopnconf_lock);
538 client = getclnthandle(hostname, loopnconf, NULL);
539 return (client);
540 }
541
542 /*
543 * Set a mapping between program, version and address.
544 * Calls the rpcbind service to do the mapping.
545 */
546 bool_t
547 rpcb_set(program, version, nconf, address)
548 rpcprog_t program;
549 rpcvers_t version;
550 const struct netconfig *nconf; /* Network structure of transport */
551 const struct netbuf *address; /* Services netconfig address */
552 {
553 CLIENT *client;
554 bool_t rslt = FALSE;
555 RPCB parms;
556 char uidbuf[32];
557
558 /* parameter checking */
559 if (nconf == NULL) {
560 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
561 return (FALSE);
562 }
563 if (address == NULL) {
564 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
565 return (FALSE);
566 }
567 client = local_rpcb();
568 if (! client) {
569 return (FALSE);
570 }
571
572 /* convert to universal */
573 parms.r_addr = taddr2uaddr(__UNCONST(nconf), __UNCONST(address));
574 if (!parms.r_addr) {
575 CLNT_DESTROY(client);
576 rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
577 return (FALSE); /* no universal address */
578 }
579 parms.r_prog = program;
580 parms.r_vers = version;
581 parms.r_netid = nconf->nc_netid;
582 /*
583 * Though uid is not being used directly, we still send it for
584 * completeness. For non-unix platforms, perhaps some other
585 * string or an empty string can be sent.
586 */
587 (void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
588 parms.r_owner = uidbuf;
589
590 CLNT_CALL(client, (rpcproc_t)RPCBPROC_SET, (xdrproc_t) xdr_rpcb,
591 (char *)(void *)&parms, (xdrproc_t) xdr_bool,
592 (char *)(void *)&rslt, tottimeout);
593
594 CLNT_DESTROY(client);
595 free(parms.r_addr);
596 return (rslt);
597 }
598
599 /*
600 * Remove the mapping between program, version and netbuf address.
601 * Calls the rpcbind service to do the un-mapping.
602 * If netbuf is NULL, unset for all the transports, otherwise unset
603 * only for the given transport.
604 */
605 bool_t
606 rpcb_unset(program, version, nconf)
607 rpcprog_t program;
608 rpcvers_t version;
609 const struct netconfig *nconf;
610 {
611 CLIENT *client;
612 bool_t rslt = FALSE;
613 RPCB parms;
614 char uidbuf[32];
615
616 client = local_rpcb();
617 if (! client) {
618 return (FALSE);
619 }
620
621 parms.r_prog = program;
622 parms.r_vers = version;
623 if (nconf)
624 parms.r_netid = nconf->nc_netid;
625 else {
626 parms.r_netid = __UNCONST(&nullstring[0]); /* unsets all */
627 }
628 parms.r_addr = __UNCONST(&nullstring[0]);
629 (void) snprintf(uidbuf, sizeof uidbuf, "%d", geteuid());
630 parms.r_owner = uidbuf;
631
632 CLNT_CALL(client, (rpcproc_t)RPCBPROC_UNSET, (xdrproc_t) xdr_rpcb,
633 (char *)(void *)&parms, (xdrproc_t) xdr_bool,
634 (char *)(void *)&rslt, tottimeout);
635
636 CLNT_DESTROY(client);
637 return (rslt);
638 }
639
640 /*
641 * From the merged list, find the appropriate entry
642 */
643 static struct netbuf *
644 got_entry(relp, nconf)
645 rpcb_entry_list_ptr relp;
646 const struct netconfig *nconf;
647 {
648 struct netbuf *na = NULL;
649 rpcb_entry_list_ptr sp;
650 rpcb_entry *rmap;
651
652 _DIAGASSERT(nconf != NULL);
653
654 for (sp = relp; sp != NULL; sp = sp->rpcb_entry_next) {
655 rmap = &sp->rpcb_entry_map;
656 if ((strcmp(nconf->nc_proto, rmap->r_nc_proto) == 0) &&
657 (strcmp(nconf->nc_protofmly, rmap->r_nc_protofmly) == 0) &&
658 (nconf->nc_semantics == rmap->r_nc_semantics) &&
659 (rmap->r_maddr != NULL) && (rmap->r_maddr[0] != 0)) {
660 na = uaddr2taddr(nconf, rmap->r_maddr);
661 #ifdef ND_DEBUG
662 fprintf(stderr, "\tRemote address is [%s].\n",
663 rmap->r_maddr);
664 if (!na)
665 fprintf(stderr,
666 "\tCouldn't resolve remote address!\n");
667 #endif
668 break;
669 }
670 }
671 return (na);
672 }
673
674 /*
675 * An internal function which optimizes rpcb_getaddr function. It also
676 * returns the client handle that it uses to contact the remote rpcbind.
677 *
678 * The algorithm used: If the transports is TCP or UDP, it first tries
679 * version 2 (portmap), 4 and then 3 (svr4). This order should be
680 * changed in the next OS release to 4, 2 and 3. We are assuming that by
681 * that time, version 4 would be available on many machines on the network.
682 * With this algorithm, we get performance as well as a plan for
683 * obsoleting version 2.
684 *
685 * For all other transports, the algorithm remains as 4 and then 3.
686 *
687 * XXX: Due to some problems with t_connect(), we do not reuse the same client
688 * handle for COTS cases and hence in these cases we do not return the
689 * client handle. This code will change if t_connect() ever
690 * starts working properly. Also look under clnt_vc.c.
691 */
692 struct netbuf *
693 __rpcb_findaddr(program, version, nconf, host, clpp)
694 rpcprog_t program;
695 rpcvers_t version;
696 const struct netconfig *nconf;
697 const char *host;
698 CLIENT **clpp;
699 {
700 CLIENT *client = NULL;
701 RPCB parms;
702 enum clnt_stat clnt_st;
703 char *ua = NULL;
704 rpcvers_t vers;
705 struct netbuf *address = NULL;
706 rpcvers_t start_vers = RPCBVERS4;
707 struct netbuf servaddr;
708
709 /* nconf is handled below */
710 _DIAGASSERT(host != NULL);
711 /* clpp may be NULL */
712
713 /* parameter checking */
714 if (nconf == NULL) {
715 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
716 return (NULL);
717 }
718
719 parms.r_addr = NULL;
720
721 #ifdef PORTMAP
722 /* Try version 2 for TCP or UDP */
723 if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
724 u_short port = 0;
725 struct netbuf remote;
726 rpcvers_t pmapvers = 2;
727 struct pmap pmapparms;
728
729 /*
730 * Try UDP only - there are some portmappers out
731 * there that use UDP only.
732 */
733 if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
734 struct netconfig *newnconf;
735
736 if ((newnconf = getnetconfigent("udp")) == NULL) {
737 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
738 return (NULL);
739 }
740 client = getclnthandle(host, newnconf, &parms.r_addr);
741 freenetconfigent(newnconf);
742 } else {
743 client = getclnthandle(host, nconf, &parms.r_addr);
744 }
745 if (client == NULL) {
746 return (NULL);
747 }
748
749 /* Set the version */
750 CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&pmapvers);
751 pmapparms.pm_prog = program;
752 pmapparms.pm_vers = version;
753 pmapparms.pm_prot = strcmp(nconf->nc_proto, NC_TCP) ?
754 IPPROTO_UDP : IPPROTO_TCP;
755 pmapparms.pm_port = 0; /* not needed */
756 clnt_st = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
757 (xdrproc_t) xdr_pmap, (caddr_t)(void *)&pmapparms,
758 (xdrproc_t) xdr_u_short, (caddr_t)(void *)&port,
759 tottimeout);
760 if (clnt_st != RPC_SUCCESS) {
761 if ((clnt_st == RPC_PROGVERSMISMATCH) ||
762 (clnt_st == RPC_PROGUNAVAIL))
763 goto try_rpcbind; /* Try different versions */
764 rpc_createerr.cf_stat = RPC_PMAPFAILURE;
765 clnt_geterr(client, &rpc_createerr.cf_error);
766 goto error;
767 } else if (port == 0) {
768 address = NULL;
769 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
770 goto error;
771 }
772 port = htons(port);
773 CLNT_CONTROL(client, CLGET_SVC_ADDR, (char *)(void *)&remote);
774 if (((address = malloc(sizeof(*addr))) == NULL) ||
775 ((address->buf = malloc(remote.len)) == NULL)) {
776 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
777 clnt_geterr(client, &rpc_createerr.cf_error);
778 if (address) {
779 free(address);
780 address = NULL;
781 }
782 goto error;
783 }
784 memcpy(address->buf, remote.buf, remote.len);
785 memcpy(&((char *)address->buf)[sizeof (short)],
786 (char *)(void *)&port, sizeof (short));
787 address->len = address->maxlen = remote.len;
788 goto done;
789 }
790 #endif
791
792 try_rpcbind:
793 /*
794 * Now we try version 4 and then 3.
795 * We also send the remote system the address we used to
796 * contact it in case it can help to connect back with us
797 */
798 parms.r_prog = program;
799 parms.r_vers = version;
800 parms.r_owner = __UNCONST(&nullstring[0]); /* not needed; */
801 /* just for xdring */
802 parms.r_netid = nconf->nc_netid; /* not really needed */
803
804 /*
805 * If a COTS transport is being used, try getting address via CLTS
806 * transport. This works only with version 4.
807 * NOTE: This is being done for all transports EXCEPT LOOPBACK
808 * because with loopback the cost to go to a COTS is same as
809 * the cost to go through CLTS, plus you get the advantage of
810 * finding out immediately if the local rpcbind process is dead.
811 */
812 #if 1
813 if ((nconf->nc_semantics == NC_TPI_COTS_ORD ||
814 nconf->nc_semantics == NC_TPI_COTS) &&
815 (strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0))
816 #else
817 if (client != NULL) {
818 CLNT_DESTROY(client);
819 client = NULL;
820 }
821 if (nconf->nc_semantics == NC_TPI_CLTS)
822 #endif
823 {
824 void *handle;
825 struct netconfig *nconf_clts;
826 rpcb_entry_list_ptr relp = NULL;
827
828 if (client == NULL) {
829 /* This did not go through the above PORTMAP/TCP code */
830 #if 1
831 if ((handle = __rpc_setconf("datagram_v")) != NULL)
832 #else
833 if ((handle = __rpc_setconf("circuit_v")) != NULL)
834 #endif
835 {
836 while ((nconf_clts = __rpc_getconf(handle))
837 != NULL) {
838 if (strcmp(nconf_clts->nc_protofmly,
839 nconf->nc_protofmly) != 0) {
840 continue;
841 }
842 client = getclnthandle(host, nconf_clts,
843 &parms.r_addr);
844 break;
845 }
846 __rpc_endconf(handle);
847 }
848 if (client == NULL)
849 goto regular_rpcbind; /* Go the regular way */
850 } else {
851 /* This is a UDP PORTMAP handle. Change to version 4 */
852 vers = RPCBVERS4;
853 CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
854 }
855 /*
856 * We also send the remote system the address we used to
857 * contact it in case it can help it connect back with us
858 */
859 if (parms.r_addr == NULL) {
860 /* for XDRing */
861 parms.r_addr = __UNCONST(&nullstring[0]);
862 }
863 clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDRLIST,
864 (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
865 (xdrproc_t) xdr_rpcb_entry_list_ptr,
866 (char *)(void *)&relp, tottimeout);
867 if (clnt_st == RPC_SUCCESS) {
868 if ((address = got_entry(relp, nconf)) != NULL) {
869 xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
870 (char *)(void *)&relp);
871 CLNT_CONTROL(client, CLGET_SVC_ADDR,
872 (char *)(void *)&servaddr);
873 __rpc_fixup_addr(address, &servaddr);
874 goto done;
875 }
876 /* Entry not found for this transport */
877 xdr_free((xdrproc_t) xdr_rpcb_entry_list_ptr,
878 (char *)(void *)&relp);
879 /*
880 * XXX: should have perhaps returned with error but
881 * since the remote machine might not always be able
882 * to send the address on all transports, we try the
883 * regular way with regular_rpcbind
884 */
885 goto regular_rpcbind;
886 } else if ((clnt_st == RPC_PROGVERSMISMATCH) ||
887 (clnt_st == RPC_PROGUNAVAIL)) {
888 start_vers = RPCBVERS; /* Try version 3 now */
889 goto regular_rpcbind; /* Try different versions */
890 } else {
891 rpc_createerr.cf_stat = RPC_PMAPFAILURE;
892 clnt_geterr(client, &rpc_createerr.cf_error);
893 goto error;
894 }
895 }
896
897 regular_rpcbind:
898
899 /* Now the same transport is to be used to get the address */
900 #if 1
901 if (client && ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
902 (nconf->nc_semantics == NC_TPI_COTS)))
903 #else
904 if (client && nconf->nc_semantics == NC_TPI_CLTS)
905 #endif
906 {
907 /* A CLTS type of client - destroy it */
908 CLNT_DESTROY(client);
909 client = NULL;
910 }
911
912 if (client == NULL) {
913 client = getclnthandle(host, nconf, &parms.r_addr);
914 if (client == NULL) {
915 goto error;
916 }
917 }
918 if (parms.r_addr == NULL)
919 parms.r_addr = __UNCONST(&nullstring[0]);
920
921 /* First try from start_vers and then version 3 (RPCBVERS) */
922 for (vers = start_vers; vers >= RPCBVERS; vers--) {
923 /* Set the version */
924 CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
925 clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
926 (xdrproc_t) xdr_rpcb, (char *)(void *)&parms,
927 (xdrproc_t) xdr_wrapstring, (char *)(void *) &ua,
928 tottimeout);
929 if (clnt_st == RPC_SUCCESS) {
930 if ((ua == NULL) || (ua[0] == 0)) {
931 /* address unknown */
932 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
933 goto error;
934 }
935 address = uaddr2taddr(nconf, ua);
936 #ifdef ND_DEBUG
937 fprintf(stderr, "\tRemote address is [%s]\n", ua);
938 if (!address)
939 fprintf(stderr,
940 "\tCouldn't resolve remote address!\n");
941 #endif
942 xdr_free((xdrproc_t)xdr_wrapstring,
943 (char *)(void *)&ua);
944
945 if (! address) {
946 /* We don't know about your universal address */
947 rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
948 goto error;
949 }
950 CLNT_CONTROL(client, CLGET_SVC_ADDR,
951 (char *)(void *)&servaddr);
952 __rpc_fixup_addr(address, &servaddr);
953 goto done;
954 } else if (clnt_st == RPC_PROGVERSMISMATCH) {
955 struct rpc_err rpcerr;
956
957 clnt_geterr(client, &rpcerr);
958 if (rpcerr.re_vers.low > RPCBVERS4)
959 goto error; /* a new version, can't handle */
960 } else if (clnt_st != RPC_PROGUNAVAIL) {
961 /* Cant handle this error */
962 rpc_createerr.cf_stat = clnt_st;
963 clnt_geterr(client, &rpc_createerr.cf_error);
964 goto error;
965 }
966 }
967
968 error:
969 if (client) {
970 CLNT_DESTROY(client);
971 client = NULL;
972 }
973 done:
974 if (nconf->nc_semantics != NC_TPI_CLTS) {
975 /* This client is the connectionless one */
976 if (client) {
977 CLNT_DESTROY(client);
978 client = NULL;
979 }
980 }
981 if (clpp) {
982 *clpp = client;
983 } else if (client) {
984 CLNT_DESTROY(client);
985 }
986 return (address);
987 }
988
989
990 /*
991 * Find the mapped address for program, version.
992 * Calls the rpcbind service remotely to do the lookup.
993 * Uses the transport specified in nconf.
994 * Returns FALSE (0) if no map exists, else returns 1.
995 *
996 * Assuming that the address is all properly allocated
997 */
998 int
999 rpcb_getaddr(program, version, nconf, address, host)
1000 rpcprog_t program;
1001 rpcvers_t version;
1002 const struct netconfig *nconf;
1003 struct netbuf *address;
1004 const char *host;
1005 {
1006 struct netbuf *na;
1007
1008 _DIAGASSERT(address != NULL);
1009
1010 if ((na = __rpcb_findaddr(program, version, nconf,
1011 host, NULL)) == NULL)
1012 return (FALSE);
1013
1014 if (na->len > address->maxlen) {
1015 /* Too long address */
1016 free(na->buf);
1017 free(na);
1018 rpc_createerr.cf_stat = RPC_FAILED;
1019 return (FALSE);
1020 }
1021 memcpy(address->buf, na->buf, (size_t)na->len);
1022 address->len = na->len;
1023 free(na->buf);
1024 free(na);
1025 return (TRUE);
1026 }
1027
1028 /*
1029 * Get a copy of the current maps.
1030 * Calls the rpcbind service remotely to get the maps.
1031 *
1032 * It returns only a list of the services
1033 * It returns NULL on failure.
1034 */
1035 rpcblist *
1036 rpcb_getmaps(nconf, host)
1037 const struct netconfig *nconf;
1038 const char *host;
1039 {
1040 rpcblist_ptr head = NULL;
1041 CLIENT *client;
1042 enum clnt_stat clnt_st;
1043 rpcvers_t vers = 0;
1044
1045 client = getclnthandle(host, nconf, NULL);
1046 if (client == NULL) {
1047 return (head);
1048 }
1049 clnt_st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1050 (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1051 (char *)(void *)&head, tottimeout);
1052 if (clnt_st == RPC_SUCCESS)
1053 goto done;
1054
1055 if ((clnt_st != RPC_PROGVERSMISMATCH) &&
1056 (clnt_st != RPC_PROGUNAVAIL)) {
1057 rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1058 clnt_geterr(client, &rpc_createerr.cf_error);
1059 goto done;
1060 }
1061
1062 /* fall back to earlier version */
1063 CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1064 if (vers == RPCBVERS4) {
1065 vers = RPCBVERS;
1066 CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1067 if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_DUMP,
1068 (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_rpcblist_ptr,
1069 (char *)(void *)&head, tottimeout) == RPC_SUCCESS)
1070 goto done;
1071 }
1072 rpc_createerr.cf_stat = RPC_RPCBFAILURE;
1073 clnt_geterr(client, &rpc_createerr.cf_error);
1074
1075 done:
1076 CLNT_DESTROY(client);
1077 return (head);
1078 }
1079
1080 /*
1081 * rpcbinder remote-call-service interface.
1082 * This routine is used to call the rpcbind remote call service
1083 * which will look up a service program in the address maps, and then
1084 * remotely call that routine with the given parameters. This allows
1085 * programs to do a lookup and call in one step.
1086 */
1087 enum clnt_stat
1088 rpcb_rmtcall(nconf, host, prog, vers, proc, xdrargs, argsp,
1089 xdrres, resp, tout, addr_ptr)
1090 const struct netconfig *nconf; /* Netconfig structure */
1091 const char *host; /* Remote host name */
1092 rpcprog_t prog;
1093 rpcvers_t vers;
1094 rpcproc_t proc; /* Remote proc identifiers */
1095 xdrproc_t xdrargs, xdrres; /* XDR routines */
1096 const char *argsp; /* Argument */
1097 caddr_t resp; /* Result */
1098 struct timeval tout; /* Timeout value for this call */
1099 const struct netbuf *addr_ptr; /* Preallocated netbuf address */
1100 {
1101 CLIENT *client;
1102 enum clnt_stat stat;
1103 struct r_rpcb_rmtcallargs a;
1104 struct r_rpcb_rmtcallres r;
1105 rpcvers_t rpcb_vers;
1106
1107 stat = RPC_FAILED; /* XXXGCC -Wuninitialized [dreamcast] */
1108
1109 client = getclnthandle(host, nconf, NULL);
1110 if (client == NULL) {
1111 return (RPC_FAILED);
1112 }
1113 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, __UNCONST(&rmttimeout));
1114 a.prog = prog;
1115 a.vers = vers;
1116 a.proc = proc;
1117 a.args.args_val = argsp;
1118 a.xdr_args = xdrargs;
1119 r.addr = NULL;
1120 r.results.results_val = resp;
1121 r.xdr_res = xdrres;
1122
1123 for (rpcb_vers = RPCBVERS4; rpcb_vers >= RPCBVERS; rpcb_vers--) {
1124 CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&rpcb_vers);
1125 stat = CLNT_CALL(client, (rpcproc_t)RPCBPROC_CALLIT,
1126 (xdrproc_t) xdr_rpcb_rmtcallargs, (char *)(void *)&a,
1127 (xdrproc_t) xdr_rpcb_rmtcallres, (char *)(void *)&r, tout);
1128 if ((stat == RPC_SUCCESS) && (addr_ptr != NULL)) {
1129 struct netbuf *na;
1130 na = uaddr2taddr(__UNCONST(nconf), r.addr);
1131 if (!na) {
1132 stat = RPC_N2AXLATEFAILURE;
1133 ((struct netbuf *)__UNCONST(addr_ptr))->len = 0;
1134 goto error;
1135 }
1136 if (na->len > addr_ptr->maxlen) {
1137 /* Too long address */
1138 stat = RPC_FAILED; /* XXX A better error no */
1139 free(na->buf);
1140 free(na);
1141 ((struct netbuf *)__UNCONST(addr_ptr))->len = 0;
1142 goto error;
1143 }
1144 memcpy(addr_ptr->buf, na->buf, (size_t)na->len);
1145 ((struct netbuf *)__UNCONST(addr_ptr))->len = na->len;
1146 free(na->buf);
1147 free(na);
1148 break;
1149 } else if ((stat != RPC_PROGVERSMISMATCH) &&
1150 (stat != RPC_PROGUNAVAIL)) {
1151 goto error;
1152 }
1153 }
1154 error:
1155 CLNT_DESTROY(client);
1156 if (r.addr)
1157 xdr_free((xdrproc_t) xdr_wrapstring, (char *)(void *)&r.addr);
1158 return (stat);
1159 }
1160
1161 /*
1162 * Gets the time on the remote host.
1163 * Returns 1 if succeeds else 0.
1164 */
1165 bool_t
1166 rpcb_gettime(host, timep)
1167 const char *host;
1168 time_t *timep;
1169 {
1170 CLIENT *client = NULL;
1171 void *handle;
1172 struct netconfig *nconf;
1173 rpcvers_t vers;
1174 enum clnt_stat st;
1175
1176
1177 if ((host == NULL) || (host[0] == 0)) {
1178 time(timep);
1179 return (TRUE);
1180 }
1181
1182 if ((handle = __rpc_setconf("netpath")) == NULL) {
1183 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1184 return (FALSE);
1185 }
1186 rpc_createerr.cf_stat = RPC_SUCCESS;
1187 while (client == NULL) {
1188 if ((nconf = __rpc_getconf(handle)) == NULL) {
1189 if (rpc_createerr.cf_stat == RPC_SUCCESS)
1190 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1191 break;
1192 }
1193 client = getclnthandle(host, nconf, NULL);
1194 if (client)
1195 break;
1196 }
1197 __rpc_endconf(handle);
1198 if (client == NULL) {
1199 return (FALSE);
1200 }
1201
1202 st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1203 (xdrproc_t) xdr_void, NULL,
1204 (xdrproc_t) xdr_int, (char *)(void *)timep, tottimeout);
1205
1206 if ((st == RPC_PROGVERSMISMATCH) || (st == RPC_PROGUNAVAIL)) {
1207 CLNT_CONTROL(client, CLGET_VERS, (char *)(void *)&vers);
1208 if (vers == RPCBVERS4) {
1209 /* fall back to earlier version */
1210 vers = RPCBVERS;
1211 CLNT_CONTROL(client, CLSET_VERS, (char *)(void *)&vers);
1212 st = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETTIME,
1213 (xdrproc_t) xdr_void, NULL,
1214 (xdrproc_t) xdr_int, (char *)(void *)timep,
1215 tottimeout);
1216 }
1217 }
1218 CLNT_DESTROY(client);
1219 return (st == RPC_SUCCESS? TRUE: FALSE);
1220 }
1221
1222 /*
1223 * Converts taddr to universal address. This routine should never
1224 * really be called because local n2a libraries are always provided.
1225 */
1226 char *
1227 rpcb_taddr2uaddr(nconf, taddr)
1228 struct netconfig *nconf;
1229 struct netbuf *taddr;
1230 {
1231 CLIENT *client;
1232 char *uaddr = NULL;
1233
1234
1235 /* parameter checking */
1236 if (nconf == NULL) {
1237 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1238 return (NULL);
1239 }
1240 if (taddr == NULL) {
1241 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1242 return (NULL);
1243 }
1244 client = local_rpcb();
1245 if (! client) {
1246 return (NULL);
1247 }
1248
1249 CLNT_CALL(client, (rpcproc_t)RPCBPROC_TADDR2UADDR,
1250 (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1251 (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr, tottimeout);
1252 CLNT_DESTROY(client);
1253 return (uaddr);
1254 }
1255
1256 /*
1257 * Converts universal address to netbuf. This routine should never
1258 * really be called because local n2a libraries are always provided.
1259 */
1260 struct netbuf *
1261 rpcb_uaddr2taddr(nconf, uaddr)
1262 struct netconfig *nconf;
1263 char *uaddr;
1264 {
1265 CLIENT *client;
1266 struct netbuf *taddr;
1267
1268
1269 /* parameter checking */
1270 if (nconf == NULL) {
1271 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
1272 return (NULL);
1273 }
1274 if (uaddr == NULL) {
1275 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
1276 return (NULL);
1277 }
1278 client = local_rpcb();
1279 if (! client) {
1280 return (NULL);
1281 }
1282
1283 taddr = (struct netbuf *)calloc(1, sizeof (struct netbuf));
1284 if (taddr == NULL) {
1285 CLNT_DESTROY(client);
1286 return (NULL);
1287 }
1288 if (CLNT_CALL(client, (rpcproc_t)RPCBPROC_UADDR2TADDR,
1289 (xdrproc_t) xdr_wrapstring, (char *)(void *)&uaddr,
1290 (xdrproc_t) xdr_netbuf, (char *)(void *)taddr,
1291 tottimeout) != RPC_SUCCESS) {
1292 free(taddr);
1293 taddr = NULL;
1294 }
1295 CLNT_DESTROY(client);
1296 return (taddr);
1297 }
1298