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