| /* |
| * dummypmap.c |
| * |
| * Enough portmapper functionality that mount doesn't hang trying |
| * to start lockd. Enables nfsroot with locking functionality. |
| * |
| * Note: the kernel will only speak to the local portmapper |
| * using RPC over UDP. |
| */ |
| |
| #include <sys/types.h> |
| #include <netinet/in.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <sys/socket.h> |
| |
| #include "sunrpc.h" |
| |
| struct portmap_call |
| { |
| struct rpc_call rpc; |
| __u32 program; |
| __u32 version; |
| __u32 proto; |
| __u32 port; |
| }; |
| |
| struct portmap_reply |
| { |
| struct rpc_reply rpc; |
| __u32 port; |
| }; |
| |
| int bind_portmap(void) |
| { |
| int sock = socket(PF_INET, SOCK_DGRAM, 0); |
| struct sockaddr_in sin; |
| |
| if ( sock < 0 ) |
| return -1; |
| |
| memset(&sin, 0, sizeof sin); |
| sin.sin_family = AF_INET; |
| sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ |
| sin.sin_port = htons(RPC_PMAP_PORT); |
| if ( bind(sock, (struct sockaddr *)&sin, sizeof sin) < 0 ) { |
| int err = errno; |
| close(sock); |
| errno = err; |
| return -1; |
| } |
| |
| return sock; |
| } |
| |
| int dummy_portmap(int sock, FILE *portmap_file) |
| { |
| struct sockaddr_in sin; |
| int pktlen, addrlen; |
| union { |
| struct portmap_call c; |
| unsigned char b[65536]; /* Max UDP packet size */ |
| } pkt; |
| struct portmap_reply rply; |
| |
| for(;;) { |
| addrlen = sizeof sin; |
| pktlen = recvfrom(sock, &pkt.c.rpc.hdr.udp, sizeof pkt, 0, |
| (struct sockaddr *)&sin, &addrlen); |
| |
| if ( pktlen < 0 ) { |
| if ( errno == EINTR ) |
| continue; |
| |
| return -1; |
| } |
| |
| /* +4 to skip the TCP fragment header */ |
| if ( pktlen+4 < sizeof(struct portmap_call) ) |
| continue; /* Bad packet */ |
| |
| if ( pkt.c.rpc.hdr.udp.msg_type != htonl(RPC_CALL) ) |
| continue; /* Bad packet */ |
| |
| memset(&rply, 0, sizeof rply); |
| |
| rply.rpc.hdr.udp.xid = pkt.c.rpc.hdr.udp.xid; |
| rply.rpc.hdr.udp.msg_type = htonl(RPC_REPLY); |
| |
| if ( pkt.c.rpc.rpc_vers != htonl(2) ) { |
| rply.rpc.reply_state = htonl(REPLY_DENIED); |
| /* state <- RPC_MISMATCH == 0 */ |
| } else if ( pkt.c.rpc.program != htonl(PORTMAP_PROGRAM) ) { |
| rply.rpc.reply_state = htonl(PROG_UNAVAIL); |
| } else if ( pkt.c.rpc.prog_vers != htonl(2) ) { |
| rply.rpc.reply_state = htonl(PROG_MISMATCH); |
| } else if ( pkt.c.rpc.cred_len != 0 || |
| pkt.c.rpc.vrf_len != 0 ) { |
| /* Can't deal with credentials data; the kernel won't send them */ |
| rply.rpc.reply_state = htonl(SYSTEM_ERR); |
| } else { |
| switch ( ntohl(pkt.c.rpc.proc) ) { |
| case PMAP_PROC_NULL: |
| break; |
| case PMAP_PROC_SET: |
| if ( pkt.c.proto == htonl(IPPROTO_TCP) || |
| pkt.c.proto == htonl(IPPROTO_UDP) ) { |
| if ( portmap_file ) |
| fprintf(portmap_file, "%u %u %s %u\n", |
| ntohl(pkt.c.program), ntohl(pkt.c.version), |
| pkt.c.proto == htonl(IPPROTO_TCP) ? "tcp" : "udp", |
| ntohl(pkt.c.port)); |
| rply.port = htonl(1); /* TRUE = success */ |
| } |
| break; |
| case PMAP_PROC_UNSET: |
| rply.port = htonl(1); /* TRUE = success */ |
| break; |
| case PMAP_PROC_GETPORT: |
| break; |
| case PMAP_PROC_DUMP: |
| break; |
| default: |
| rply.rpc.reply_state = htonl(PROC_UNAVAIL); |
| break; |
| } |
| } |
| |
| sendto(sock, &rply.rpc.hdr.udp, sizeof rply - 4, 0, |
| (struct sockaddr *)&sin, addrlen); |
| } |
| } |
| |
| #ifdef TEST |
| int main(int argc, char *argv[]) |
| { |
| if ( argc > 1 ) |
| portmap_file = fopen(argv[1], "a"); |
| |
| return dummy_portmap(); |
| } |
| #endif |
| |