1 /*
2 * Copyright (c) 1989 Jan-Simon Pendry
3 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Jan-Simon Pendry at Imperial College, London.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * from: @(#)rpc_fwd.c 8.1 (Berkeley) 6/6/93
35 * $Id: rpc_fwd.c,v 1.11 2019/06/28 13:32:46 deraadt Exp $
36 */
37
38 /*
39 * RPC packet forwarding
40 */
41
42 #include "am.h"
43 #include <sys/ioctl.h>
44
45 /*
46 * Note that the ID field in the external packet is only
47 * ever treated as a 32 bit opaque data object, so there
48 * is no need to convert to and from network byte ordering.
49 */
50
51 /*
52 * Each pending reply has an rpc_forward structure
53 * associated with it. These have a 15 second lifespan.
54 * If a new structure is required, then an expired
55 * one will be re-allocated if available, otherwise a fresh
56 * one is allocated. Whenever a reply is received the
57 * structure is discarded.
58 */
59 typedef struct rpc_forward rpc_forward;
60 struct rpc_forward {
61 qelem rf_q; /* Linked list */
62 time_t rf_ttl; /* Time to live */
63 u_int rf_xid; /* Packet id */
64 u_int rf_oldid; /* Original packet id */
65 fwd_fun rf_fwd; /* Forwarding function */
66 void * rf_ptr;
67 struct sockaddr_in rf_sin;
68 };
69
70 /*
71 * Head of list of pending replies
72 */
73 extern qelem rpc_head;
74 qelem rpc_head = { &rpc_head, &rpc_head };
75
76 static u_int xid;
77 #define XID_ALLOC() (xid++)
78
79 #define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */
80
81 int fwd_sock;
82
83 /*
84 * Allocate a rely structure
85 */
86 static rpc_forward *
fwd_alloc()87 fwd_alloc()
88 {
89 time_t now = clocktime();
90 rpc_forward *p = 0, *p2;
91
92 #ifdef DEBUG
93 /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
94 #endif /* DEBUG */
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 #ifdef DEBUG
116 dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
117 #endif /* DEBUG */
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(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 #ifdef DEBUG
133 /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
134 #endif /* DEBUG */
135 return p;
136 }
137
138 /*
139 * Free an allocated reply structure.
140 * First unlink it from the list, then
141 * discard it.
142 */
143 static void
fwd_free(rpc_forward * p)144 fwd_free(rpc_forward *p)
145 {
146 #ifdef DEBUG
147 /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
148 #endif /* DEBUG */
149 rem_que(&p->rf_q);
150 #ifdef DEBUG
151 /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
152 #endif /* DEBUG */
153 free(p);
154 }
155
156 /*
157 * Initialise the RPC forwarder
158 */
fwd_init()159 int fwd_init()
160 {
161 /*
162 * Create ping socket
163 */
164 fwd_sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
165 if (fwd_sock == -1) {
166 plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m");
167 return errno;
168 }
169
170 /*
171 * Some things we talk to require a priv port - so make one here
172 */
173 if (bind_resv_port(fwd_sock, (unsigned short *) 0) == -1)
174 plog(XLOG_ERROR, "can't bind privileged port");
175
176 return 0;
177 }
178
179 /*
180 * Locate a packet in the forwarding list
181 */
182 static rpc_forward *
fwd_locate(u_int id)183 fwd_locate(u_int id)
184 {
185 rpc_forward *p;
186
187 ITER(p, rpc_forward, &rpc_head) {
188 if (p->rf_xid == id)
189 return p;
190 }
191
192 return 0;
193 }
194
195 /*
196 * This is called to forward a packet to another
197 * RPC server. The message id is changed and noted
198 * so that when a reply appears we can tie it up
199 * correctly. Just matching the reply's source address
200 * would not work because it might come from a
201 * different address.
202 */
203 int
fwd_packet(int type_id,void * pkt,int len,struct sockaddr_in * fwdto,struct sockaddr_in * replyto,void * i,fwd_fun cb)204 fwd_packet(int type_id, void *pkt, int len, struct sockaddr_in *fwdto,
205 struct sockaddr_in *replyto, void *i, fwd_fun cb)
206 {
207 rpc_forward *p;
208 u_int *pkt_int;
209 int error;
210
211 if ((int)amd_state >= (int)Finishing)
212 return ENOENT;
213
214 /*
215 * See if the type_id is fully specified.
216 * If so, then discard any old entries
217 * for this id.
218 * Otherwise make sure the type_id is
219 * fully qualified by allocating an id here.
220 */
221 #ifdef DEBUG
222 switch (type_id & RPC_XID_MASK) {
223 case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break;
224 case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break;
225 case RPC_XID_NFSPING: dlog("Sending NFS ping"); break;
226 default: dlog("UNKNOWN RPC XID"); break;
227 }
228 #endif /* DEBUG */
229
230 if (type_id & ~RPC_XID_MASK) {
231 #ifdef DEBUG
232 /*dlog("Fully qualified rpc type provided");*/
233 #endif /* DEBUG */
234 p = fwd_locate(type_id);
235 if (p) {
236 #ifdef DEBUG
237 dlog("Discarding earlier rpc fwd handle");
238 #endif /* DEBUG */
239 fwd_free(p);
240 }
241 } else {
242 #ifdef DEBUG
243 dlog("Allocating a new xid...");
244 #endif /* DEBUG */
245 type_id = MK_RPC_XID(type_id, XID_ALLOC());
246 }
247
248 p = fwd_alloc();
249 if (!p)
250 return ENOBUFS;
251
252 error = 0;
253
254 pkt_int = (u_int *) pkt;
255
256 /*
257 * Get the original packet id
258 */
259 p->rf_oldid = *pkt_int;
260
261 /*
262 * Replace with newly allocated id
263 */
264 p->rf_xid = *pkt_int = type_id;
265
266 /*
267 * The sendto may fail if, for example, the route
268 * to a remote host is lost because an intermediate
269 * gateway has gone down. Important to fill in the
270 * rest of "p" otherwise nasty things happen later...
271 */
272 #ifdef DEBUG
273 { char dq[20];
274
275 dlog("Sending packet id %#x to %s.%d", p->rf_xid,
276 inet_dquad(dq, sizeof(dq), fwdto->sin_addr.s_addr),
277 ntohs(fwdto->sin_port));
278 }
279 #endif /* DEBUG */
280 if (sendto(fwd_sock, (char *) pkt, len, 0,
281 (struct sockaddr *) fwdto, sizeof(*fwdto)) == -1)
282 error = errno;
283
284 /*
285 * Save callback function and return address
286 */
287 p->rf_fwd = cb;
288 if (replyto)
289 p->rf_sin = *replyto;
290 else
291 bzero(&p->rf_sin, sizeof(p->rf_sin));
292 p->rf_ptr = i;
293
294 return error;
295 }
296
297 /*
298 * Called when some data arrives on the forwarding socket
299 */
300 void
fwd_reply()301 fwd_reply()
302 {
303 int len;
304 #ifdef DYNAMIC_BUFFERS
305 void *pkt;
306 #else
307 u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1];
308 #endif /* DYNAMIC_BUFFERS */
309 u_int *pkt_int;
310 int rc;
311 rpc_forward *p;
312 struct sockaddr_in src_addr;
313 socklen_t src_addr_len;
314
315 /*
316 * Determine the length of the packet
317 */
318 #ifdef DYNAMIC_BUFFERS
319 if (ioctl(fwd_sock, FIONREAD, &len) == -1 || len < 0) {
320 plog(XLOG_ERROR, "Error reading packet size: %m");
321 return;
322 }
323
324 /*
325 * Allocate a buffer
326 */
327 pkt = malloc(len);
328 if (!pkt) {
329 plog(XLOG_ERROR, "Out of buffers in fwd_reply");
330 return;
331 }
332 #else
333 len = MAX_PACKET_SIZE;
334 #endif /* DYNAMIC_BUFFERS */
335
336 /*
337 * Read the packet and check for validity
338 */
339 again:
340 src_addr_len = sizeof(src_addr);
341 rc = recvfrom(fwd_sock, (char *) pkt, len, 0,
342 (struct sockaddr *) &src_addr, &src_addr_len);
343 if (rc == -1 || src_addr_len != sizeof(src_addr) ||
344 src_addr.sin_family != AF_INET) {
345 if (rc == -1 && errno == EINTR)
346 goto again;
347 plog(XLOG_ERROR, "Error reading RPC reply: %m");
348 goto out;
349 }
350
351 #ifdef DYNAMIC_BUFFERS
352 if (rc != len) {
353 plog(XLOG_ERROR, "Short read in fwd_reply");
354 goto out;
355 }
356 #endif /* DYNAMIC_BUFFERS */
357
358 /*
359 * Do no more work if finishing soon
360 */
361 if ((int)amd_state >= (int)Finishing)
362 goto out;
363
364 /*
365 * Find packet reference
366 */
367 pkt_int = (u_int *) pkt;
368
369 #ifdef DEBUG
370 switch (*pkt_int & RPC_XID_MASK) {
371 case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break;
372 case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break;
373 case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break;
374 default: dlog("UNKNOWN RPC XID"); break;
375 }
376 #endif /* DEBUG */
377
378 p = fwd_locate(*pkt_int);
379 if (!p) {
380 #ifdef DEBUG
381 dlog("Can't forward reply id %#x", *pkt_int);
382 #endif /* DEBUG */
383 goto out;
384 }
385
386 if (p->rf_fwd) {
387 /*
388 * Put the original message id back
389 * into the packet.
390 */
391 *pkt_int = p->rf_oldid;
392
393 /*
394 * Call forwarding function
395 */
396 (*p->rf_fwd)(pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
397 }
398
399 /*
400 * Free forwarding info
401 */
402 fwd_free(p);
403
404 out:;
405 #ifdef DYNAMIC_BUFFERS
406 /*
407 * Free the packet
408 */
409 free(pkt);
410 #endif /* DYNAMIC_BUFFERS */
411 }
412