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