]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/amd/conf/transp/transp_sockets.c
This commit was generated by cvs2svn to compensate for changes in r135471,
[FreeBSD/FreeBSD.git] / contrib / amd / conf / transp / transp_sockets.c
1 /*
2  * Copyright (c) 1997-2004 Erez Zadok
3  * Copyright (c) 1990 Jan-Simon Pendry
4  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgment:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *      %W% (Berkeley) %G%
40  *
41  * $Id: transp_sockets.c,v 1.6.2.11 2004/01/06 03:15:20 ezk Exp $
42  *
43  * Socket specific utilities.
44  *      -Erez Zadok <ezk@cs.columbia.edu>
45  */
46
47 #ifdef HAVE_CONFIG_H
48 # include <config.h>
49 #endif /* HAVE_CONFIG_H */
50 #include <am_defs.h>
51 #include <amu.h>
52
53
54 /*
55  * find the IP address that can be used to connect to the local host
56  */
57 void
58 amu_get_myaddress(struct in_addr *iap)
59 {
60   struct sockaddr_in sin;
61
62   memset((char *) &sin, 0, sizeof(sin));
63   get_myaddress(&sin);
64   iap->s_addr = sin.sin_addr.s_addr;
65 }
66
67
68 /*
69  * How to bind to reserved ports.
70  */
71 int
72 bind_resv_port(int so, u_short *pp)
73 {
74   struct sockaddr_in sin;
75   int rc;
76   u_short port;
77
78   memset((voidp) &sin, 0, sizeof(sin));
79   sin.sin_family = AF_INET;
80
81   port = IPPORT_RESERVED;
82
83   do {
84     --port;
85     sin.sin_port = htons(port);
86     rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
87   } while (rc < 0 && (int) port > IPPORT_RESERVED / 2);
88
89   if (pp && rc == 0)
90     *pp = port;
91
92   return rc;
93 }
94
95
96 /*
97  * close a descriptor, Sockets style
98  */
99 int
100 amu_close(int fd)
101 {
102   return close(fd);
103 }
104
105
106 /*
107  * Create an rpc client attached to the mount daemon.
108  */
109 CLIENT *
110 get_mount_client(char *unused_host, struct sockaddr_in *sin, struct timeval *tv, int *sock, u_long mnt_version)
111 {
112   CLIENT *client;
113
114   /*
115    * First try a TCP socket
116    */
117   if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0) {
118     /*
119      * Bind to a privileged port
120      */
121     if (bind_resv_port(*sock, (u_short *) 0) < 0)
122       plog(XLOG_ERROR, "can't bind privileged port (socket)");
123
124     /*
125      * Find mountd port to connect to.
126      * Connect to mountd.
127      * Create a tcp client.
128      */
129     if ((sin->sin_port = htons(pmap_getport(sin, MOUNTPROG, mnt_version, IPPROTO_TCP))) != 0) {
130       if (connect(*sock, (struct sockaddr *) sin, sizeof(*sin)) >= 0
131           && ((client = clnttcp_create(sin, MOUNTPROG, mnt_version, sock, 0, 0)) != NULL))
132         return client;
133     }
134     /*
135      * Failed so close socket
136      */
137     (void) close(*sock);
138   }                             /* tcp socket opened */
139   /* TCP failed so try UDP */
140   if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
141     plog(XLOG_ERROR, "Can't create socket to connect to mountd: %m");
142     *sock = RPC_ANYSOCK;
143     return NULL;
144   }
145   /*
146    * Bind to a privileged port
147    */
148   if (bind_resv_port(*sock, (u_short *) 0) < 0)
149     plog(XLOG_ERROR, "can't bind privileged port");
150
151   /*
152    * Zero out the port - make sure we recompute
153    */
154   sin->sin_port = 0;
155
156   /*
157    * Make a UDP client
158    */
159   if ((client = clntudp_create(sin, MOUNTPROG, mnt_version, *tv, sock)) == NULL) {
160     (void) close(*sock);
161     *sock = RPC_ANYSOCK;
162     return NULL;
163   }
164 #ifdef DEBUG
165   dlog("get_mount_client: Using udp, port %d", sin->sin_port);
166 #endif /* DEBUG */
167   return client;
168 }
169
170
171 /*
172  * find the address of the caller of an RPC procedure.
173  */
174 struct sockaddr_in *
175 amu_svc_getcaller(SVCXPRT *xprt)
176 {
177   /* glibc 2.2 returns a sockaddr_storage ??? */
178   return (struct sockaddr_in *)svc_getcaller(xprt);
179 }
180
181
182 /*
183  * Create the nfs service for amd
184  */
185 int
186 create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp))
187 {
188
189   *soNFSp = socket(AF_INET, SOCK_DGRAM, 0);
190
191   if (*soNFSp < 0 || bind_resv_port(*soNFSp, NULL) < 0) {
192     plog(XLOG_FATAL, "Can't create privileged nfs port (socket)");
193     return 1;
194   }
195   if ((*nfs_xprtp = svcudp_create(*soNFSp)) == NULL) {
196     plog(XLOG_FATAL, "cannot create rpc/udp service");
197     return 2;
198   }
199   if ((*nfs_portp = (*nfs_xprtp)->xp_port) >= IPPORT_RESERVED) {
200     plog(XLOG_FATAL, "Can't create privileged nfs port");
201     return 1;
202   }
203   if (!svc_register(*nfs_xprtp, NFS_PROGRAM, NFS_VERSION, dispatch_fxn, 0)) {
204     plog(XLOG_FATAL, "unable to register (%ld, %ld, 0)",
205          (u_long) NFS_PROGRAM, (u_long) NFS_VERSION);
206     return 3;
207   }
208
209   return 0;                     /* all is well */
210 }
211
212
213 /*
214  * Create the amq service for amd (both TCP and UDP)
215  */
216 int
217 create_amq_service(int *udp_soAMQp, SVCXPRT **udp_amqpp, int *tcp_soAMQp, SVCXPRT **tcp_amqpp)
218 {
219   /* first create TCP service */
220   if (tcp_soAMQp) {
221     *tcp_soAMQp = socket(AF_INET, SOCK_STREAM, 0);
222     if (*tcp_soAMQp < 0) {
223       plog(XLOG_FATAL, "cannot create tcp socket for amq service: %m");
224       return 1;
225     }
226
227     /* now create RPC service handle for amq */
228     if (tcp_amqpp &&
229         (*tcp_amqpp = svctcp_create(*tcp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
230       plog(XLOG_FATAL, "cannot create tcp service for amq: soAMQp=%d", *tcp_soAMQp);
231       return 2;
232     }
233
234 #ifdef SVCSET_CONNMAXREC
235     /*
236      * This is *BSD at its best.
237      * They just had to do things differently than everyone else
238      * so they fixed a library DoS issue by forcing client-side changes...
239      */
240 # ifndef RPC_MAXDATASIZE
241 #  define RPC_MAXDATASIZE 9000
242 # endif /* not RPC_MAXDATASIZE */
243     {
244       int maxrec = RPC_MAXDATASIZE;
245       SVC_CONTROL(*tcp_amqpp, SVCSET_CONNMAXREC, &maxrec);
246     }
247 #endif /* not SVCSET_CONNMAXREC */
248   }
249
250   /* next create UDP service */
251   if (udp_soAMQp) {
252     *udp_soAMQp = socket(AF_INET, SOCK_DGRAM, 0);
253     if (*udp_soAMQp < 0) {
254       plog(XLOG_FATAL, "cannot create udp socket for amq service: %m");
255       return 3;
256     }
257
258     /* now create RPC service handle for amq */
259     if (udp_amqpp &&
260         (*udp_amqpp = svcudp_bufcreate(*udp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
261       plog(XLOG_FATAL, "cannot create udp service for amq: soAMQp=%d", *udp_soAMQp);
262       return 4;
263     }
264   }
265
266   return 0;                     /* all is well */
267 }
268
269
270 /*
271  * Ping the portmapper on a remote system by calling the nullproc
272  */
273 enum clnt_stat
274 pmap_ping(struct sockaddr_in *address)
275 {
276   CLIENT *client;
277   enum clnt_stat clnt_stat = RPC_TIMEDOUT; /* assume failure */
278   int socket = RPC_ANYSOCK;
279   struct timeval timeout;
280
281   timeout.tv_sec = 3;
282   timeout.tv_usec = 0;
283   address->sin_port = htons(PMAPPORT);
284   client = clntudp_create(address, PMAPPROG, PMAPVERS, timeout, &socket);
285   if (client != (CLIENT *) NULL) {
286     clnt_stat = clnt_call(client,
287                           PMAPPROC_NULL,
288                           (XDRPROC_T_TYPE) xdr_void,
289                           NULL,
290                           (XDRPROC_T_TYPE) xdr_void,
291                           NULL,
292                           timeout);
293     clnt_destroy(client);
294   }
295   close(socket);
296   address->sin_port = 0;
297
298   return clnt_stat;
299 }
300
301
302 /*
303  * Find the best NFS version for a host and protocol.
304  */
305 u_long
306 get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto)
307 {
308   CLIENT *clnt;
309   int again = 0;
310   enum clnt_stat clnt_stat;
311   struct timeval tv;
312   int sock;
313
314   /*
315    * If not set or set wrong, then try from NFS_VERS_MAX on down. If
316    * set, then try from nfs_version on down.
317    */
318   if (nfs_version <= 0 || nfs_version > NFS_VERS_MAX) {
319     nfs_version = NFS_VERS_MAX;
320     again = 1;
321   }
322   tv.tv_sec = 3;                /* retry every 3 seconds, but also timeout */
323   tv.tv_usec = 0;
324
325   /*
326    * First check if remote portmapper is up (verify if remote host is up).
327    */
328   clnt_stat = pmap_ping(sin);
329   if (clnt_stat == RPC_TIMEDOUT) {
330     plog(XLOG_ERROR, "get_nfs_version: failed to contact portmapper on host \"%s\": %s", host, clnt_sperrno(clnt_stat));
331     return 0;
332   }
333
334 #ifdef HAVE_FS_NFS3
335 try_again:
336 #endif /* HAVE_FS_NFS3 */
337
338   sock = RPC_ANYSOCK;
339   if (STREQ(proto, "tcp"))
340     clnt = clnttcp_create(sin, NFS_PROGRAM, nfs_version, &sock, 0, 0);
341   else if (STREQ(proto, "udp"))
342     clnt = clntudp_create(sin, NFS_PROGRAM, nfs_version, tv, &sock);
343   else
344     clnt = NULL;
345
346   if (clnt == NULL) {
347 #ifdef HAVE_CLNT_SPCREATEERROR
348     plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s: %s",
349          (int) nfs_version, proto, host, clnt_spcreateerror(""));
350 #else /* not HAVE_CLNT_SPCREATEERROR */
351     plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s",
352          (int) nfs_version, proto, host);
353 #endif /* not HAVE_CLNT_SPCREATEERROR */
354     return 0;
355   }
356
357   /* Try a couple times to verify the CLIENT handle. */
358   tv.tv_sec = 6;
359   clnt_stat = clnt_call(clnt,
360                         NFSPROC_NULL,
361                         (XDRPROC_T_TYPE) xdr_void,
362                         0,
363                         (XDRPROC_T_TYPE) xdr_void,
364                         0,
365                         tv);
366   close(sock);
367   clnt_destroy(clnt);
368   if (clnt_stat != RPC_SUCCESS) {
369     if (again) {
370 #ifdef HAVE_FS_NFS3
371       if (nfs_version == NFS_VERSION3) {
372         plog(XLOG_INFO, "get_nfs_version trying a lower version");
373         nfs_version = NFS_VERSION;
374         again = 0;
375       }
376       goto try_again;
377 #endif /* HAVE_FS_NFS3 */
378     }
379     plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s",
380          (int) nfs_version, proto, host);
381     return 0;
382   }
383
384   plog(XLOG_INFO, "get_nfs_version: returning (%d,%s) on host %s",
385        (int) nfs_version, proto, host);
386   return nfs_version;
387 }