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