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 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <locale.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <memory.h>
30 #include <varargs.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <sys/param.h>
36 #include <rpc/rpc.h>
37 #include <errno.h>
38 #include <sys/stat.h>
39 #include <netdb.h>
40 #include <sys/pathconf.h>
41 #include <netdir.h>
42 #include <netconfig.h>
43 #include <sys/sockio.h>
44 #include <net/if.h>
45 #include <syslog.h>
46 #include <netinet/in.h>
47 #include <nfs/nfs_sec.h>
48 #include <strings.h>
49 #include <sys/nsctl/rdc_prot.h>
50 #include <nsctl.h>
51
52 #include "librdc.h"
53
54 #define MAXIFS 32
55
56 /* number of transports to try */
57 #define MNT_PREF_LISTLEN 2
58 #define FIRST_TRY 1
59 #define SECOND_TRY 2
60
61
62 int
Is_ipv6present(void)63 Is_ipv6present(void)
64 {
65 #ifdef AF_INET6
66 int sock;
67 struct lifnum lifn;
68
69 sock = socket(AF_INET6, SOCK_DGRAM, 0);
70 if (sock < 0)
71 return (0);
72
73 lifn.lifn_family = AF_INET6;
74 lifn.lifn_flags = 0;
75 if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
76 close(sock);
77 return (0);
78 }
79 close(sock);
80 if (lifn.lifn_count == 0)
81 return (0);
82 return (1);
83 #else
84 return (0);
85 #endif
86 }
87
88 /*
89 * The following is stolen from autod_nfs.c
90 */
91 static void
getmyaddrs(struct ifconf * ifc)92 getmyaddrs(struct ifconf *ifc)
93 {
94 int sock;
95 int numifs;
96 char *buf;
97 int family;
98
99 ifc->ifc_buf = NULL;
100 ifc->ifc_len = 0;
101
102 #ifdef AF_INET6
103 family = AF_INET6;
104 #else
105 family = AF_INET;
106 #endif
107 if ((sock = socket(family, SOCK_DGRAM, 0)) < 0) {
108 #ifdef DEBUG
109 perror("getmyaddrs(): socket");
110 #endif
111 return;
112 }
113
114 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
115 #ifdef DEBUG
116 perror("getmyaddrs(): SIOCGIFNUM");
117 #endif
118 numifs = MAXIFS;
119 }
120
121 buf = (char *)malloc(numifs * sizeof (struct ifreq));
122 if (buf == NULL) {
123 #ifdef DEBUG
124 fprintf(stderr, "getmyaddrs(): malloc failed\n");
125 #endif
126 (void) close(sock);
127 return;
128 }
129
130 ifc->ifc_buf = buf;
131 ifc->ifc_len = numifs * sizeof (struct ifreq);
132
133 if (ioctl(sock, SIOCGIFCONF, (char *)ifc) < 0) {
134 #ifdef DEBUG
135 perror("getmyaddrs(): SIOCGIFCONF");
136 #else
137 ;
138 /*EMPTY*/
139 #endif
140 }
141
142 (void) close(sock);
143 }
144
145 int
self_check(char * hostname)146 self_check(char *hostname)
147 {
148 int n;
149 struct sockaddr_in *s1, *s2;
150 struct ifreq *ifr;
151 struct nd_hostserv hs;
152 struct nd_addrlist *retaddrs;
153 struct netconfig *nconfp;
154 struct ifconf *ifc;
155 int retval;
156
157 ifc = malloc(sizeof (struct ifconf));
158 if (ifc == NULL)
159 return (0);
160 memset((char *)ifc, 0, sizeof (struct ifconf));
161 getmyaddrs(ifc);
162 /*
163 * Get the IP address for hostname
164 */
165 nconfp = getnetconfigent("udp");
166 if (nconfp == NULL) {
167 #ifdef DEBUG
168 fprintf(stderr, "self_check(): getnetconfigent failed\n");
169 #endif
170 retval = 0;
171 goto out;
172 }
173 hs.h_host = hostname;
174 hs.h_serv = "rpcbind";
175 if (netdir_getbyname(nconfp, &hs, &retaddrs) != ND_OK) {
176 freenetconfigent(nconfp);
177 retval = 0;
178 goto out;
179 }
180 freenetconfigent(nconfp);
181 /* LINTED pointer alignment */
182 s1 = (struct sockaddr_in *)retaddrs->n_addrs->buf;
183
184 /*
185 * Now compare it against the list of
186 * addresses for the interfaces on this
187 * host.
188 */
189 ifr = ifc->ifc_req;
190 n = ifc->ifc_len / sizeof (struct ifreq);
191 s2 = NULL;
192 for (; n > 0; n--, ifr++) {
193 if (ifr->ifr_addr.sa_family != AF_INET)
194 continue;
195
196 /* LINTED pointer alignment */
197 s2 = (struct sockaddr_in *)&ifr->ifr_addr;
198
199 if (memcmp((char *)&s2->sin_addr,
200 (char *)&s1->sin_addr, sizeof (s1->sin_addr)) == 0) {
201 netdir_free((void *)retaddrs, ND_ADDRLIST);
202 retval = 1;
203 goto out; /* it's me */
204 }
205 }
206 netdir_free((void *)retaddrs, ND_ADDRLIST);
207 retval = 0;
208
209 out:
210 if (ifc->ifc_buf != NULL)
211 free(ifc->ifc_buf);
212 free(ifc);
213 return (retval);
214 }
215
216
217 int
convert_nconf_to_knconf(struct netconfig * nconf,struct knetconfig * knconf)218 convert_nconf_to_knconf(struct netconfig *nconf, struct knetconfig *knconf)
219 {
220 struct stat sb;
221
222 if (stat(nconf->nc_device, &sb) < 0) {
223 (void) syslog(LOG_ERR, "can't find device for transport %s\n",
224 nconf->nc_device);
225 return (-1);
226 }
227 #ifdef DEBUG_ADDR
228 printf("lib knconf %x %s %s %x\n", nconf->nc_semantics,
229 nconf->nc_protofmly, nconf->nc_proto, sb.st_rdev);
230 #endif
231
232 knconf->knc_semantics = nconf->nc_semantics;
233 knconf->knc_protofmly = nconf->nc_protofmly;
234 knconf->knc_proto = nconf->nc_proto;
235 knconf->knc_rdev = sb.st_rdev;
236
237 return (0);
238 }
239
240 struct hostent *
gethost_byname(const char * name)241 gethost_byname(const char *name)
242 {
243 int errnum;
244 #ifdef AF_INET6
245 return (getipnodebyname(name, AF_INET6, AI_DEFAULT, &errnum));
246 #else /* !AF_INET6 */
247 return (gethostbyname(name));
248 #endif /* AF_INET6 */
249 }
250
251 int
gethost_netaddrs(char * fromhost,char * tohost,char * fromnetaddr,char * tonetaddr)252 gethost_netaddrs(char *fromhost, char *tohost,
253 char *fromnetaddr, char *tonetaddr)
254 {
255 struct hostent *host;
256 int j;
257 int errnum;
258
259 #ifdef AF_INET6
260 host = getipnodebyname(fromhost, AF_INET6, AI_DEFAULT, &errnum);
261 if (host == NULL) {
262 #ifdef DEBUG
263 (void) fprintf(stderr, dgettext("sndr",
264 "Could not find host %s"), fromhost);
265 #endif
266 return (-1);
267 }
268 for (j = 0; j < host->h_length; j++)
269 fromnetaddr[j] = host->h_addr[j];
270 freehostent(host);
271 #else /* !AF_INET6 */
272 host = gethostbyname(fromhost);
273 if (host == NULL) {
274 #ifdef DEBUG
275 (void) fprintf(stderr, dgettext("sndr",
276 "Could not find host %s"), fromhost);
277 #endif
278 return (-1);
279 }
280
281 if (host->h_length < 4) {
282 #ifdef DEBUG
283 fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length);
284 #endif
285 return (-1);
286 }
287
288 for (j = 0; j < host->h_length; j++)
289 fromnetaddr[j] = host->h_addr[j];
290 #endif /* AF_INET6 */
291
292 #ifdef AF_INET6
293 host = getipnodebyname(tohost, AF_INET6, AI_DEFAULT, &errnum);
294 if (host == NULL) {
295 #ifdef DEBUG
296 (void) fprintf(stderr, dgettext("sndr",
297 "Could not find host %s"), tohost);
298 #endif
299 return (-1);
300 }
301 for (j = 0; j < host->h_length; j++)
302 tonetaddr[j] = host->h_addr[j];
303 freehostent(host);
304 #else /* !AF_INET6 */
305 host = gethostbyname(tohost);
306 if (host == NULL) {
307 #ifdef DEBUG
308 (void) fprintf(stderr, dgettext("sndr",
309 "Could not find host %s"), tohost);
310 #endif
311 return (-1);
312 }
313
314 if (host->h_length < 4) {
315 #ifdef DEBUG
316 fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length);
317 #endif
318 return (-1);
319 }
320
321 for (j = 0; j < host->h_length; j++)
322 tonetaddr[j] = host->h_addr[j];
323 #endif /* AF_INET6 */
324 return (0);
325 }
326
327 /*
328 * Get the network address on "hostname" for program "prog"
329 * with version "vers" by using the nconf configuration data
330 * passed in.
331 *
332 * If the address of a netconfig pointer is null then
333 * information is not sufficient and no netbuf will be returned.
334 *
335 * Finally, ping the null procedure of that service.
336 *
337 */
338 static struct netbuf *
get_the_addr(char * hostname,ulong_t prog,ulong_t vers,struct netconfig * nconf,ushort_t port,struct t_info * tinfo,int portmap)339 get_the_addr(char *hostname, ulong_t prog, ulong_t vers,
340 struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
341 int portmap)
342 {
343 struct netbuf *nb = NULL;
344 struct t_bind *tbind = NULL;
345 CLIENT *cl = NULL;
346 struct timeval tv;
347 int fd = -1;
348 AUTH *ah = NULL;
349
350 if (nconf == NULL)
351 return (NULL);
352
353 if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
354 goto done;
355
356 /* LINTED pointer alignment */
357 if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
358 goto done;
359
360 if (portmap) { /* contact rpcbind */
361 if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
362 hostname) == FALSE) {
363 goto done;
364 }
365
366 if (port) {
367 if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
368 /* LINTED pointer alignment */
369 ((struct sockaddr_in *)tbind->addr.buf)->sin_port
370 = port;
371 #ifdef NC_INET6
372 else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
373 /* LINTED pointer alignment */
374 ((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
375 = port;
376 #endif
377 }
378
379 /* Simon -- we never use the client we create?! */
380 cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
381 if (cl == NULL)
382 goto done;
383
384 ah = authsys_create_default();
385 if (ah != NULL)
386 cl->cl_auth = ah;
387
388 tv.tv_sec = 5;
389 tv.tv_usec = 0;
390
391 (void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
392 } else { /* create our own address and skip rpcbind */
393 struct netbuf *nb;
394 struct hostent *hp;
395 int j;
396 int errnum;
397 unsigned short family;
398 nb = &(tbind->addr);
399
400 #ifdef AF_INET6
401 if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
402 hp = getipnodebyname(hostname, AF_INET6, 0, &errnum);
403 family = AF_INET6;
404 nb->len = nb->maxlen = sizeof (struct sockaddr_in6);
405 } else {
406 hp = getipnodebyname(hostname, AF_INET, 0, &errnum);
407 family = AF_INET;
408 nb->len = nb->maxlen = sizeof (struct sockaddr_in);
409 }
410 if (hp == NULL) {
411 #ifdef DEBUG_ADDR
412 (void) fprintf(stderr, dgettext("sndr",
413 "Could not find host %s\n"), hostname);
414 #endif
415 goto done;
416 }
417 nb->buf = (char *)calloc(1, nb->maxlen);
418 if (nb->buf == NULL) {
419 (void) printf(dgettext("sndr", "no memory\n"));
420 goto done;
421 }
422
423 if (family == AF_INET) {
424 for (j = 0; j < hp->h_length; j++)
425 nb->buf[j+4] = hp->h_addr[j];
426 /* LINTED pointer alignment */
427 ((struct sockaddr_in *)(nb->buf))->sin_port = port;
428 /* LINTED pointer alignment */
429 ((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET;
430 } else {
431 for (j = 0; j < hp->h_length; j++)
432 nb->buf[j+8] = hp->h_addr[j];
433 /* LINTED pointer alignment */
434 ((struct sockaddr_in6 *)(nb->buf))->sin6_port = port;
435 /* LINTED pointer alignment */
436 ((struct sockaddr_in6 *)(nb->buf))->sin6_family =
437 AF_INET6;
438 }
439 freehostent(hp);
440 #else
441 hp = gethostbyname(hostname);
442 if (hp == NULL) {
443 #ifdef DEBUG
444 (void) fprintf(stderr, dgettext("sndr",
445 "Could not find host %s"), hostname);
446 #endif
447 goto done;
448 }
449
450 nb->len = nb->maxlen = sizeof (struct sockaddr_in);
451 nb->buf = (char *)calloc(1, nb->maxlen);
452 if (nb->buf == NULL) {
453 (void) printf(dgettext("sndr", "no memory\n"));
454 free(nb);
455 nb = NULL;
456 goto done;
457 }
458
459 for (j = 0; j < hp->h_length; j++)
460 nb->buf[j+4] = hp->h_addr[j];
461
462 if (hp->h_addrtype == AF_INET) {
463 ((struct sockaddr_in *)(nb->buf))->sin_port = port;
464 ((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET;
465 }
466 #endif
467 }
468
469 /*
470 * Make a copy of the netbuf to return
471 */
472 nb = (struct netbuf *)calloc(1, sizeof (*nb));
473 if (nb == NULL) {
474 (void) printf(dgettext("sndr", "no memory\n"));
475 goto done;
476 }
477
478 *nb = tbind->addr; /* structure copy */
479
480 nb->buf = (char *)calloc(1, nb->maxlen);
481 if (nb->buf == NULL) {
482 (void) printf(dgettext("sndr", "no memory\n"));
483 free(nb);
484 nb = NULL;
485 goto done;
486 }
487
488 (void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
489
490 done:
491 if (cl) {
492 if (ah != NULL) {
493 AUTH_DESTROY(cl->cl_auth);
494 cl->cl_auth = NULL;
495 }
496
497 clnt_destroy(cl);
498 cl = NULL;
499 }
500
501 if (tbind) {
502 t_free((char *)tbind, T_BIND);
503 tbind = NULL;
504 }
505
506 if (fd >= 0)
507 (void) t_close(fd);
508 return (nb);
509 }
510
511 /*
512 * Get a network address on "hostname" for program "prog"
513 * with version "vers". If the port number is specified (non zero)
514 * then try for a TCP/UDP transport and set the port number of the
515 * resulting IP address.
516 *
517 * If the address of a netconfig pointer was passed and
518 * if it's not null, use it as the netconfig otherwise
519 * assign the address of the netconfig that was used to
520 * establish contact with the service.
521 * If portmap is false, we return a similiar address and we do not
522 * contact rpcbind
523 *
524 */
525 struct netbuf *
get_addr(char * hostname,ulong_t prog,ulong_t vers,struct netconfig ** nconfp,char * proto,char * srvport,struct t_info * tinfo,int portmap)526 get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp,
527 char *proto, char *srvport, struct t_info *tinfo, int portmap)
528 {
529 struct netbuf *nb = NULL;
530 struct netconfig *nconf = NULL;
531 NCONF_HANDLE *nc = NULL;
532 int nthtry = FIRST_TRY;
533 struct servent *svp;
534 ushort_t port;
535
536 /*
537 * First lets get the requested port
538 */
539
540 if ((svp = getservbyname(srvport, proto)) == NULL)
541 goto done;
542 port = svp->s_port;
543 /*
544 * No nconf passed in.
545 *
546 * Try to get a nconf from /etc/netconfig filtered by
547 * the NETPATH environment variable.
548 * First search for COTS, second for CLTS unless proto
549 * is specified. When we retry, we reset the
550 * netconfig list so that we would search the whole list
551 * all over again.
552 */
553 if ((nc = setnetpath()) == NULL)
554 goto done;
555
556 /*
557 * If proto is specified, then only search for the match,
558 * otherwise try COTS first, if failed, try CLTS.
559 */
560 if (proto) {
561 while (nconf = getnetpath(nc)) {
562 if (strcmp(nconf->nc_netid, proto) == 0) {
563 /*
564 * If the port number is specified then TCP/UDP
565 * is needed. Otherwise any cots/clts will do.
566 */
567 if (port == 0)
568 break;
569
570 if ((strcmp(nconf->nc_protofmly, NC_INET) == 0
571 #ifdef NC_INET6
572 /* CSTYLED */
573 || strcmp(nconf->nc_protofmly, NC_INET6) == 0
574 #endif
575 /* CSTYLED */
576 ) &&
577 (strcmp(nconf->nc_proto, NC_TCP) == 0 ||
578 strcmp(nconf->nc_proto, NC_UDP) == 0))
579 break;
580 else {
581 nconf = NULL;
582 break;
583 }
584 }
585 }
586 if (nconf == NULL)
587 goto done;
588 if ((nb = get_the_addr(hostname, prog, vers, nconf, port,
589 tinfo, portmap)) == NULL) {
590 goto done;
591 }
592 } else {
593 retry:
594 while (nconf = getnetpath(nc)) {
595 if (nconf->nc_flag & NC_VISIBLE) {
596 if (nthtry == FIRST_TRY) {
597 if ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
598 (nconf->nc_semantics == NC_TPI_COTS)) {
599 if (port == 0)
600 break;
601 if ((strcmp(nconf->nc_protofmly,
602 NC_INET) == 0
603 #ifdef NC_INET6
604 /* CSTYLED */
605 || strcmp(nconf->nc_protofmly,
606 NC_INET6) == 0
607 #endif
608 /* CSTYLED */
609 ) &&
610 (strcmp(nconf->nc_proto, NC_TCP) == 0))
611 break;
612 }
613 }
614 }
615 } /* while */
616 if (nconf == NULL) {
617 if (++nthtry <= MNT_PREF_LISTLEN) {
618 endnetpath(nc);
619 if ((nc = setnetpath()) == NULL)
620 goto done;
621 goto retry;
622 } else
623 goto done;
624 } else {
625 if ((nb = get_the_addr(hostname, prog, vers, nconf,
626 port, tinfo, portmap)) == NULL) {
627 /*
628 * Continue the same search path in the
629 * netconfig db until no more matched
630 * nconf (nconf == NULL).
631 */
632 goto retry;
633 }
634 #ifdef AF_INET6
635 if ((nb->len == 8) &&
636 (strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
637 /*
638 * We have a mismatch in the netconfig retry
639 */
640 free(nb);
641 goto retry;
642 }
643 #endif
644 }
645 }
646
647 /*
648 * Got nconf and nb. Now dup the netconfig structure (nconf)
649 * and return it thru nconfp.
650 */
651 *nconfp = getnetconfigent(nconf->nc_netid);
652 if (*nconfp == NULL) {
653 syslog(LOG_ERR, "no memory\n");
654 free(nb);
655 nb = NULL;
656 }
657 done:
658 if (nc)
659 endnetpath(nc);
660 return (nb);
661 }
662
663
664 /* return values as for nsc_check_release() */
665 int
rdc_check_release(char ** reqd)666 rdc_check_release(char **reqd)
667 {
668 /* librdc.so must be built on the runtime OS release */
669 return (nsc_check_release(BUILD_REV_STR, NULL, reqd));
670 }
671