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