xref: /original-bsd/usr.sbin/portmap/portmap.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1990, 1993\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)portmap.c	8.1 (Berkeley) 06/06/93";
16 #endif /* not lint */
17 
18 /*
19 @(#)portmap.c	2.3 88/08/11 4.0 RPCSRC
20 static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro";
21 */
22 
23 /*
24  * portmap.c, Implements the program,version to port number mapping for
25  * rpc.
26  */
27 
28 /*
29  * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
30  * unrestricted use provided that this legend is included on all tape
31  * media and as a part of the software program in whole or part.  Users
32  * may copy or modify Sun RPC without charge, but are not authorized
33  * to license or distribute it to anyone else except as part of a product or
34  * program developed by the user.
35  *
36  * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
37  * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
38  * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
39  *
40  * Sun RPC is provided with no support and without any obligation on the
41  * part of Sun Microsystems, Inc. to assist in its use, correction,
42  * modification or enhancement.
43  *
44  * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
45  * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
46  * OR ANY PART THEREOF.
47  *
48  * In no event will Sun Microsystems, Inc. be liable for any lost revenue
49  * or profits or other special, indirect and consequential damages, even if
50  * Sun has been advised of the possibility of such damages.
51  *
52  * Sun Microsystems, Inc.
53  * 2550 Garcia Avenue
54  * Mountain View, California  94043
55  */
56 
57 #include <rpc/rpc.h>
58 #include <rpc/pmap_prot.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <syslog.h>
63 #include <unistd.h>
64 #include <netdb.h>
65 #include <sys/socket.h>
66 #include <sys/ioctl.h>
67 #include <sys/wait.h>
68 #include <sys/signal.h>
69 #include <sys/resource.h>
70 
71 void reg_service();
72 void reap();
73 static void callit();
74 struct pmaplist *pmaplist;
75 int debugging = 0;
76 extern int errno;
77 
78 main(argc, argv)
79 	int argc;
80 	char **argv;
81 {
82 	SVCXPRT *xprt;
83 	int sock, c;
84 	struct sockaddr_in addr;
85 	int len = sizeof(struct sockaddr_in);
86 	register struct pmaplist *pml;
87 
88 	while ((c = getopt(argc, argv, "d")) != EOF) {
89 		switch (c) {
90 
91 		case 'd':
92 			debugging = 1;
93 			break;
94 
95 		default:
96 			(void) fprintf(stderr, "usage: %s [-d]\n", argv[0]);
97 			exit(1);
98 		}
99 	}
100 
101 	if (!debugging && daemon(0, 0)) {
102 		(void) fprintf(stderr, "portmap: fork: %s", strerror(errno));
103 		exit(1);
104 	}
105 
106 	openlog("portmap", debugging ? LOG_PID | LOG_PERROR : LOG_PID,
107 	    LOG_DAEMON);
108 
109 	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
110 		syslog(LOG_ERR, "cannot create udp socket: %m");
111 		exit(1);
112 	}
113 
114 	addr.sin_addr.s_addr = 0;
115 	addr.sin_family = AF_INET;
116 	addr.sin_port = htons(PMAPPORT);
117 	if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
118 		syslog(LOG_ERR, "cannot bind udp: %m");
119 		exit(1);
120 	}
121 
122 	if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
123 		syslog(LOG_ERR, "couldn't do udp_create");
124 		exit(1);
125 	}
126 	/* make an entry for ourself */
127 	pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
128 	pml->pml_next = 0;
129 	pml->pml_map.pm_prog = PMAPPROG;
130 	pml->pml_map.pm_vers = PMAPVERS;
131 	pml->pml_map.pm_prot = IPPROTO_UDP;
132 	pml->pml_map.pm_port = PMAPPORT;
133 	pmaplist = pml;
134 
135 	if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
136 		syslog(LOG_ERR, "cannot create tcp socket: %m");
137 		exit(1);
138 	}
139 	if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
140 		syslog(LOG_ERR, "cannot bind udp: %m");
141 		exit(1);
142 	}
143 	if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
144 	    == (SVCXPRT *)NULL) {
145 		syslog(LOG_ERR, "couldn't do tcp_create");
146 		exit(1);
147 	}
148 	/* make an entry for ourself */
149 	pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
150 	pml->pml_map.pm_prog = PMAPPROG;
151 	pml->pml_map.pm_vers = PMAPVERS;
152 	pml->pml_map.pm_prot = IPPROTO_TCP;
153 	pml->pml_map.pm_port = PMAPPORT;
154 	pml->pml_next = pmaplist;
155 	pmaplist = pml;
156 
157 	(void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);
158 
159 	(void)signal(SIGCHLD, reap);
160 	svc_run();
161 	syslog(LOG_ERR, "run_svc returned unexpectedly");
162 	abort();
163 }
164 
165 #ifndef lint
166 /* need to override perror calls in rpc library */
167 void
168 perror(what)
169 	const char *what;
170 {
171 
172 	syslog(LOG_ERR, "%s: %m", what);
173 }
174 #endif
175 
176 static struct pmaplist *
177 find_service(prog, vers, prot)
178 	u_long prog, vers, prot;
179 {
180 	register struct pmaplist *hit = NULL;
181 	register struct pmaplist *pml;
182 
183 	for (pml = pmaplist; pml != NULL; pml = pml->pml_next) {
184 		if ((pml->pml_map.pm_prog != prog) ||
185 			(pml->pml_map.pm_prot != prot))
186 			continue;
187 		hit = pml;
188 		if (pml->pml_map.pm_vers == vers)
189 		    break;
190 	}
191 	return (hit);
192 }
193 
194 /*
195  * 1 OK, 0 not
196  */
197 void
198 reg_service(rqstp, xprt)
199 	struct svc_req *rqstp;
200 	SVCXPRT *xprt;
201 {
202 	struct pmap reg;
203 	struct pmaplist *pml, *prevpml, *fnd;
204 	int ans, port;
205 	caddr_t t;
206 
207 	if (debugging)
208 		(void) fprintf(stderr, "server: about do a switch\n");
209 	switch (rqstp->rq_proc) {
210 
211 	case PMAPPROC_NULL:
212 		/*
213 		 * Null proc call
214 		 */
215 		if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) {
216 			abort();
217 		}
218 		break;
219 
220 	case PMAPPROC_SET:
221 		/*
222 		 * Set a program,version to port mapping
223 		 */
224 		if (!svc_getargs(xprt, xdr_pmap, &reg))
225 			svcerr_decode(xprt);
226 		else {
227 			/*
228 			 * check to see if already used
229 			 * find_service returns a hit even if
230 			 * the versions don't match, so check for it
231 			 */
232 			fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
233 			if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) {
234 				if (fnd->pml_map.pm_port == reg.pm_port) {
235 					ans = 1;
236 					goto done;
237 				}
238 				else {
239 					ans = 0;
240 					goto done;
241 				}
242 			} else {
243 				/*
244 				 * add to END of list
245 				 */
246 				pml = (struct pmaplist *)
247 				    malloc((u_int)sizeof(struct pmaplist));
248 				pml->pml_map = reg;
249 				pml->pml_next = 0;
250 				if (pmaplist == 0) {
251 					pmaplist = pml;
252 				} else {
253 					for (fnd= pmaplist; fnd->pml_next != 0;
254 					    fnd = fnd->pml_next);
255 					fnd->pml_next = pml;
256 				}
257 				ans = 1;
258 			}
259 		done:
260 			if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
261 			    debugging) {
262 				(void) fprintf(stderr, "svc_sendreply\n");
263 				abort();
264 			}
265 		}
266 		break;
267 
268 	case PMAPPROC_UNSET:
269 		/*
270 		 * Remove a program,version to port mapping.
271 		 */
272 		if (!svc_getargs(xprt, xdr_pmap, &reg))
273 			svcerr_decode(xprt);
274 		else {
275 			ans = 0;
276 			for (prevpml = NULL, pml = pmaplist; pml != NULL; ) {
277 				if ((pml->pml_map.pm_prog != reg.pm_prog) ||
278 					(pml->pml_map.pm_vers != reg.pm_vers)) {
279 					/* both pml & prevpml move forwards */
280 					prevpml = pml;
281 					pml = pml->pml_next;
282 					continue;
283 				}
284 				/* found it; pml moves forward, prevpml stays */
285 				ans = 1;
286 				t = (caddr_t)pml;
287 				pml = pml->pml_next;
288 				if (prevpml == NULL)
289 					pmaplist = pml;
290 				else
291 					prevpml->pml_next = pml;
292 				free(t);
293 			}
294 			if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
295 			    debugging) {
296 				(void) fprintf(stderr, "svc_sendreply\n");
297 				abort();
298 			}
299 		}
300 		break;
301 
302 	case PMAPPROC_GETPORT:
303 		/*
304 		 * Lookup the mapping for a program,version and return its port
305 		 */
306 		if (!svc_getargs(xprt, xdr_pmap, &reg))
307 			svcerr_decode(xprt);
308 		else {
309 			fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
310 			if (fnd)
311 				port = fnd->pml_map.pm_port;
312 			else
313 				port = 0;
314 			if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) &&
315 			    debugging) {
316 				(void) fprintf(stderr, "svc_sendreply\n");
317 				abort();
318 			}
319 		}
320 		break;
321 
322 	case PMAPPROC_DUMP:
323 		/*
324 		 * Return the current set of mapped program,version
325 		 */
326 		if (!svc_getargs(xprt, xdr_void, NULL))
327 			svcerr_decode(xprt);
328 		else {
329 			if ((!svc_sendreply(xprt, xdr_pmaplist,
330 			    (caddr_t)&pmaplist)) && debugging) {
331 				(void) fprintf(stderr, "svc_sendreply\n");
332 				abort();
333 			}
334 		}
335 		break;
336 
337 	case PMAPPROC_CALLIT:
338 		/*
339 		 * Calls a procedure on the local machine.  If the requested
340 		 * procedure is not registered this procedure does not return
341 		 * error information!!
342 		 * This procedure is only supported on rpc/udp and calls via
343 		 * rpc/udp.  It passes null authentication parameters.
344 		 */
345 		callit(rqstp, xprt);
346 		break;
347 
348 	default:
349 		svcerr_noproc(xprt);
350 		break;
351 	}
352 }
353 
354 
355 /*
356  * Stuff for the rmtcall service
357  */
358 #define ARGSIZE 9000
359 
360 struct encap_parms {
361 	u_long arglen;
362 	char *args;
363 };
364 
365 static bool_t
366 xdr_encap_parms(xdrs, epp)
367 	XDR *xdrs;
368 	struct encap_parms *epp;
369 {
370 
371 	return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE));
372 }
373 
374 struct rmtcallargs {
375 	u_long	rmt_prog;
376 	u_long	rmt_vers;
377 	u_long	rmt_port;
378 	u_long	rmt_proc;
379 	struct encap_parms rmt_args;
380 };
381 
382 static bool_t
383 xdr_rmtcall_args(xdrs, cap)
384 	register XDR *xdrs;
385 	register struct rmtcallargs *cap;
386 {
387 
388 	/* does not get a port number */
389 	if (xdr_u_long(xdrs, &(cap->rmt_prog)) &&
390 	    xdr_u_long(xdrs, &(cap->rmt_vers)) &&
391 	    xdr_u_long(xdrs, &(cap->rmt_proc))) {
392 		return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
393 	}
394 	return (FALSE);
395 }
396 
397 static bool_t
398 xdr_rmtcall_result(xdrs, cap)
399 	register XDR *xdrs;
400 	register struct rmtcallargs *cap;
401 {
402 	if (xdr_u_long(xdrs, &(cap->rmt_port)))
403 		return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
404 	return (FALSE);
405 }
406 
407 /*
408  * only worries about the struct encap_parms part of struct rmtcallargs.
409  * The arglen must already be set!!
410  */
411 static bool_t
412 xdr_opaque_parms(xdrs, cap)
413 	XDR *xdrs;
414 	struct rmtcallargs *cap;
415 {
416 
417 	return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
418 }
419 
420 /*
421  * This routine finds and sets the length of incoming opaque paraters
422  * and then calls xdr_opaque_parms.
423  */
424 static bool_t
425 xdr_len_opaque_parms(xdrs, cap)
426 	register XDR *xdrs;
427 	struct rmtcallargs *cap;
428 {
429 	register u_int beginpos, lowpos, highpos, currpos, pos;
430 
431 	beginpos = lowpos = pos = xdr_getpos(xdrs);
432 	highpos = lowpos + ARGSIZE;
433 	while ((int)(highpos - lowpos) >= 0) {
434 		currpos = (lowpos + highpos) / 2;
435 		if (xdr_setpos(xdrs, currpos)) {
436 			pos = currpos;
437 			lowpos = currpos + 1;
438 		} else {
439 			highpos = currpos - 1;
440 		}
441 	}
442 	xdr_setpos(xdrs, beginpos);
443 	cap->rmt_args.arglen = pos - beginpos;
444 	return (xdr_opaque_parms(xdrs, cap));
445 }
446 
447 /*
448  * Call a remote procedure service
449  * This procedure is very quiet when things go wrong.
450  * The proc is written to support broadcast rpc.  In the broadcast case,
451  * a machine should shut-up instead of complain, less the requestor be
452  * overrun with complaints at the expense of not hearing a valid reply ...
453  *
454  * This now forks so that the program & process that it calls can call
455  * back to the portmapper.
456  */
457 static void
458 callit(rqstp, xprt)
459 	struct svc_req *rqstp;
460 	SVCXPRT *xprt;
461 {
462 	struct rmtcallargs a;
463 	struct pmaplist *pml;
464 	u_short port;
465 	struct sockaddr_in me;
466 	int pid, so = -1;
467 	CLIENT *client;
468 	struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred;
469 	struct timeval timeout;
470 	char buf[ARGSIZE];
471 
472 	timeout.tv_sec = 5;
473 	timeout.tv_usec = 0;
474 	a.rmt_args.args = buf;
475 	if (!svc_getargs(xprt, xdr_rmtcall_args, &a))
476 		return;
477 	if ((pml = find_service(a.rmt_prog, a.rmt_vers,
478 	    (u_long)IPPROTO_UDP)) == NULL)
479 		return;
480 	/*
481 	 * fork a child to do the work.  Parent immediately returns.
482 	 * Child exits upon completion.
483 	 */
484 	if ((pid = fork()) != 0) {
485 		if (pid < 0)
486 			syslog(LOG_ERR, "CALLIT (prog %lu): fork: %m",
487 			    a.rmt_prog);
488 		return;
489 	}
490 	port = pml->pml_map.pm_port;
491 	get_myaddress(&me);
492 	me.sin_port = htons(port);
493 	client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &so);
494 	if (client != (CLIENT *)NULL) {
495 		if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
496 			client->cl_auth = authunix_create(au->aup_machname,
497 			   au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids);
498 		}
499 		a.rmt_port = (u_long)port;
500 		if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a,
501 		    xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) {
502 			svc_sendreply(xprt, xdr_rmtcall_result, (caddr_t)&a);
503 		}
504 		AUTH_DESTROY(client->cl_auth);
505 		clnt_destroy(client);
506 	}
507 	(void)close(so);
508 	exit(0);
509 }
510 
511 void
512 reap()
513 {
514 	while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0);
515 }
516