transp_sockets.c revision 131702
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 */
57void
58amu_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 */
71int
72bind_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 */
99int
100amu_close(int fd)
101{
102  return close(fd);
103}
104
105
106/*
107 * Create an rpc client attached to the mount daemon.
108 */
109CLIENT *
110get_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 */
174struct sockaddr_in *
175amu_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 */
185int
186create_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 */
216int
217create_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 */
273enum clnt_stat
274pmap_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 */
305u_long
306get_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
335try_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}
388