]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/amd/amd/rpc_fwd.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / amd / amd / rpc_fwd.c
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  */
67 typedef struct rpc_forward rpc_forward;
68 struct 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  */
81 qelem rpc_head = {&rpc_head, &rpc_head};
82 int fwd_sock;
83 static u_int xid;
84
85
86 /*
87  * Allocate a rely structure
88  */
89 static rpc_forward *
90 fwd_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  */
139 static void
140 fwd_free(rpc_forward *p)
141 {
142   rem_que(&p->rf_q);
143   XFREE(p);
144 }
145
146
147 /*
148  * Initialize the RPC forwarder
149  */
150 int
151 fwd_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  */
201 static rpc_forward *
202 fwd_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  */
223 int
224 fwd_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    */
337 out:
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  */
352 void
353 fwd_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    */
376 again:
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
473 out:;
474 }