xref: /openbsd/usr.sbin/amd/amd/rpc_fwd.c (revision df69c215)
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