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