transp_sockets.c revision 119679
1/* 2 * Copyright (c) 1997-2003 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.10 2003/04/23 14:27:34 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