xref: /openbsd-src/usr.sbin/amd/amd/rpc_fwd.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	from: @(#)rpc_fwd.c	8.1 (Berkeley) 6/6/93
39  *	$Id: rpc_fwd.c,v 1.1.1.1 1995/10/18 08:47:12 deraadt Exp $
40  */
41 
42 /*
43  * RPC packet forwarding
44  */
45 
46 #include "am.h"
47 #include <sys/ioctl.h>
48 #ifndef F_SETFL
49 #include <fcntl.h>
50 #endif /* F_SETFL */
51 #ifndef FNDELAY
52 #include <sys/file.h>
53 #endif /* FNDELAY */
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 /*
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 extern qelem rpc_head;
84 qelem rpc_head = { &rpc_head, &rpc_head };
85 
86 static u_int xid;
87 #define	XID_ALLOC()	(xid++)
88 
89 #define	MAX_PACKET_SIZE	8192	/* Maximum UDP packet size */
90 
91 int fwd_sock;
92 
93 /*
94  * Allocate a rely structure
95  */
96 static rpc_forward *fwd_alloc()
97 {
98 	time_t now = clocktime();
99 	rpc_forward *p = 0, *p2;
100 
101 #ifdef DEBUG
102 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
103 #endif /* DEBUG */
104 	/*
105 	 * First search for an existing expired one.
106 	 */
107 	ITER(p2, rpc_forward, &rpc_head) {
108 		if (p2->rf_ttl <= now) {
109 			p = p2;
110 			break;
111 		}
112 	}
113 
114 	/*
115 	 * If one couldn't be found then allocate
116 	 * a new structure and link it at the
117 	 * head of the list.
118 	 */
119 	if (p) {
120 		/*
121 		 * Call forwarding function to say that
122 		 * this message was junked.
123 		 */
124 #ifdef DEBUG
125 		dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
126 #endif /* DEBUG */
127 		if (p->rf_fwd)
128 			(*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE);
129 		rem_que(&p->rf_q);
130 	} else {
131 		p = ALLOC(rpc_forward);
132 	}
133 	ins_que(&p->rf_q, &rpc_head);
134 
135 	/*
136 	 * Set the time to live field
137 	 * Timeout in 43 seconds
138 	 */
139 	p->rf_ttl = now + 43;
140 
141 #ifdef DEBUG
142 	/*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
143 #endif /* DEBUG */
144 	return p;
145 }
146 
147 /*
148  * Free an allocated reply structure.
149  * First unlink it from the list, then
150  * discard it.
151  */
152 static void fwd_free(p)
153 rpc_forward *p;
154 {
155 #ifdef DEBUG
156 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
157 #endif /* DEBUG */
158 	rem_que(&p->rf_q);
159 #ifdef DEBUG
160 	/*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
161 #endif /* DEBUG */
162 	free((voidp) p);
163 }
164 
165 /*
166  * Initialise the RPC forwarder
167  */
168 int fwd_init()
169 {
170 	int on = 1;
171 
172 	/*
173 	 * Create ping socket
174 	 */
175 	fwd_sock = socket(AF_INET, SOCK_DGRAM, 0);
176 	if (fwd_sock < 0) {
177 		plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m");
178 		return errno;
179 	}
180 
181 	/*
182 	 * Some things we talk to require a priv port - so make one here
183 	 */
184 	if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0)
185 		plog(XLOG_ERROR, "can't bind privileged port");
186 
187 	if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 &&
188 			ioctl(fwd_sock, FIONBIO, &on) < 0) {
189 		plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m");
190 		return errno;
191 	}
192 
193 	return 0;
194 }
195 
196 /*
197  * Locate a packet in the forwarding list
198  */
199 static rpc_forward *fwd_locate(id)
200 u_int id;
201 {
202 	rpc_forward *p;
203 
204 	ITER(p, rpc_forward, &rpc_head) {
205 		if (p->rf_xid == id)
206 			return p;
207 	}
208 
209 	return 0;
210 }
211 
212 /*
213  * This is called to forward a packet to another
214  * RPC server.  The message id is changed and noted
215  * so that when a reply appears we can tie it up
216  * correctly.  Just matching the reply's source address
217  * would not work because it might come from a
218  * different address.
219  */
220 int fwd_packet(type_id, pkt, len, fwdto, replyto, i, cb)
221 int type_id;
222 voidp pkt;
223 int len;
224 struct sockaddr_in *fwdto, *replyto;
225 voidp i;
226 fwd_fun cb;
227 {
228 	rpc_forward *p;
229 	u_int *pkt_int;
230 	int error;
231 
232 	if ((int)amd_state >= (int)Finishing)
233 		return ENOENT;
234 
235 	/*
236 	 * See if the type_id is fully specified.
237 	 * If so, then discard any old entries
238 	 * for this id.
239 	 * Otherwise make sure the type_id is
240 	 * fully qualified by allocating an id here.
241 	 */
242 #ifdef DEBUG
243 	switch (type_id & RPC_XID_MASK) {
244 	case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break;
245 	case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break;
246 	case RPC_XID_NFSPING: dlog("Sending NFS ping"); break;
247 	default: dlog("UNKNOWN RPC XID"); break;
248 	}
249 #endif /* DEBUG */
250 
251 	if (type_id & ~RPC_XID_MASK) {
252 #ifdef DEBUG
253 		/*dlog("Fully qualified rpc type provided");*/
254 #endif /* DEBUG */
255 		p = fwd_locate(type_id);
256 		if (p) {
257 #ifdef DEBUG
258 			dlog("Discarding earlier rpc fwd handle");
259 #endif /* DEBUG */
260 			fwd_free(p);
261 		}
262 	} else {
263 #ifdef DEBUG
264 		dlog("Allocating a new xid...");
265 #endif /* DEBUG */
266 		type_id = MK_RPC_XID(type_id, XID_ALLOC());
267 	}
268 
269 	p = fwd_alloc();
270 	if (!p)
271 		return ENOBUFS;
272 
273 	error = 0;
274 
275 	pkt_int = (u_int *) pkt;
276 
277 	/*
278 	 * Get the original packet id
279 	 */
280 	p->rf_oldid = *pkt_int;
281 
282 	/*
283 	 * Replace with newly allocated id
284 	 */
285 	p->rf_xid = *pkt_int = type_id;
286 
287 	/*
288 	 * The sendto may fail if, for example, the route
289 	 * to a remote host is lost because an intermediate
290 	 * gateway has gone down.  Important to fill in the
291 	 * rest of "p" otherwise nasty things happen later...
292 	 */
293 #ifdef DEBUG
294 	{ char dq[20];
295 	dlog("Sending packet id %#x to %s.%d", p->rf_xid, inet_dquad(dq, fwdto->sin_addr.s_addr), ntohs(fwdto->sin_port));
296 	}
297 #endif /* DEBUG */
298 	if (sendto(fwd_sock, (char *) pkt, len, 0,
299 			(struct sockaddr *) fwdto, sizeof(*fwdto)) < 0)
300 		error = errno;
301 
302 	/*
303 	 * Save callback function and return address
304 	 */
305 	p->rf_fwd = cb;
306 	if (replyto)
307 		p->rf_sin = *replyto;
308 	else
309 		bzero((voidp) &p->rf_sin, sizeof(p->rf_sin));
310 	p->rf_ptr = i;
311 
312 	return error;
313 }
314 
315 /*
316  * Called when some data arrives on the forwarding socket
317  */
318 void fwd_reply()
319 {
320 	int len;
321 #ifdef DYNAMIC_BUFFERS
322 	voidp pkt;
323 #else
324 	u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1];
325 #endif /* DYNAMIC_BUFFERS */
326 	u_int *pkt_int;
327 	int rc;
328 	rpc_forward *p;
329 	struct sockaddr_in src_addr;
330 	int src_addr_len;
331 
332 	/*
333 	 * Determine the length of the packet
334 	 */
335 #ifdef DYNAMIC_BUFFERS
336 	if (ioctl(fwd_sock, FIONREAD, &len) < 0) {
337 		plog(XLOG_ERROR, "Error reading packet size: %m");
338 		return;
339 	}
340 
341 	/*
342 	 * Allocate a buffer
343 	 */
344 	pkt = (voidp) malloc((unsigned) len);
345 	if (!pkt) {
346 		plog(XLOG_ERROR, "Out of buffers in fwd_reply");
347 		return;
348 	}
349 #else
350 	len = MAX_PACKET_SIZE;
351 #endif /* DYNAMIC_BUFFERS */
352 
353 	/*
354 	 * Read the packet and check for validity
355 	 */
356 again:
357 	src_addr_len = sizeof(src_addr);
358 	rc = recvfrom(fwd_sock, (char *) pkt, len, 0,
359 			(struct sockaddr *) &src_addr, &src_addr_len);
360 	if (rc < 0 || src_addr_len != sizeof(src_addr) ||
361 			src_addr.sin_family != AF_INET) {
362 		if (rc < 0 && errno == EINTR)
363 			goto again;
364 		plog(XLOG_ERROR, "Error reading RPC reply: %m");
365 		goto out;
366 	}
367 
368 #ifdef DYNAMIC_BUFFERS
369 	if (rc != len) {
370 		plog(XLOG_ERROR, "Short read in fwd_reply");
371 		goto out;
372 	}
373 #endif /* DYNAMIC_BUFFERS */
374 
375 	/*
376 	 * Do no more work if finishing soon
377 	 */
378 	if ((int)amd_state >= (int)Finishing)
379 		goto out;
380 
381 	/*
382 	 * Find packet reference
383 	 */
384 	pkt_int = (u_int *) pkt;
385 
386 #ifdef DEBUG
387 	switch (*pkt_int & RPC_XID_MASK) {
388 	case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break;
389 	case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break;
390 	case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break;
391 	default: dlog("UNKNOWN RPC XID"); break;
392 	}
393 #endif /* DEBUG */
394 
395 	p = fwd_locate(*pkt_int);
396 	if (!p) {
397 #ifdef DEBUG
398 		dlog("Can't forward reply id %#x", *pkt_int);
399 #endif /* DEBUG */
400 		goto out;
401 	}
402 
403 	if (p->rf_fwd) {
404 		/*
405 		 * Put the original message id back
406 		 * into the packet.
407 		 */
408 		*pkt_int = p->rf_oldid;
409 
410 		/*
411 		 * Call forwarding function
412 		 */
413 		(*p->rf_fwd)((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
414 	}
415 
416 	/*
417 	 * Free forwarding info
418 	 */
419 	fwd_free(p);
420 
421 out:;
422 #ifdef DYNAMIC_BUFFERS
423 	/*
424 	 * Free the packet
425 	 */
426 	free((voidp) pkt);
427 #endif /* DYNAMIC_BUFFERS */
428 }
429