rpc_fwd.c revision 119679
1/* 2 * Copyright (c) 1997-2003 Erez Zadok 3 * Copyright (c) 1989 Jan-Simon Pendry 4 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 5 * Copyright (c) 1989 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: rpc_fwd.c,v 1.3.2.4 2002/12/27 22:44:44 ezk Exp $ 42 * 43 */ 44 45/* 46 * RPC packet forwarding 47 */ 48 49#ifdef HAVE_CONFIG_H 50# include <config.h> 51#endif /* HAVE_CONFIG_H */ 52#include <am_defs.h> 53#include <amd.h> 54 55/* 56 * Note that the ID field in the external packet is only 57 * ever treated as a 32 bit opaque data object, so there 58 * is no need to convert to and from network byte ordering. 59 */ 60 61#define XID_ALLOC(struct ) (xid++) 62#define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */ 63 64/* 65 * Each pending reply has an rpc_forward structure 66 * associated with it. These have a 15 second lifespan. 67 * If a new structure is required, then an expired 68 * one will be re-allocated if available, otherwise a fresh 69 * one is allocated. Whenever a reply is received the 70 * structure is discarded. 71 */ 72typedef struct rpc_forward rpc_forward; 73struct rpc_forward { 74 qelem rf_q; /* Linked list */ 75 time_t rf_ttl; /* Time to live */ 76 u_int rf_xid; /* Packet id */ 77 u_int rf_oldid; /* Original packet id */ 78 fwd_fun rf_fwd; /* Forwarding function */ 79 voidp rf_ptr; 80 struct sockaddr_in rf_sin; 81}; 82 83/* 84 * Head of list of pending replies 85 */ 86qelem rpc_head = {&rpc_head, &rpc_head}; 87int fwd_sock; 88static u_int xid; 89 90 91/* 92 * Allocate a rely structure 93 */ 94static rpc_forward * 95fwd_alloc(void) 96{ 97 time_t now = clocktime(); 98 rpc_forward *p = 0, *p2; 99 100 /* 101 * First search for an existing expired one. 102 */ 103 ITER(p2, rpc_forward, &rpc_head) { 104 if (p2->rf_ttl <= now) { 105 p = p2; 106 break; 107 } 108 } 109 110 /* 111 * If one couldn't be found then allocate 112 * a new structure and link it at the 113 * head of the list. 114 */ 115 if (p) { 116 /* 117 * Call forwarding function to say that 118 * this message was junked. 119 */ 120#ifdef DEBUG 121 dlog("Re-using packet forwarding slot - id %#x", p->rf_xid); 122#endif /* DEBUG */ 123 if (p->rf_fwd) 124 (*p->rf_fwd) (0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE); 125 rem_que(&p->rf_q); 126 } else { 127 p = ALLOC(struct rpc_forward); 128 } 129 ins_que(&p->rf_q, &rpc_head); 130 131 /* 132 * Set the time to live field 133 * Timeout in 43 seconds 134 */ 135 p->rf_ttl = now + 43; 136 137 return p; 138} 139 140 141/* 142 * Free an allocated reply structure. 143 * First unlink it from the list, then 144 * discard it. 145 */ 146static void 147fwd_free(rpc_forward *p) 148{ 149 rem_que(&p->rf_q); 150 XFREE(p); 151} 152 153 154/* 155 * Initialize the RPC forwarder 156 */ 157int 158fwd_init(void) 159{ 160#ifdef FIONBIO 161 int on = 1; 162#endif /* FIONBIO */ 163 164#ifdef HAVE_TRANSPORT_TYPE_TLI 165 /* 166 * Create ping TLI socket (/dev/tcp and /dev/ticlts did not work) 167 * (HPUX-11 does not like using O_NDELAY in flags) 168 */ 169 fwd_sock = t_open("/dev/udp", O_RDWR|O_NONBLOCK, 0); 170 if (fwd_sock < 0) { 171 plog(XLOG_ERROR, "unable to create RPC forwarding TLI socket: %s", 172 t_errlist[t_errno]); 173 return errno; 174 } 175#else /* not HAVE_TRANSPORT_TYPE_TLI */ 176 /* 177 * Create ping socket 178 */ 179 fwd_sock = socket(AF_INET, SOCK_DGRAM, 0); 180 if (fwd_sock < 0) { 181 plog(XLOG_ERROR, "unable to create RPC forwarding socket: %m"); 182 return errno; 183 } 184#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 185 186 /* 187 * Some things we talk to require a priv port - so make one here 188 */ 189 if (bind_resv_port(fwd_sock, (u_short *) 0) < 0) 190 plog(XLOG_ERROR, "can't bind privileged port (rpc_fwd)"); 191 192 if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 193#ifdef FIONBIO 194 && ioctl(fwd_sock, FIONBIO, &on) < 0 195#endif /* FIONBIO */ 196 ) { 197 plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m"); 198 return errno; 199 } 200 201 return 0; 202} 203 204 205/* 206 * Locate a packet in the forwarding list 207 */ 208static rpc_forward * 209fwd_locate(u_int id) 210{ 211 rpc_forward *p; 212 213 ITER(p, rpc_forward, &rpc_head) { 214 if (p->rf_xid == id) 215 return p; 216 } 217 218 return 0; 219} 220 221 222/* 223 * This is called to forward a packet to another 224 * RPC server. The message id is changed and noted 225 * so that when a reply appears we can tie it up 226 * correctly. Just matching the reply's source address 227 * would not work because it might come from a 228 * different address. 229 */ 230int 231fwd_packet(int type_id, voidp pkt, int len, struct sockaddr_in *fwdto, struct sockaddr_in *replyto, voidp i, fwd_fun cb) 232{ 233 rpc_forward *p; 234 u_int *pkt_int; 235 int error; 236#ifdef HAVE_TRANSPORT_TYPE_TLI 237 struct t_unitdata ud; 238#endif /* HAVE_TRANSPORT_TYPE_TLI */ 239 240 if ((int) amd_state >= (int) Finishing) 241 return ENOENT; 242 243 /* 244 * See if the type_id is fully specified. 245 * If so, then discard any old entries 246 * for this id. 247 * Otherwise make sure the type_id is 248 * fully qualified by allocating an id here. 249 */ 250#ifdef DEBUG 251 switch (type_id & RPC_XID_MASK) { 252 case RPC_XID_PORTMAP: 253 dlog("Sending PORTMAP request"); 254 break; 255 case RPC_XID_MOUNTD: 256 dlog("Sending MOUNTD request %#x", type_id); 257 break; 258 case RPC_XID_NFSPING: 259 dlog("Sending NFS ping"); 260 break; 261 default: 262 dlog("UNKNOWN RPC XID"); 263 break; 264 } 265#endif /* DEBUG */ 266 267 if (type_id & ~RPC_XID_MASK) { 268 p = fwd_locate(type_id); 269 if (p) { 270#ifdef DEBUG 271 dlog("Discarding earlier rpc fwd handle"); 272#endif /* DEBUG */ 273 fwd_free(p); 274 } 275 } else { 276#ifdef DEBUG 277 dlog("Allocating a new xid..."); 278#endif /* DEBUG */ 279 type_id = MK_RPC_XID(type_id, XID_ALLOC(struct )); 280 } 281 282 p = fwd_alloc(); 283 if (!p) 284 return ENOBUFS; 285 286 error = 0; 287 288 pkt_int = (u_int *) pkt; 289 290 /* 291 * Get the original packet id 292 */ 293 p->rf_oldid = *pkt_int; 294 295 /* 296 * Replace with newly allocated id 297 */ 298 p->rf_xid = *pkt_int = type_id; 299 300 /* 301 * The sendto may fail if, for example, the route 302 * to a remote host is lost because an intermediate 303 * gateway has gone down. Important to fill in the 304 * rest of "p" otherwise nasty things happen later... 305 */ 306#ifdef DEBUG 307 { 308 char dq[20]; 309 if (p && fwdto) 310 dlog("Sending packet id %#x to %s.%d", 311 p->rf_xid, 312 inet_dquad(dq, fwdto->sin_addr.s_addr), 313 ntohs(fwdto->sin_port)); 314 } 315#endif /* DEBUG */ 316 317 /* if NULL, remote server probably down */ 318 if (!fwdto) { 319 error = AM_ERRNO_HOST_DOWN; 320 goto out; 321 } 322 323#ifdef HAVE_TRANSPORT_TYPE_TLI 324 ud.addr.buf = (char *) fwdto; 325 if (fwdto) /* if NULL, set sizes to zero */ 326 ud.addr.maxlen = ud.addr.len = sizeof(struct sockaddr_in); 327 else 328 ud.addr.maxlen = ud.addr.len = 0; 329 ud.opt.buf = (char *) NULL; 330 ud.opt.maxlen = ud.opt.len = 0; 331 ud.udata.buf = pkt; 332 ud.udata.maxlen = ud.udata.len = len; 333 if (t_sndudata(fwd_sock, &ud) < 0) { 334 plog(XLOG_ERROR,"fwd_packet failed: t_errno=%d, errno=%d",t_errno,errno); 335 error = errno; 336 } 337#else /* not HAVE_TRANSPORT_TYPE_TLI */ 338 if (sendto(fwd_sock, (char *) pkt, len, 0, 339 (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0) 340 error = errno; 341#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 342 343 /* 344 * Save callback function and return address 345 */ 346out: 347 p->rf_fwd = cb; 348 if (replyto) 349 p->rf_sin = *replyto; 350 else 351 memset((voidp) &p->rf_sin, 0, sizeof(p->rf_sin)); 352 p->rf_ptr = i; 353 354 return error; 355} 356 357 358/* 359 * Called when some data arrives on the forwarding socket 360 */ 361void 362fwd_reply(void) 363{ 364 int len; 365 u_int pkt[MAX_PACKET_SIZE / sizeof(u_int) + 1]; 366 u_int *pkt_int; 367 int rc; 368 rpc_forward *p; 369 struct sockaddr_in src_addr; 370 RECVFROM_FROMLEN_TYPE src_addr_len; 371#ifdef HAVE_TRANSPORT_TYPE_TLI 372 struct t_unitdata ud; 373 int flags = 0; 374#endif /* HAVE_TRANSPORT_TYPE_TLI */ 375 376 /* 377 * Determine the length of the packet 378 */ 379 len = MAX_PACKET_SIZE; 380 381 /* 382 * Read the packet and check for validity 383 */ 384again: 385 src_addr_len = sizeof(src_addr); 386#ifdef HAVE_TRANSPORT_TYPE_TLI 387 ud.addr.buf = (char *) &src_addr; 388 ud.addr.maxlen = ud.addr.len = src_addr_len; 389 ud.opt.buf = (char *) NULL; 390 ud.opt.maxlen = ud.opt.len = 0; 391 ud.udata.buf = (char *) pkt; 392 ud.udata.maxlen = ud.udata.len = len; 393 /* XXX: use flags accordingly such as if T_MORE set */ 394 rc = t_rcvudata(fwd_sock, &ud, &flags); 395 if (rc == 0) /* success, reset rc to length */ 396 rc = ud.udata.len; 397 else { 398 plog(XLOG_ERROR,"fwd_reply failed: t_errno=%d, errno=%d, flags=%d",t_errno,errno, flags); 399 } 400#else /* not HAVE_TRANSPORT_TYPE_TLI */ 401 rc = recvfrom(fwd_sock, 402 (char *) pkt, 403 len, 404 0, 405 (struct sockaddr *) &src_addr, 406 &src_addr_len); 407#endif /* not HAVE_TRANSPORT_TYPE_TLI */ 408 409 /* 410 * XXX: in svr4, if the T_MORE bit of flags is set, what do 411 * we then do? -Erez 412 */ 413 if (rc < 0 || src_addr_len != sizeof(src_addr) || 414 src_addr.sin_family != AF_INET) { 415 if (rc < 0 && errno == EINTR) 416 goto again; 417 plog(XLOG_ERROR, "Error reading RPC reply: %m"); 418 goto out; 419 } 420 421 /* 422 * Do no more work if finishing soon 423 */ 424 if ((int) amd_state >= (int) Finishing) 425 goto out; 426 427 /* 428 * Find packet reference 429 */ 430 pkt_int = (u_int *) pkt; 431 432#ifdef DEBUG 433 switch (*pkt_int & RPC_XID_MASK) { 434 case RPC_XID_PORTMAP: 435 dlog("Receiving PORTMAP reply"); 436 break; 437 case RPC_XID_MOUNTD: 438 dlog("Receiving MOUNTD reply %#x", *pkt_int); 439 break; 440 case RPC_XID_NFSPING: 441 dlog("Receiving NFS ping %#x", *pkt_int); 442 break; 443 default: 444 dlog("UNKNOWN RPC XID"); 445 break; 446 } 447#endif /* DEBUG */ 448 449 p = fwd_locate(*pkt_int); 450 if (!p) { 451#ifdef DEBUG 452 dlog("Can't forward reply id %#x", *pkt_int); 453#endif /* DEBUG */ 454 goto out; 455 } 456 457 if (p->rf_fwd) { 458 /* 459 * Put the original message id back 460 * into the packet. 461 */ 462 *pkt_int = p->rf_oldid; 463 464 /* 465 * Call forwarding function 466 */ 467 (*p->rf_fwd) ((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE); 468 } 469 470 /* 471 * Free forwarding info 472 */ 473 fwd_free(p); 474 475out:; 476} 477