#ifndef lint static char sccsid[] = "@(#)portmap.c 1.1 94/10/31 Copyr 1984 Sun Micro"; #endif /* * Copyright (c) 1984 by Sun Microsystems, Inc. */ /* * portmap.c, Implements the program, version to port number * mapping for rpc. */ #include #include #include #include #include #include #include #include #include #include /* to find local addresses */ #include #include #include #include #define BUGID_1082319 #ifdef BUGID_1082319 #include #endif #define MYSTAKSIZE 2048 static int restarted; /* Whether the portmap was restarted */ static struct sigstack ss; /* All the signal handling on stack */ static struct sigstack oss; static int signalstack[MYSTAKSIZE]; static jmp_buf my_environment; static int sjret; static int debugging; static int verbose; /* Catch all errors */ static int forward_socket; /* forward the PMAPPROC_CALLIT on this fd */ static int tcp_fd = -1; /* For checking whether TCP service is up */ static int udp_fd = -1; /* For checking whether UDP service is up */ static SVCXPRT *udp_xprt; /* server handle for UDP */ static SVCXPRT *tcp_xprt; /* server handle for TCP */ static struct sockaddr_in me; /* for forwarding */ struct pmaplist *pmaplist; /* port number list */ time_t time(); void svc_versquiet(); void reg_service(); void reap(); void mysvc_run(); void onsig(); void detachfromtty(); u_long forward_register(); bool_t chklocal(); main(argc, argv) int argc; char **argv; { register struct pmaplist *pml; struct sockaddr_in addr; int sock; int len = sizeof (struct sockaddr_in); int dontblock = 1; int on = 1; while (argc > 1) { if (strcmp("-v", argv[1]) == 0) verbose = 1; if (strcmp("-d", argv[1]) == 0) { debugging = 1; verbose = 1; } argc--; argv++; } if (debugging) { printf("portmap debugging enabled -- will abort on errors!\n"); } else { detachfromtty(); } /* * This is an effort to make portmap really bullet proof so that * it never dies. We try to catch (almost) all the signals and * then we restart portmapper with the same portlist. On * receiving any such signals, it also leaves a core file. * All the signals are handled on a different stack. This was * done so that if somehow the stack gets corrupted, atleast * the signal handler can run on a clean one. And we also did not * want the signal stuff to be in the core file. */ ss.ss_sp = (char *)signalstack; ss.ss_onstack = 0; if (sigstack(&ss, &oss) < 0) perror("portmap: sigstack"); /* * Save the environment, so that the old one can be reused in case * portmapper dies. */ if (sjret = setjmp(my_environment)) { if (verbose) fprintf(stderr, "portmap restarted signal %d\n", sjret); restarted = 1; tcp_fd = -1; udp_fd = -1; FD_ZERO(&svc_fdset); /* lose all rpc sockets */ svc_destroy(udp_xprt); /* free svc vectors */ svc_destroy(tcp_xprt); } /* create a socket to forward PMAPPROC_CALLIT calls */ if ((forward_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("portmap cannot create forward socket"); exit(1); } /* * The forwarding socket may get many broadcast requests and * hence it is important that it doesnt get blocked for any reasons */ (void) ioctl(forward_socket, FIONBIO, &dontblock); get_myaddress(&me); /* destination for forwarding == me */ addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_family = AF_INET; addr.sin_port = 0; if (bind(forward_socket, (struct sockaddr *) &addr, len) != 0) { perror("portmap cannot bind forward_socket"); exit(1); } if (debugging) printf("portmap: forward socket is %d on port %d\n", forward_socket, addr.sin_port); /* create a server handle for UDP */ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("portmap cannot create socket"); exit(1); } addr.sin_port = htons(PMAPPORT); if (bind(sock, (struct sockaddr *) &addr, len) != 0) { perror("portmap cannot bind udp"); exit(1); } /* * We use SO_REUSEADDR because there is a possibility of kernel * disallowing the use of the same bind address for some time even * after that corresponding socket has been closed. In the case * of TCP, some data transfer may still be pending. */ (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on); if ((udp_xprt = svcudp_create(sock)) == (SVCXPRT *) NULL) { fprintf(stderr, "portmap could not do svcudp_create\n"); exit(1); } (void) svc_register(udp_xprt, PMAPPROG, PMAPVERS, reg_service, FALSE); /* make an entry for ourself */ if (!restarted){ pml = (struct pmaplist *) malloc((u_int) sizeof (struct pmaplist)); if (pml == 0) { perror("portmap: malloc failed"); exit(1); } pml->pml_next = 0; pml->pml_map.pm_prog = PMAPPROG; pml->pml_map.pm_vers = PMAPVERS; pml->pml_map.pm_prot = IPPROTO_UDP; pml->pml_map.pm_port = PMAPPORT; pmaplist = pml; } /* create a server handle for TCP */ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("portmap cannot create socket"); exit(1); } (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on); if (bind(sock, (struct sockaddr *) &addr, len) != 0) { perror("portmap cannot bind tcp"); exit(1); } if ((tcp_xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) == (SVCXPRT *) NULL) { fprintf(stderr, "portmap could not do svctcp_create\n"); exit(1); } (void) svc_register(tcp_xprt, PMAPPROG, PMAPVERS, reg_service, FALSE); /* make an entry for ourself */ if (!restarted) { pml = (struct pmaplist *) malloc((u_int) sizeof (struct pmaplist)); if (pml == 0) { perror("portmap: malloc failed"); exit(1); } pml->pml_map.pm_prog = PMAPPROG; pml->pml_map.pm_vers = PMAPVERS; pml->pml_map.pm_prot = IPPROTO_TCP; pml->pml_map.pm_port = PMAPPORT; pml->pml_next = pmaplist; pmaplist = pml; } /* * sockets send this on write after disconnect -- * live and learn from SVR4 work */ (void) signal(SIGPIPE, SIG_IGN); if (!debugging) { #ifdef SIGABRT (void) signal(SIGABRT, onsig); /* restart */ #endif (void) signal(SIGIOT, onsig); /* restart */ (void) signal(SIGILL, onsig); /* restart */ (void) signal(SIGBUS, onsig); /* restart */ (void) signal(SIGSEGV, onsig); /* restart */ } (void) signal(SIGCHLD, reap); /* * Tell RPC library to shut up about version mismatches so that new * revs of broadcast protocols don't cause all the old servers to * say: "wrong version". */ svc_versquiet(udp_xprt); svc_versquiet(tcp_xprt); mysvc_run(); fprintf(stderr, "portmap: svc_run returned unexpectedly\n"); abort(); /* will restart portmap */ /* NOTREACHED */ } /* * This routine is called to make sure that the given port number is * still bound. This is helpful in those cases where the server dies * and the client gets the wrong information. It is better if the * client about this fact at create time itself. If the given port * number is not bound, we return FALSE. */ bool_t is_bound(pml) struct pmaplist *pml; { int fd; int res; struct sockaddr_in myaddr; struct sockaddr_in *sin; if (pml->pml_map.pm_prot == IPPROTO_TCP) { if (tcp_fd < 0) tcp_fd = socket(AF_INET, SOCK_STREAM, 0); fd = tcp_fd; } else if (pml->pml_map.pm_prot == IPPROTO_UDP) { if (udp_fd < 0) udp_fd = socket(AF_INET, SOCK_DGRAM, 0); fd = udp_fd; } else return (TRUE); if (fd < 0) return (TRUE); sin = &myaddr; bzero((char *)sin, sizeof (*sin)); sin->sin_family = AF_INET; sin->sin_port = htons(pml->pml_map.pm_port); res = bind(fd, (struct sockaddr *)sin, sizeof (struct sockaddr_in)); if (res < 0) return (TRUE); if (pml->pml_map.pm_prot == IPPROTO_TCP) { /* XXX: Too bad that there is no unbind(). */ (void) close(tcp_fd); tcp_fd = -1; } else { (void) close(udp_fd); udp_fd = -1; } return (FALSE); } /* * Finds the service for prog, vers. Even if the specified vers * is not registered, we still return the registeration for that * program number, with the hope that the client will figure out * the actual available version number on doing a clnt_call() via * the RPC_PROGVERSMISMTACH. */ static struct pmaplist * find_service(prog, vers, prot) u_long prog; u_long vers; u_long prot; { register struct pmaplist *hit = NULL; register struct pmaplist *hitp = NULL; register struct pmaplist *pml; register struct pmaplist *prevpml; for (prevpml = NULL, pml = pmaplist; pml != NULL; pml = pml->pml_next) { if ((pml->pml_map.pm_prog != prog) || (pml->pml_map.pm_prot != prot)) { prevpml = pml; continue; } hit = pml; hitp = prevpml; if (pml->pml_map.pm_vers == vers) break; prevpml = pml; } if (hit == NULL) return (NULL); /* Make sure it is bound */ if (is_bound(hit)) return (hit); /* unhook it from the list */ if (verbose) fprintf(stderr, "portmap: service failed for prog=%d, vers=%d, prot=%d\n", hit->pml_map.pm_prog, hit->pml_map.pm_vers, hit->pml_map.pm_prot); pml = hit->pml_next; if (hitp == NULL) pmaplist = pml; else hitp->pml_next = pml; free((caddr_t) hit); /* * Mostprobably other versions and transports for this * program number were also registered by this same * service, so we should check their bindings also. * This may slow down the response time a bit. */ for (prevpml = NULL, pml = pmaplist; pml != NULL;) { if (pml->pml_map.pm_prog != prog) { prevpml = pml; pml = pml->pml_next; continue; } /* Make sure it is bound */ if (is_bound(pml)) { prevpml = pml; pml = pml->pml_next; continue; } else { /* unhook it */ hit = pml; pml = hit->pml_next; if (prevpml == NULL) pmaplist = pml; else prevpml->pml_next = pml; free((caddr_t) hit); } } return (NULL); } /* * 1 OK, 0 not */ void reg_service(rqstp, xprt) struct svc_req *rqstp; SVCXPRT *xprt; { struct pmap reg; struct pmaplist *pml, *prevpml, *fnd; int ans; u_long port; caddr_t t; struct sockaddr_in *who; if (debugging) printf("portmap: request %d\n", rqstp->rq_proc); switch (rqstp->rq_proc) { case PMAPPROC_NULL: /* * Null proc call */ if ((!svc_sendreply(xprt, xdr_void, (char *)NULL)) && debugging) { fprintf(stderr, "portmap: svc_sendreply\n"); abort(); } break; case PMAPPROC_SET: /* * Set a program, version to port mapping */ if (!svc_getargs(xprt, xdr_pmap, ®)) { svcerr_decode(xprt); return; } who = svc_getcaller(xprt); if (chklocal(ntohl(who->sin_addr)) == FALSE) { if (verbose) fprintf(stderr, "portmap: attempt to set prog %d vers %d port %d from host %s port %d\n", reg.pm_prog, reg.pm_vers, reg.pm_port, inet_ntoa(who->sin_addr), ntohs(who->sin_port)); ans = 0; goto done_set; } /* * check to see if already used find_service returns * a hit even if the versions don't match, so check * for it */ fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot); if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) { if (fnd->pml_map.pm_port == reg.pm_port) { ans = 1; goto done_set; } else { /* Caller should have done UNSET first */ ans = 0; goto done_set; } } else { if ((reg.pm_port < IPPORT_RESERVED) && (ntohs(who->sin_port) >= IPPORT_RESERVED)) { if (verbose) fprintf(stderr, "portmap: attempt to set (prog %d vers %d reserved port %d) from port %d\n", reg.pm_prog, reg.pm_vers, reg.pm_port, ntohs(who->sin_port)); ans = 0; goto done_set; } /* * add to END of list */ pml = (struct pmaplist *) malloc((u_int) sizeof (struct pmaplist)); if (pml == 0) { perror("portmap: malloc failed in set"); svcerr_systemerr(xprt); if (debugging) abort(); return; } pml->pml_map = reg; pml->pml_next = 0; if (pmaplist == 0) { pmaplist = pml; } else { for (fnd = pmaplist; fnd->pml_next != 0; fnd = fnd->pml_next); fnd->pml_next = pml; } ans = 1; } done_set: if ((!svc_sendreply(xprt, xdr_long, (caddr_t) &ans)) && debugging) { fprintf(stderr, "portmap: svc_sendreply\n"); abort(); } break; case PMAPPROC_UNSET: /* * Remove a program, version to port mapping. */ if (!svc_getargs(xprt, xdr_pmap, ®)) { svcerr_decode(xprt); return; } ans = 0; who = svc_getcaller(xprt); if (chklocal(ntohl(who->sin_addr)) == FALSE) { if (verbose) fprintf(stderr, "portmap: attempt to unset (prog %d vers %d) from host %s port %d\n", reg.pm_prog, reg.pm_vers, inet_ntoa(who->sin_addr), ntohs(who->sin_port)); goto done_unset; } for (prevpml = NULL, pml = pmaplist; pml != NULL;) { if ((pml->pml_map.pm_prog != reg.pm_prog) || (pml->pml_map.pm_vers != reg.pm_vers)) { /* both pml & prevpml move forwards */ prevpml = pml; pml = pml->pml_next; continue; } if ((pml->pml_map.pm_port < IPPORT_RESERVED) && (ntohs(who->sin_port) >= IPPORT_RESERVED)) { if (verbose) fprintf(stderr, "portmap: attempt to unset (prog %d vers %d reserved port %d) port from %d\n", pml->pml_map.pm_prog, pml->pml_map.pm_vers, pml->pml_map.pm_port, ntohs(who->sin_port)); goto done_unset; } /* found it; pml moves forward, prevpml stays */ ans = 1; t = (caddr_t) pml; pml = pml->pml_next; if (prevpml == NULL) pmaplist = pml; else prevpml->pml_next = pml; free(t); } done_unset: if ((!svc_sendreply(xprt, xdr_long, (caddr_t) &ans)) && debugging) { fprintf(stderr, "portmap: svc_sendreply\n"); abort(); } break; case PMAPPROC_GETPORT: /* * Lookup the mapping for a program, version and * return its port */ if (!svc_getargs(xprt, xdr_pmap, ®)) { svcerr_decode(xprt); return; } fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot); if (fnd) port = fnd->pml_map.pm_port; else port = 0; if ((!svc_sendreply(xprt, xdr_long, (caddr_t) &port)) && debugging) { fprintf(stderr, "portmap: svc_sendreply\n"); abort(); } break; case PMAPPROC_DUMP: /* * Return the current set of mapped program, version */ if ((!svc_sendreply(xprt, xdr_pmaplist, (caddr_t) &pmaplist)) && debugging) { fprintf(stderr, "portmap: svc_sendreply\n"); abort(); } break; case PMAPPROC_CALLIT: /* * Calls a procedure on the local machine. If the requested * procedure is not registered this procedure does not return * error information!! This procedure is only supported on * rpc/udp and calls via rpc/udp. It passes null * authentication parameters. */ callit(rqstp, xprt); break; default: svcerr_noproc(xprt); break; } return; } /* * Stuff for the rmtcall service */ #define ARGSIZE 9000 typedef struct encap_parms { u_long arglen; char *args; }; static bool_t xdr_encap_parms(xdrs, epp) XDR *xdrs; struct encap_parms *epp; { return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE)); } typedef struct rmtcallargs { u_long rmt_prog; u_long rmt_vers; u_long rmt_port; u_long rmt_proc; struct encap_parms rmt_args; }; static bool_t xdr_rmtcall_args(xdrs, cap) register XDR *xdrs; register struct rmtcallargs *cap; { /* does not get a port number */ if (xdr_u_long(xdrs, &(cap->rmt_prog)) && xdr_u_long(xdrs, &(cap->rmt_vers)) && xdr_u_long(xdrs, &(cap->rmt_proc))) { return (xdr_encap_parms(xdrs, &(cap->rmt_args))); } return (FALSE); } static bool_t xdr_rmtcall_result(xdrs, cap) register XDR *xdrs; register struct rmtcallargs *cap; { if (xdr_u_long(xdrs, &(cap->rmt_port))) return (xdr_encap_parms(xdrs, &(cap->rmt_args))); return (FALSE); } /* * only worries about the struct encap_parms part of struct rmtcallargs. The * arglen must already be set!! */ static bool_t xdr_opaque_parms(xdrs, cap) XDR *xdrs; struct rmtcallargs *cap; { return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen)); } /* * This routine finds and sets the length of incoming opaque paraters and * then calls xdr_opaque_parms. */ static bool_t xdr_len_opaque_parms(xdrs, cap) register XDR *xdrs; struct rmtcallargs *cap; { register u_int beginpos, lowpos, highpos, currpos, pos; beginpos = lowpos = pos = xdr_getpos(xdrs); highpos = lowpos + ARGSIZE; while ((int) (highpos - lowpos) >= 0) { currpos = (lowpos + highpos) / 2; if (xdr_setpos(xdrs, currpos)) { pos = currpos; lowpos = currpos + 1; } else { highpos = currpos - 1; } } xdr_setpos(xdrs, beginpos); cap->rmt_args.arglen = pos - beginpos; return (xdr_opaque_parms(xdrs, cap)); } /* * Call a remote procedure service This procedure is very quiet when things * go wrong. The proc is written to support broadcast rpc. In the broadcast * case, a machine should shut-up instead of complain, less the requestor be * overrun with complaints at the expense of not hearing a valid reply ... * The data is forwarded on a new xid. When we hear back from the service, * we send the answer back to the client with the old xid patched in. */ struct bogus_data { u_int buflen; u_long xid; }; #define getbogus_data(xprt) ((struct bogus_data *) (xprt->xp_p2)) static callit(rqstp, xprt) struct svc_req *rqstp; SVCXPRT *xprt; { struct rmtcallargs a; struct pmaplist *pml; u_long port; struct sockaddr_in *who; int outlen; XDR outxdr; struct rpc_msg call_msg; int addrlen; char buf[ARGSIZE]; char outbuf[ARGSIZE]; struct bogus_data *bd; AUTH *auth; a.rmt_args.args = buf; /* arguments on stack */ if (!svc_getargs(xprt, xdr_rmtcall_args, &a)) return; #ifdef BUGID_1082319 if (a.rmt_prog == YPPROG) { /* * Note although YPPROC_XFR sounds dangerous it returns NO data * these are the only three YP procedures that return data. * The rest of the INFO procedures are allowed through. */ if ((a.rmt_proc == YPPROC_FIRST) || (a.rmt_proc == YPPROC_NEXT) || (a.rmt_proc == YPPROC_MATCH)) return; } #endif if (a.rmt_prog == PMAPPROG) { if (verbose) { who = svc_getcaller(xprt); fprintf(stderr, "portmap: self callit reqst for proc=%d, from host=%s, port=%d\n", a.rmt_proc, inet_ntoa(who->sin_addr), ntohs(who->sin_port)); } return; } if ((pml = find_service(a.rmt_prog, a.rmt_vers, (u_long) IPPROTO_UDP)) == NULL) return; port = pml->pml_map.pm_port; if (debugging) printf("portmap: callit entered port=%d\n", port); me.sin_port = htons(port); /* destination */ bd = getbogus_data(xprt); who = svc_getcaller(xprt); call_msg.rm_xid = forward_register(bd->xid, who, (u_short)port); call_msg.rm_direction = CALL; call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; call_msg.rm_call.cb_prog = a.rmt_prog; call_msg.rm_call.cb_vers = a.rmt_vers; xdrmem_create(&outxdr, outbuf, ARGSIZE, XDR_ENCODE); if (!xdr_callhdr(&outxdr, &call_msg)) { if (debugging) fprintf(stderr, "portmap: cant xdr callheader\n"); return; } if (!xdr_u_long(&outxdr, &(a.rmt_proc))) return; if (rqstp->rq_cred.oa_flavor == AUTH_NULL) { auth = authnone_create(); } else if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) { struct authunix_parms *au; au = (struct authunix_parms *)rqstp->rq_clntcred; auth = authunix_create(au->aup_machname, au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids); if (auth == NULL) /* fall back */ auth = authnone_create(); } else { /* we do not support any other authentication scheme */ return; } if (auth == NULL) return; if (!AUTH_MARSHALL(auth, &outxdr)) { AUTH_DESTROY(auth); return; } AUTH_DESTROY(auth); if (!xdr_opaque_parms(&outxdr, &a)) return; outlen = (int) XDR_GETPOS(&outxdr); addrlen = sizeof (struct sockaddr_in); (void) sendto(forward_socket, outbuf, outlen, 0, &me, addrlen); return; } /* * this does an asynchronous get (manually) and uses svc_udpreplyto to * forward it. Hand generate the entire RPC packet for delivery to * the initial caller. */ #define MAXREPLY 9000 int handle_reply() { int inlen; struct sockaddr_in from; struct sockaddr_in to; int fromlen; XDR reply_xdrs; struct rpc_msg reply_msg; struct rpc_err reply_error; int pos; int len; int ok; u_long xto; struct rmtcallargs a; char buffer[MAXREPLY]; reply_msg.acpted_rply.ar_verf = _null_auth; reply_msg.acpted_rply.ar_results.where = 0; reply_msg.acpted_rply.ar_results.proc = xdr_void; fromlen = sizeof (struct sockaddr); inlen = recvfrom(forward_socket, buffer, MAXREPLY, 0, &from, &fromlen); if (inlen < 0) { if (debugging) perror("portmap: recvfrom in handle_reply"); return (-1); } xdrmem_create(&reply_xdrs, buffer, (u_int)inlen, XDR_DECODE); ok = xdr_replymsg(&reply_xdrs, &reply_msg); if (!ok) { if (debugging) fprintf(stderr, "portmap: handle_reply: bad message\n"); return (-2); } _seterr_reply(&reply_msg, &reply_error); if (reply_error.re_status != RPC_SUCCESS) { /* We keep quite in cases of RPC failure */ if (debugging) fprintf(stderr, "portmap: handle_reply: RPC error\n"); return (-3); } pos = XDR_GETPOS(&reply_xdrs); len = inlen - pos; a.rmt_args.args = &buffer[pos]; a.rmt_args.arglen = len; a.rmt_port = (u_long) from.sin_port; if (forward_find(reply_msg.rm_xid, from.sin_port, &to, &xto)) { if (chklocal(from.sin_addr) == FALSE) { if (verbose) fprintf(stderr, "portmap: attempt to forward reply from host %s port %d to host %s port%d\n", inet_ntoa(from.sin_addr), ntohs(from.sin_port), inet_ntoa(to.sin_addr), ntohs(to.sin_port)); return (-3); } if (debugging) printf( "portmap: lookup ok for xid=%d port=%d to=%s.%d xto=%d\n", reply_msg.rm_xid, from.sin_port, inet_ntoa(to.sin_addr), ntohs(to.sin_port), xto); return (svc_udpreplyto(udp_xprt, &to, xto, xdr_rmtcall_result, (char *)&a)); } else { if (verbose) fprintf(stderr, "portmap: cant lookup xid=%d port=%d\n", reply_msg.rm_xid, from.sin_port); return (-4); } } /* * udpreplyto is like sendreply but it only works for udp and it allow the * caller to specify the reply xid and destination */ int svc_udpreplyto(xprt, who, xid, xdr_results, a) SVCXPRT *xprt; struct sockaddr_in *who; u_long xid; xdrproc_t xdr_results; char *a; { struct bogus_data *bd; /* bogus */ *(svc_getcaller(xprt)) = *who; bd = getbogus_data(xprt); bd->xid = xid; /* set xid on reply */ return (svc_sendreply(xprt, xdr_results, a)); } #define NFS 256 struct finfo { u_long xid; struct sockaddr_in repto; u_short port; time_t time; u_long callerxid; }; struct finfo FINFO[NFS]; static u_long forward_register(callerxid, who, port) u_long callerxid; struct sockaddr_in *who; u_short port; { int i; int j; time_t mint; static u_long lastxid; mint = FINFO[0].time; j = 0; if (lastxid == 0) lastxid = time((time_t *)0) * NFS; /* initialize value */ for (i = 0; i < NFS; i++) { if ((FINFO[i].callerxid == callerxid) && (FINFO[i].port == port) && (FINFO[i].repto.sin_addr.s_addr == who->sin_addr.s_addr) && (FINFO[i].repto.sin_port == who->sin_port)) { FINFO[i].time = time((time_t *)0); return (FINFO[i].xid); /* forward on this xid */ } if (FINFO[i].time < mint) { j = i; mint = FINFO[i].time; } } FINFO[j].time = time((time_t *)0); FINFO[j].callerxid = callerxid; FINFO[j].port = port; FINFO[j].repto = *who; lastxid = lastxid + NFS; FINFO[j].xid = lastxid + j; /* encode slot */ return (FINFO[j].xid); /* forward on this xid */ } int forward_find(xid, port, who, outxid) u_int xid; u_short port; struct sockaddr_in *who; u_long *outxid; { int i; i = xid % NFS; if (i < 0) i += NFS; if ((FINFO[i].xid == xid) && (port == FINFO[i].port)) { *who = FINFO[i].repto; *outxid = FINFO[i].callerxid; return (1); } return (0); } /* * Recvd some signal. We never say die, and we try to move along * because we do not want to loose our registrations. */ void onsig(x) { int t; int ds = getdtablesize(); for (t = 0; t < ds; t++) (void) close(t); /* close any sockets */ if (fork() == 0) { #ifdef SIGABRT signal(SIGABRT, SIG_DFL); #endif (void) signal(SIGIOT, SIG_DFL); (void) sigsetmask(0); abort(); /* snapshot */ exit(-1); } longjmp(my_environment, x); exit(-1); /* NOTREACHED */ } void detachfromtty() { int ds = getdtablesize(); int pid, t; pid = fork(); if (pid < 0) { perror("portmap: fork"); exit(1); } if (pid != 0) exit(0); for (t = 0; t < ds; t++) (void) close(t); t = open("/dev/console", 2); if (t >= 0) { (void) dup2(t, 1); (void) dup2(t, 2); } t = open("/dev/tty", 2); if (t >= 0) { (void) ioctl(t, TIOCNOTTY, (char *) 0); (void) close(t); } /* more disassociation */ (void) setpgrp(getpid(), getpid()); } /* * This is required when portmapper forks off to create its new * copy because of some signal. */ void reap() { while (wait3((union wait *)NULL, WNOHANG, NULL) > 0); } /* how many interfaces could there be on a computer? */ #define MAX_LOCAL 64 static int num_local = -1; static struct in_addr addrs[MAX_LOCAL]; bool_t chklocal(taddr) struct in_addr taddr; { int i; struct in_addr iaddr; if (taddr.s_addr == INADDR_LOOPBACK) return (TRUE); if (num_local == -1) { num_local = getlocal(); if (debugging) printf("portmap: %d interfaces detected.\n", num_local); } for (i = 0; i < num_local; i++) { iaddr.s_addr = ntohl(addrs[i].s_addr); if (bcmp((char *) &taddr, (char *) &(iaddr), sizeof (struct in_addr)) == 0) return (TRUE); } return (FALSE); } int getlocal() { struct ifconf ifc; struct ifreq ifreq, *ifr; int n, j, sock; char buf[UDPMSGSIZE]; ifc.ifc_len = UDPMSGSIZE; ifc.ifc_buf = buf; sock = socket(PF_INET, SOCK_DGRAM, 0); if (sock < 0) return (FALSE); if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) { perror("portmap:icotl SIOCGIFCONF"); (void) close(sock); return (FALSE); } ifr = ifc.ifc_req; j = 0; for (n = ifc.ifc_len / sizeof (struct ifreq); n > 0; n--, ifr++) { ifreq = *ifr; if (ioctl(sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) { perror("portmap:icotl SIOCGIFFLAGS"); continue; } if ((ifreq.ifr_flags & IFF_UP) && ifr->ifr_addr.sa_family == AF_INET) { if (ioctl(sock, SIOCGIFADDR, (char *) &ifreq) < 0) { perror("SIOCGIFADDR"); } else { addrs[j] = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr; j++; } } if (j >= (MAX_LOCAL - 1)) break; } (void) close(sock); return (j); } void mysvc_run() { fd_set readfds; int dtbsize = _rpc_dtablesize(); for (;;) { readfds = svc_fdset; FD_SET(forward_socket, &readfds); switch (select(dtbsize, &readfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0)) { case -1: if (errno == EINTR) { continue; } perror("select error"); return; case 0: continue; default: if (FD_ISSET(forward_socket, &readfds)) { (void) handle_reply(); continue; }; svc_getreqset(&readfds); } } }