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