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