1 /* $NetBSD: transp_tli.c,v 1.1.1.3 2015/01/17 16:34:16 christos Exp $ */
2
3 /*
4 * Copyright (c) 1997-2014 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 *
38 * File: am-utils/conf/transp/transp_tli.c
39 *
40 * TLI specific utilities.
41 * -Erez Zadok <ezk@cs.columbia.edu>
42 */
43
44 #ifdef HAVE_CONFIG_H
45 # include <config.h>
46 #endif /* HAVE_CONFIG_H */
47 #include <am_defs.h>
48 #include <amu.h>
49
50 struct netconfig *nfsncp;
51
52
53 /*
54 * find the IP address that can be used to connect to the local host
55 */
56 void
amu_get_myaddress(struct in_addr * iap,const char * preferred_localhost)57 amu_get_myaddress(struct in_addr *iap, const char *preferred_localhost)
58 {
59 int ret;
60 voidp handlep;
61 struct netconfig *ncp;
62 struct nd_addrlist *addrs = (struct nd_addrlist *) NULL;
63 struct nd_hostserv service;
64
65 handlep = setnetconfig();
66 ncp = getnetconfig(handlep);
67 service.h_host = (preferred_localhost ? (char *) preferred_localhost : HOST_SELF_CONNECT);
68 service.h_serv = (char *) NULL;
69
70 ret = netdir_getbyname(ncp, &service, &addrs);
71
72 if (ret || !addrs || addrs->n_cnt < 1) {
73 plog(XLOG_FATAL, "cannot get local host address. using 127.0.0.1");
74 iap->s_addr = htonl(INADDR_LOOPBACK);
75 } else {
76 /*
77 * XXX: there may be more more than one address for this local
78 * host. Maybe something can be done with those.
79 */
80 struct sockaddr_in *sinp = (struct sockaddr_in *) addrs->n_addrs[0].buf;
81 char dq[20];
82 if (preferred_localhost)
83 plog(XLOG_INFO, "localhost_address \"%s\" requested, using %s",
84 preferred_localhost, inet_dquad(dq, sizeof(dq), iap->s_addr));
85 iap->s_addr = sinp->sin_addr.s_addr; /* XXX: used to be htonl() */
86 }
87
88 endnetconfig(handlep); /* free's up internal resources too */
89 netdir_free((voidp) addrs, ND_ADDRLIST);
90 }
91
92
93 /*
94 * How to bind to reserved ports.
95 * TLI handle (socket) and port version.
96 */
97 int
bind_resv_port(int td,u_short * pp)98 bind_resv_port(int td, u_short *pp)
99 {
100 int rc = -1, port;
101 struct t_bind *treq, *tret;
102 struct sockaddr_in *sin;
103
104 treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
105 if (!treq) {
106 plog(XLOG_ERROR, "t_alloc req");
107 return -1;
108 }
109 tret = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
110 if (!tret) {
111 t_free((char *) treq, T_BIND);
112 plog(XLOG_ERROR, "t_alloc ret");
113 return -1;
114 }
115 memset((char *) treq->addr.buf, 0, treq->addr.len);
116 sin = (struct sockaddr_in *) treq->addr.buf;
117 sin->sin_family = AF_INET;
118 treq->qlen = 64; /* 0 is ok for udp, for tcp you need qlen>0 */
119 treq->addr.len = treq->addr.maxlen;
120 errno = EADDRINUSE;
121 port = IPPORT_RESERVED;
122
123 do {
124 --port;
125 sin->sin_port = htons(port);
126 rc = t_bind(td, treq, tret);
127 if (rc < 0) {
128 plog(XLOG_ERROR, "t_bind");
129 } else {
130 if (memcmp(treq->addr.buf, tret->addr.buf, tret->addr.len) == 0)
131 break;
132 else
133 t_unbind(td);
134 }
135 } while ((rc < 0 || errno == EADDRINUSE) && (int) port > IPPORT_RESERVED / 2);
136
137 if (pp) {
138 if (rc == 0)
139 *pp = port;
140 else
141 plog(XLOG_ERROR, "could not t_bind to any reserved port");
142 }
143 t_free((char *) tret, T_BIND);
144 t_free((char *) treq, T_BIND);
145 return rc;
146 }
147
148
149
150
151 /*
152 * close a descriptor, TLI style
153 */
154 int
amu_close(int fd)155 amu_close(int fd)
156 {
157 return t_close(fd);
158 }
159
160
161 /*
162 * Create an rpc client attached to the mount daemon.
163 */
164 CLIENT *
get_mount_client(char * host,struct sockaddr_in * unused_sin,struct timeval * tv,int * sock,u_long mnt_version)165 get_mount_client(char *host, struct sockaddr_in *unused_sin, struct timeval *tv, int *sock, u_long mnt_version)
166 {
167 CLIENT *client;
168 struct netbuf nb;
169 struct netconfig *nc = NULL;
170 struct sockaddr_in sin;
171
172 nb.maxlen = sizeof(sin);
173 nb.buf = (char *) &sin;
174
175 /*
176 * First try a TCP handler
177 */
178
179 /*
180 * Find mountd address on TCP
181 */
182 if ((nc = getnetconfigent(NC_TCP)) == NULL) {
183 plog(XLOG_ERROR, "getnetconfig for tcp failed: %s", nc_sperror());
184 goto tryudp;
185 }
186 if (!rpcb_getaddr(MOUNTPROG, mnt_version, nc, &nb, host)) {
187 /*
188 * don't print error messages here, since mountd might legitimately
189 * serve udp only
190 */
191 goto tryudp;
192 }
193 /*
194 * Create privileged TCP socket
195 */
196 *sock = t_open(nc->nc_device, O_RDWR, 0);
197
198 if (*sock < 0) {
199 plog(XLOG_ERROR, "t_open %s: %m", nc->nc_device);
200 goto tryudp;
201 }
202 if (bind_resv_port(*sock, (u_short *) NULL) < 0)
203 plog(XLOG_ERROR, "couldn't bind mountd socket to privileged port");
204
205 if ((client = clnt_vc_create(*sock, &nb, MOUNTPROG, mnt_version, 0, 0))
206 == (CLIENT *) NULL) {
207 plog(XLOG_ERROR, "clnt_vc_create failed");
208 t_close(*sock);
209 goto tryudp;
210 }
211 /* tcp succeeded */
212 dlog("get_mount_client: using tcp, port %d", sin.sin_port);
213 if (nc)
214 freenetconfigent(nc);
215 return client;
216
217 tryudp:
218 /* first free possibly previously allocated netconfig entry */
219 if (nc)
220 freenetconfigent(nc);
221
222 /*
223 * TCP failed so try UDP
224 */
225
226 /*
227 * Find mountd address on UDP
228 */
229 if ((nc = getnetconfigent(NC_UDP)) == NULL) {
230 plog(XLOG_ERROR, "getnetconfig for udp failed: %s", nc_sperror());
231 goto badout;
232 }
233 if (!rpcb_getaddr(MOUNTPROG, mnt_version, nc, &nb, host)) {
234 plog(XLOG_ERROR, "%s",
235 clnt_spcreateerror("couldn't get mountd address on udp"));
236 goto badout;
237 }
238 /*
239 * Create privileged UDP socket
240 */
241 *sock = t_open(nc->nc_device, O_RDWR, 0);
242
243 if (*sock < 0) {
244 plog(XLOG_ERROR, "t_open %s: %m", nc->nc_device);
245 goto badout; /* neither tcp not udp succeeded */
246 }
247 if (bind_resv_port(*sock, (u_short *) NULL) < 0)
248 plog(XLOG_ERROR, "couldn't bind mountd socket to privileged port");
249
250 if ((client = clnt_dg_create(*sock, &nb, MOUNTPROG, mnt_version, 0, 0))
251 == (CLIENT *) NULL) {
252 plog(XLOG_ERROR, "clnt_dg_create failed");
253 t_close(*sock);
254 goto badout; /* neither tcp not udp succeeded */
255 }
256 if (clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) tv) == FALSE) {
257 plog(XLOG_ERROR, "clnt_control CLSET_RETRY_TIMEOUT for udp failed");
258 clnt_destroy(client);
259 goto badout; /* neither tcp not udp succeeded */
260 }
261 /* udp succeeded */
262 dlog("get_mount_client: using udp, port %d", sin.sin_port);
263 return client;
264
265 badout:
266 /* failed */
267 if (nc)
268 freenetconfigent(nc);
269 return NULL;
270 }
271
272
273 #ifdef NOT_NEEDED_ON_TLI_SYSTEMS
274 /*
275 * find the address of the caller of an RPC procedure.
276 */
277 struct sockaddr_in *
amu_svc_getcaller(SVCXPRT * xprt)278 amu_svc_getcaller(SVCXPRT *xprt)
279 {
280 /*
281 * On TLI systems we don't use an INET network type, but a "ticlts" (see
282 * /etc/netconfig). This means that packets could only come from the
283 * loopback interface, and we don't need to check them and filter possibly
284 * spoofed packets. Therefore we simply return NULL here, and the caller
285 * will ignore the result.
286 */
287 return NULL; /* tell called to ignore check */
288 }
289 #endif /* NOT_NEEDED_ON_TLI_SYSTEMS */
290
291
292 /*
293 * Register an RPC server:
294 * return 1 on success, 0 otherwise.
295 */
296 int
amu_svc_register(SVCXPRT * xprt,u_long prognum,u_long versnum,void (* dispatch)(struct svc_req * rqstp,SVCXPRT * xprt),u_long protocol,struct netconfig * ncp)297 amu_svc_register(SVCXPRT *xprt, u_long prognum, u_long versnum,
298 void (*dispatch)(struct svc_req *rqstp, SVCXPRT *xprt),
299 u_long protocol, struct netconfig *ncp)
300 {
301 /* on TLI: svc_reg returns 1 on success, 0 otherwise */
302 return svc_reg(xprt, prognum, versnum, dispatch, ncp);
303 }
304
305
306 /*
307 * Bind to reserved UDP port, for NFS service only.
308 * Port-only version.
309 */
310 static int
bind_resv_port_only_udp(u_short * pp)311 bind_resv_port_only_udp(u_short *pp)
312 {
313 int td, rc = -1, port;
314 struct t_bind *treq, *tret;
315 struct sockaddr_in *sin;
316 extern char *t_errlist[];
317 extern int t_errno;
318 struct netconfig *nc = (struct netconfig *) NULL;
319 voidp nc_handle;
320
321 if ((nc_handle = setnetconfig()) == (voidp) NULL) {
322 plog(XLOG_ERROR, "Cannot rewind netconfig: %s", nc_sperror());
323 return -1;
324 }
325 /*
326 * Search the netconfig table for INET/UDP.
327 * This loop will terminate if there was an error in the /etc/netconfig
328 * file or if you reached the end of the file without finding the udp
329 * device. Either way your machine has probably far more problems (for
330 * example, you cannot have nfs v2 w/o UDP).
331 */
332 while (1) {
333 if ((nc = getnetconfig(nc_handle)) == (struct netconfig *) NULL) {
334 plog(XLOG_ERROR, "Error accessing getnetconfig: %s", nc_sperror());
335 endnetconfig(nc_handle);
336 return -1;
337 }
338 if (STREQ(nc->nc_protofmly, NC_INET) &&
339 STREQ(nc->nc_proto, NC_UDP))
340 break;
341 }
342
343 /*
344 * This is the primary reason for the getnetconfig code above: to get the
345 * correct device name to udp, and t_open a descriptor to be used in
346 * t_bind below.
347 */
348 td = t_open(nc->nc_device, O_RDWR, (struct t_info *) NULL);
349 endnetconfig(nc_handle);
350
351 if (td < 0) {
352 plog(XLOG_ERROR, "t_open failed: %d: %s", t_errno, t_errlist[t_errno]);
353 return -1;
354 }
355 treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
356 if (!treq) {
357 plog(XLOG_ERROR, "t_alloc req");
358 return -1;
359 }
360 tret = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
361 if (!tret) {
362 t_free((char *) treq, T_BIND);
363 plog(XLOG_ERROR, "t_alloc ret");
364 return -1;
365 }
366 memset((char *) treq->addr.buf, 0, treq->addr.len);
367 sin = (struct sockaddr_in *) treq->addr.buf;
368 sin->sin_family = AF_INET;
369 treq->qlen = 64; /* 0 is ok for udp, for tcp you need qlen>0 */
370 treq->addr.len = treq->addr.maxlen;
371 errno = EADDRINUSE;
372
373 if (pp && *pp > 0) {
374 sin->sin_port = htons(*pp);
375 rc = t_bind(td, treq, tret);
376 } else {
377 port = IPPORT_RESERVED;
378
379 do {
380 --port;
381 sin->sin_port = htons(port);
382 rc = t_bind(td, treq, tret);
383 if (rc < 0) {
384 plog(XLOG_ERROR, "t_bind for port %d: %s", port, t_errlist[t_errno]);
385 } else {
386 if (memcmp(treq->addr.buf, tret->addr.buf, tret->addr.len) == 0)
387 break;
388 else
389 t_unbind(td);
390 }
391 } while ((rc < 0 || errno == EADDRINUSE) && (int) port > IPPORT_RESERVED / 2);
392
393 if (pp && rc == 0)
394 *pp = port;
395 }
396
397 t_free((char *) tret, T_BIND);
398 t_free((char *) treq, T_BIND);
399 return rc;
400 }
401
402
403 /*
404 * Bind NFS to a reserved port.
405 */
406 static int
bind_nfs_port(int unused_so,u_short * nfs_portp)407 bind_nfs_port(int unused_so, u_short *nfs_portp)
408 {
409 u_short port = 0;
410 int error = bind_resv_port_only_udp(&port);
411
412 if (error == 0)
413 *nfs_portp = port;
414 return error;
415 }
416
417
418 /*
419 * Create the nfs service for amd
420 * return 0 (TRUE) if OK, 1 (FALSE) if failed.
421 */
422 int
create_nfs_service(int * soNFSp,u_short * nfs_portp,SVCXPRT ** nfs_xprtp,void (* dispatch_fxn)(struct svc_req * rqstp,SVCXPRT * transp),u_long nfs_version)423 create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp), u_long nfs_version)
424 {
425 char *nettype = "ticlts";
426
427 nfsncp = getnetconfigent(nettype);
428 if (nfsncp == NULL) {
429 plog(XLOG_ERROR, "cannot getnetconfigent for %s", nettype);
430 /* failed with ticlts, try plain udp (hpux11) */
431 nettype = "udp";
432 nfsncp = getnetconfigent(nettype);
433 if (nfsncp == NULL) {
434 plog(XLOG_ERROR, "cannot getnetconfigent for %s", nettype);
435 return 1;
436 }
437 }
438 *nfs_xprtp = svc_tli_create(RPC_ANYFD, nfsncp, NULL, 0, 0);
439 if (*nfs_xprtp == NULL) {
440 plog(XLOG_ERROR, "cannot create nfs tli service for amd");
441 return 1;
442 }
443
444 /*
445 * Get the service file descriptor and check its number to see if
446 * the t_open failed. If it succeeded, then go on to binding to a
447 * reserved nfs port.
448 */
449 *soNFSp = (*nfs_xprtp)->xp_fd;
450 if (*soNFSp < 0 || bind_nfs_port(*soNFSp, nfs_portp) < 0) {
451 plog(XLOG_ERROR, "Can't create privileged nfs port (TLI)");
452 svc_destroy(*nfs_xprtp);
453 return 1;
454 }
455 if (svc_reg(*nfs_xprtp, NFS_PROGRAM, nfs_version, dispatch_fxn, NULL) != 1) {
456 plog(XLOG_ERROR, "could not register amd NFS service");
457 svc_destroy(*nfs_xprtp);
458 return 1;
459 }
460
461 return 0; /* all is well */
462 }
463
464
465 /*
466 * Bind to preferred AMQ port.
467 */
468 static int
bind_preferred_amq_port(u_short pref_port,const struct netconfig * ncp,struct t_bind ** tretpp)469 bind_preferred_amq_port(u_short pref_port,
470 const struct netconfig *ncp,
471 struct t_bind **tretpp)
472 {
473 int td = -1, rc = -1;
474 struct t_bind *treq;
475 struct sockaddr_in *sin, *sin2;
476 extern char *t_errlist[];
477 extern int t_errno;
478
479 if (!ncp) {
480 plog(XLOG_ERROR, "null ncp");
481 return -1;
482 }
483
484 td = t_open(ncp->nc_device, O_RDWR, (struct t_info *) NULL);
485 if (td < 0) {
486 plog(XLOG_ERROR, "t_open failed: %d: %s", t_errno, t_errlist[t_errno]);
487 return -1;
488 }
489 treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
490 if (!treq) {
491 plog(XLOG_ERROR, "t_alloc req");
492 return -1;
493 }
494 *tretpp = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR);
495 if (!*tretpp) {
496 t_free((char *) treq, T_BIND);
497 plog(XLOG_ERROR, "t_alloc tretpp");
498 return -1;
499 }
500 memset((char *) treq->addr.buf, 0, treq->addr.len);
501 sin = (struct sockaddr_in *) treq->addr.buf;
502 sin->sin_family = AF_INET;
503 treq->qlen = 64; /* must be greater than 0 to work for TCP connections */
504 treq->addr.len = treq->addr.maxlen;
505
506 if (pref_port > 0) {
507 sin->sin_port = htons(pref_port);
508 sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* XXX: may not be needed */
509 rc = t_bind(td, treq, *tretpp);
510 if (rc < 0) {
511 plog(XLOG_ERROR, "t_bind return err %d", rc);
512 goto out;
513 }
514 /* check if we got the port we asked for */
515 sin2 = (struct sockaddr_in *) (*tretpp)->addr.buf;
516 if (sin->sin_port != sin2->sin_port) {
517 plog(XLOG_ERROR, "asked for port %d, got different one (%d)",
518 ntohs(sin->sin_port), ntohs(sin2->sin_port));
519 t_errno = TNOADDR; /* XXX: is this correct? */
520 rc = -1;
521 goto out;
522 }
523 if (sin->sin_addr.s_addr != sin2->sin_addr.s_addr) {
524 plog(XLOG_ERROR, "asked for address %x, got different one (%x)",
525 (int) ntohl(sin->sin_addr.s_addr), (int) ntohl(sin2->sin_addr.s_addr));
526 t_errno = TNOADDR; /* XXX: is this correct? */
527 rc = -1;
528 goto out;
529 }
530 }
531 out:
532 t_free((char *) treq, T_BIND);
533 return (rc < 0 ? rc : td);
534 }
535
536
537 /*
538 * Create the amq service for amd (both TCP and UDP)
539 */
540 int
create_amq_service(int * udp_soAMQp,SVCXPRT ** udp_amqpp,struct netconfig ** udp_amqncpp,int * tcp_soAMQp,SVCXPRT ** tcp_amqpp,struct netconfig ** tcp_amqncpp,u_short preferred_amq_port)541 create_amq_service(int *udp_soAMQp,
542 SVCXPRT **udp_amqpp,
543 struct netconfig **udp_amqncpp,
544 int *tcp_soAMQp,
545 SVCXPRT **tcp_amqpp,
546 struct netconfig **tcp_amqncpp,
547 u_short preferred_amq_port)
548 {
549 /*
550 * (partially) create the amq service for amd
551 * to be completed further in by caller.
552 * XXX: is this "partially" still true?! See amd/nfs_start.c. -Erez
553 */
554
555 /* first create the TCP service */
556 if (tcp_amqncpp)
557 if ((*tcp_amqncpp = getnetconfigent(NC_TCP)) == NULL) {
558 plog(XLOG_ERROR, "cannot getnetconfigent for %s", NC_TCP);
559 return 1;
560 }
561
562 if (tcp_amqpp) {
563 if (preferred_amq_port > 0) {
564 struct t_bind *tbp = NULL;
565 int sock;
566
567 plog(XLOG_INFO, "requesting preferred amq TCP port %d", preferred_amq_port);
568 sock = bind_preferred_amq_port(preferred_amq_port, *tcp_amqncpp, &tbp);
569 if (sock < 0) {
570 plog(XLOG_ERROR, "bind_preferred_amq_port failed for TCP port %d: %s",
571 preferred_amq_port, t_errlist[t_errno]);
572 return 1;
573 }
574 *tcp_amqpp = svc_tli_create(sock, *tcp_amqncpp, tbp, 0, 0);
575 if (*tcp_amqpp != NULL)
576 plog(XLOG_INFO, "amq service bound to TCP port %d", preferred_amq_port);
577 t_free((char *) tbp, T_BIND);
578 } else {
579 /* select any port */
580 *tcp_amqpp = svc_tli_create(RPC_ANYFD, *tcp_amqncpp, NULL, 0, 0);
581 }
582 if (*tcp_amqpp == NULL) {
583 plog(XLOG_ERROR, "cannot create (tcp) tli service for amq");
584 return 1;
585 }
586 }
587 if (tcp_soAMQp && tcp_amqpp)
588 *tcp_soAMQp = (*tcp_amqpp)->xp_fd;
589
590 /* next create the UDP service */
591 if (udp_amqncpp)
592 if ((*udp_amqncpp = getnetconfigent(NC_UDP)) == NULL) {
593 plog(XLOG_ERROR, "cannot getnetconfigent for %s", NC_UDP);
594 return 1;
595 }
596 if (udp_amqpp) {
597 if (preferred_amq_port > 0) {
598 struct t_bind *tbp = NULL;
599 int sock;
600
601 plog(XLOG_INFO, "requesting preferred amq UDP port %d", preferred_amq_port);
602 sock = bind_preferred_amq_port(preferred_amq_port, *udp_amqncpp, &tbp);
603 if (sock < 0) {
604 plog(XLOG_ERROR, "bind_preferred_amq_port failed for UDP port %d: %s",
605 preferred_amq_port, t_errlist[t_errno]);
606 return 1;
607 }
608 *udp_amqpp = svc_tli_create(sock, *udp_amqncpp, tbp, 0, 0);
609 if (*udp_amqpp != NULL)
610 plog(XLOG_INFO, "amq service bound to UDP port %d", preferred_amq_port);
611 t_free((char *) tbp, T_BIND);
612 } else {
613 /* select any port */
614 *udp_amqpp = svc_tli_create(RPC_ANYFD, *udp_amqncpp, NULL, 0, 0);
615 }
616 if (*udp_amqpp == NULL) {
617 plog(XLOG_ERROR, "cannot create (udp) tli service for amq");
618 return 1;
619 }
620 }
621 if (udp_soAMQp && udp_amqpp)
622 *udp_soAMQp = (*udp_amqpp)->xp_fd;
623
624 return 0; /* all is well */
625 }
626
627
628 /*
629 * Find netconfig info for TCP/UDP device, and fill in the knetconfig
630 * structure. If in_ncp is not NULL, use that instead of defaulting
631 * to a TCP/UDP service. If in_ncp is NULL, then use the service type
632 * specified in nc_protoname (which may be either "tcp" or "udp"). If
633 * nc_protoname is NULL, default to UDP.
634 */
635 int
get_knetconfig(struct knetconfig ** kncpp,struct netconfig * in_ncp,char * nc_protoname)636 get_knetconfig(struct knetconfig **kncpp, struct netconfig *in_ncp, char *nc_protoname)
637 {
638 struct netconfig *ncp = NULL;
639 struct stat statbuf;
640
641 if (in_ncp)
642 ncp = in_ncp;
643 else {
644 if (nc_protoname)
645 ncp = getnetconfigent(nc_protoname);
646 else
647 ncp = getnetconfigent(NC_UDP);
648 }
649 if (!ncp)
650 return -2;
651
652 *kncpp = (struct knetconfig *) xzalloc(sizeof(struct knetconfig));
653 if (*kncpp == (struct knetconfig *) NULL) {
654 if (!in_ncp)
655 freenetconfigent(ncp);
656 return -3;
657 }
658 (*kncpp)->knc_semantics = ncp->nc_semantics;
659 (*kncpp)->knc_protofmly = xstrdup(ncp->nc_protofmly);
660 (*kncpp)->knc_proto = xstrdup(ncp->nc_proto);
661
662 if (stat(ncp->nc_device, &statbuf) < 0) {
663 plog(XLOG_ERROR, "could not stat() %s: %m", ncp->nc_device);
664 XFREE(*kncpp);
665 *kncpp = NULL;
666 if (!in_ncp)
667 freenetconfigent(ncp);
668 return -3; /* amd will end (free not needed) */
669 }
670 (*kncpp)->knc_rdev = (dev_t) statbuf.st_rdev;
671 if (!in_ncp) { /* free only if argument not passed */
672 freenetconfigent(ncp);
673 ncp = NULL;
674 }
675 return 0;
676 }
677
678
679 /*
680 * Free a previously allocated knetconfig structure.
681 */
682 void
free_knetconfig(struct knetconfig * kncp)683 free_knetconfig(struct knetconfig *kncp)
684 {
685 if (kncp) {
686 if (kncp->knc_protofmly)
687 XFREE(kncp->knc_protofmly);
688 if (kncp->knc_proto)
689 XFREE(kncp->knc_proto);
690 XFREE(kncp);
691 kncp = (struct knetconfig *) NULL;
692 }
693 }
694
695
696 /*
697 * Check if the portmapper is running and reachable: 0==down, 1==up
698 */
check_pmap_up(char * host,struct sockaddr_in * sin)699 int check_pmap_up(char *host, struct sockaddr_in* sin)
700 {
701 CLIENT *client;
702 enum clnt_stat clnt_stat = RPC_TIMEDOUT; /* assume failure */
703 int socket = RPC_ANYSOCK;
704 struct timeval timeout;
705
706 timeout.tv_sec = 2;
707 timeout.tv_usec = 0;
708 sin->sin_port = htons(PMAPPORT);
709 client = clntudp_create(sin, PMAPPROG, PMAPVERS, timeout, &socket);
710 if (client == (CLIENT *) NULL) {
711 plog(XLOG_ERROR,
712 "check_pmap_up: cannot create connection to contact portmapper on host \"%s\"%s",
713 host, clnt_spcreateerror(""));
714 return 0;
715 }
716
717 timeout.tv_sec = 6;
718 /* Ping the portmapper on a remote system by calling the nullproc */
719 clnt_stat = clnt_call(client,
720 PMAPPROC_NULL,
721 (XDRPROC_T_TYPE) xdr_void,
722 NULL,
723 (XDRPROC_T_TYPE) xdr_void,
724 NULL,
725 timeout);
726 clnt_destroy(client);
727 close(socket);
728 sin->sin_port = 0;
729
730 if (clnt_stat == RPC_TIMEDOUT) {
731 plog(XLOG_ERROR,
732 "check_pmap_up: failed to contact portmapper on host \"%s\": %s",
733 host, clnt_sperrno(clnt_stat));
734 return 0;
735 }
736 return 1;
737 }
738
739
740 /*
741 * Find the best NFS version for a host.
742 */
743 u_long
get_nfs_version(char * host,struct sockaddr_in * sin,u_long nfs_version,const char * proto,u_long def)744 get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto, u_long def)
745 {
746 CLIENT *clnt = NULL;
747 rpcvers_t versout;
748 struct timeval tv;
749
750 /*
751 * If not set or set wrong, then try from NFS_VERS_MAX on down. If
752 * set, then try from nfs_version on down.
753 */
754 if (!nfs_valid_version(nfs_version))
755 if (nfs_valid_version(def))
756 nfs_version = def;
757 else
758 nfs_version = NFS_VERS_MAX;
759 }
760
761 if (nfs_version == NFS_VERSION) {
762 dlog("get_nfs_version trying NFS(%d,%s) for %s",
763 (int) nfs_version, proto, host);
764 } else {
765 dlog("get_nfs_version trying NFS(%d-%d,%s) for %s",
766 (int) NFS_VERSION, (int) nfs_version, proto, host);
767 }
768
769 /* 3 seconds is more than enough for a LAN */
770 memset(&tv, 0, sizeof(tv));
771 tv.tv_sec = 3;
772 tv.tv_usec = 0;
773
774 #ifdef HAVE_CLNT_CREATE_VERS_TIMED
775 clnt = clnt_create_vers_timed(host, NFS_PROGRAM, &versout, NFS_VERSION, nfs_version, proto, &tv);
776 #else /* not HAVE_CLNT_CREATE_VERS_TIMED */
777 clnt = clnt_create_vers(host, NFS_PROGRAM, &versout, NFS_VERSION, nfs_version, proto);
778 #endif /* not HAVE_CLNT_CREATE_VERS_TIMED */
779
780 if (clnt == NULL) {
781 if (nfs_version == NFS_VERSION)
782 plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s: %s",
783 (int) nfs_version, proto, host, clnt_spcreateerror(""));
784 else
785 plog(XLOG_INFO, "get_nfs_version NFS(%d-%d,%s) failed for %s: %s",
786 (int) NFS_VERSION, (int) nfs_version, proto, host, clnt_spcreateerror(""));
787 return 0;
788 }
789 clnt_destroy(clnt);
790
791 return versout;
792 }
793
794
795 #if defined(HAVE_FS_AUTOFS) && defined(AUTOFS_PROG)
796 /*
797 * find the IP address that can be used to connect autofs service to.
798 */
799 static int
800 get_autofs_address(struct netconfig *ncp, struct t_bind *tbp)
801 {
802 int ret;
803 struct nd_addrlist *addrs = (struct nd_addrlist *) NULL;
804 struct nd_hostserv service;
805
806 service.h_host = HOST_SELF_CONNECT;
807 service.h_serv = "autofs";
808
809 ret = netdir_getbyname(ncp, &service, &addrs);
810
811 if (ret) {
812 plog(XLOG_FATAL, "get_autofs_address: cannot get local host address: %s", netdir_sperror());
813 goto out;
814 }
815
816 /*
817 * XXX: there may be more more than one address for this local
818 * host. Maybe something can be done with those.
819 */
820 tbp->addr.len = addrs->n_addrs->len;
821 tbp->addr.maxlen = addrs->n_addrs->len;
822 memcpy(tbp->addr.buf, addrs->n_addrs->buf, addrs->n_addrs->len);
823 /*
824 * qlen should not be zero for TCP connections. It's not clear what it
825 * should be for UDP connections, but setting it to something like 64 seems
826 * to be the safe value that works.
827 */
828 tbp->qlen = 64;
829
830 /* all OK */
831 netdir_free((voidp) addrs, ND_ADDRLIST);
832
833 out:
834 return ret;
835 }
836
837
838 /*
839 * Register the autofs service for amd
840 */
841 int
842 register_autofs_service(char *autofs_conftype,
843 void (*autofs_dispatch)(struct svc_req *rqstp, SVCXPRT *xprt))
844 {
845 struct t_bind *tbp = NULL;
846 struct netconfig *autofs_ncp;
847 SVCXPRT *autofs_xprt = NULL;
848 int fd = -1, err = 1; /* assume failed */
849
850 plog(XLOG_INFO, "registering autofs service: %s", autofs_conftype);
851 autofs_ncp = getnetconfigent(autofs_conftype);
852 if (autofs_ncp == NULL) {
853 plog(XLOG_ERROR, "register_autofs_service: cannot getnetconfigent for %s", autofs_conftype);
854 goto out;
855 }
856
857 fd = t_open(autofs_ncp->nc_device, O_RDWR, NULL);
858 if (fd < 0) {
859 plog(XLOG_ERROR, "register_autofs_service: t_open failed (%s)",
860 t_errlist[t_errno]);
861 goto out;
862 }
863
864 tbp = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR);
865 if (!tbp) {
866 plog(XLOG_ERROR, "register_autofs_service: t_alloc failed");
867 goto out;
868 }
869
870 if (get_autofs_address(autofs_ncp, tbp) != 0) {
871 plog(XLOG_ERROR, "register_autofs_service: get_autofs_address failed");
872 goto out;
873 }
874
875 autofs_xprt = svc_tli_create(fd, autofs_ncp, tbp, 0, 0);
876 if (autofs_xprt == NULL) {
877 plog(XLOG_ERROR, "cannot create autofs tli service for amd");
878 goto out;
879 }
880
881 rpcb_unset(AUTOFS_PROG, AUTOFS_VERS, autofs_ncp);
882 if (svc_reg(autofs_xprt, AUTOFS_PROG, AUTOFS_VERS, autofs_dispatch, autofs_ncp) == FALSE) {
883 plog(XLOG_ERROR, "could not register amd AUTOFS service");
884 goto out;
885 }
886 err = 0;
887 goto really_out;
888
889 out:
890 if (autofs_ncp)
891 freenetconfigent(autofs_ncp);
892 if (autofs_xprt)
893 SVC_DESTROY(autofs_xprt);
894 else {
895 if (fd > 0)
896 t_close(fd);
897 }
898
899 really_out:
900 if (tbp)
901 t_free((char *) tbp, T_BIND);
902
903 dlog("register_autofs_service: returning %d\n", err);
904 return err;
905 }
906
907
908 int
909 unregister_autofs_service(char *autofs_conftype)
910 {
911 struct netconfig *autofs_ncp;
912 int err = 1;
913
914 plog(XLOG_INFO, "unregistering autofs service listener: %s", autofs_conftype);
915
916 autofs_ncp = getnetconfigent(autofs_conftype);
917 if (autofs_ncp == NULL) {
918 plog(XLOG_ERROR, "destroy_autofs_service: cannot getnetconfigent for %s", autofs_conftype);
919 goto out;
920 }
921
922 out:
923 rpcb_unset(AUTOFS_PROG, AUTOFS_VERS, autofs_ncp);
924 return err;
925 }
926 #endif /* HAVE_FS_AUTOFS && AUTOFS_PROG */
927