blob: 0a900ca501a5fc2f4dc9931b2bc4102509a6b297 [file] [log] [blame]
/*
* 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