TEST(test_encoder, test_encode_procedure_message) { uint8_t data[256]; krpc_call_t call; krpc_argument_t arguments[0]; ASSERT_EQ(KRPC_OK, krpc_call(&call, 99, 42, 0, arguments)); pb_ostream_t stream = pb_ostream_from_buffer(data, sizeof(data)); ASSERT_EQ(KRPC_OK, krpc_encode_message_ProcedureCall(&stream, &call.message)); std::string expected = "0a0012002063282a"; ASSERT_EQ(expected, hexlify(data, stream.bytes_written)); }
/* * Call portmap to lookup a port number for a particular rpc program * Returns non-zero error on failure. */ int krpc_portmap( struct sockaddr_in *sin, /* server address */ u_int prog, u_int vers, u_int proto, /* host order */ u_int16_t *portp) /* network order */ { struct sdata { u_int32_t prog; /* call program */ u_int32_t vers; /* call version */ u_int32_t proto; /* call protocol */ u_int32_t port; /* call port (unused) */ } *sdata; struct rdata { u_int16_t pad; u_int16_t port; } *rdata; mbuf_t m; int error; /* The portmapper port is fixed. */ if (prog == PMAPPROG) { *portp = htons(PMAPPORT); return 0; } error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, &m); if (error) return error; mbuf_setlen(m, sizeof(*sdata)); mbuf_pkthdr_setlen(m, sizeof(*sdata)); sdata = mbuf_data(m); /* Do the RPC to get it. */ sdata->prog = htonl(prog); sdata->vers = htonl(vers); sdata->proto = htonl(proto); sdata->port = 0; sin->sin_port = htons(PMAPPORT); error = krpc_call(sin, SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, &m, NULL); if (error) return error; rdata = mbuf_data(m); if (mbuf_len(m) >= sizeof(*rdata)) { *portp = rdata->port; } if (mbuf_len(m) < sizeof(*rdata) || !rdata->port) error = EPROGUNAVAIL; mbuf_freem(m); return (error); }
TEST(test_encoder, test_encode_procedure_message_with_args) { uint8_t data[256]; int32_t x = 42; double y = 3.14159; krpc_call_t call; krpc_argument_t arguments[2]; ASSERT_EQ(KRPC_OK, krpc_call(&call, 99, 42, 2, arguments)); ASSERT_EQ(KRPC_OK, krpc_add_argument(&call, 0, &krpc_encode_callback_int32, &x)); ASSERT_EQ(KRPC_OK, krpc_add_argument(&call, 1, &krpc_encode_callback_double, &y)); pb_ostream_t stream = pb_ostream_from_buffer(data, sizeof(data)); ASSERT_EQ(KRPC_OK, krpc_encode_message_ProcedureCall(&stream, &call.message)); std::string expected = "0a0012001a031201541a0c080112086e861bf0f92109402063282a"; ASSERT_EQ(expected, hexlify(data, stream.bytes_written)); }
/* * Call portmap to lookup a port number for a particular rpc program * Returns non-zero error on failure. */ int krpc_portmap(struct sockaddr_in *sin, u_int prog, u_int vers, u_int16_t *portp) { struct sdata { u_int32_t prog; /* call program */ u_int32_t vers; /* call version */ u_int32_t proto; /* call protocol */ u_int32_t port; /* call port (unused) */ } *sdata; struct rdata { u_int16_t pad; u_int16_t port; } *rdata; struct mbuf *m; int error; /* The portmapper port is fixed. */ if (prog == PMAPPROG) { *portp = htons(PMAPPORT); return 0; } m = m_get(M_WAIT, MT_DATA); sdata = mtod(m, struct sdata *); m->m_len = sizeof(*sdata); /* Do the RPC to get it. */ sdata->prog = txdr_unsigned(prog); sdata->vers = txdr_unsigned(vers); sdata->proto = txdr_unsigned(IPPROTO_UDP); sdata->port = 0; sin->sin_port = htons(PMAPPORT); error = krpc_call(sin, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT, &m, NULL, -1); if (error) return error; if (m->m_len < sizeof(*rdata)) { m = m_pullup(m, sizeof(*rdata)); if (m == NULL) return ENOBUFS; } rdata = mtod(m, struct rdata *); *portp = rdata->port; m_freem(m); return 0; }
/* * RPC: mountd/mount * Given a server pathname, get an NFS file handle. * Also, sets sin->sin_port to the NFS service port. */ int md_mount(struct sockaddr_in *mdsin, /* mountd server address */ char *path, u_char *fhp, int *fhsizep, struct nfs_args *args, struct thread *td) { struct mbuf *m; int error; int authunixok; int authcount; int authver; /* First try NFS v3 */ /* Get port number for MOUNTD. */ error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER3, &mdsin->sin_port, td); if (error == 0) { m = xdr_string_encode(path, strlen(path)); /* Do RPC to mountd. */ error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT, &m, NULL, td); } if (error == 0) { args->flags |= NFSMNT_NFSV3; } else { /* Fallback to NFS v2 */ /* Get port number for MOUNTD. */ error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1, &mdsin->sin_port, td); if (error != 0) return error; m = xdr_string_encode(path, strlen(path)); /* Do RPC to mountd. */ error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT, &m, NULL, td); if (error != 0) return error; /* message already freed */ args->flags &= ~NFSMNT_NFSV3; } if (xdr_int_decode(&m, &error) != 0 || error != 0) goto bad; if ((args->flags & NFSMNT_NFSV3) != 0) { if (xdr_int_decode(&m, fhsizep) != 0 || *fhsizep > NFSX_V3FHMAX || *fhsizep <= 0) goto bad; } else *fhsizep = NFSX_V2FH; if (xdr_opaque_decode(&m, fhp, *fhsizep) != 0) goto bad; if (args->flags & NFSMNT_NFSV3) { if (xdr_int_decode(&m, &authcount) != 0) goto bad; authunixok = 0; if (authcount < 0 || authcount > 100) goto bad; while (authcount > 0) { if (xdr_int_decode(&m, &authver) != 0) goto bad; if (authver == RPCAUTH_UNIX) authunixok = 1; authcount--; } if (authunixok == 0) goto bad; } /* Set port number for NFS use. */ error = krpc_portmap(mdsin, NFS_PROG, (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2, &mdsin->sin_port, td); goto out; bad: error = EBADRPC; out: m_freem(m); return error; }
/* * RPC: mountd/mount * Given a server pathname, get an NFS file handle. * Also, sets sin->sin_port to the NFS service port. * * mdsin mountd server address */ static int md_mount(struct sockaddr_in *mdsin, char *path, struct nfs_args *argp, struct lwp *lwp) { /* The RPC structures */ struct rdata { u_int32_t errno; union { u_int8_t v2fh[NFSX_V2FH]; struct { u_int32_t fhlen; u_int8_t fh[1]; } v3fh; } fh; } *rdata; struct mbuf *m; u_int8_t *fh; int minlen, error; int mntver; mntver = (argp->flags & NFSMNT_NFSV3) ? 3 : 2; do { /* * Get port number for MOUNTD. */ error = krpc_portmap(mdsin, RPCPROG_MNT, mntver, IPPROTO_UDP, &mdsin->sin_port, lwp); if (error) continue; /* This mbuf is consumed by krpc_call. */ m = xdr_string_encode(path, strlen(path)); if (m == NULL) return ENOMEM; /* Do RPC to mountd. */ error = krpc_call(mdsin, RPCPROG_MNT, mntver, RPCMNT_MOUNT, &m, NULL, lwp); if (error != EPROGMISMATCH) break; /* Try lower version of mountd. */ } while (--mntver >= 1); if (error) { printf("nfs_boot: mountd error=%d\n", error); return error; } if (mntver != 3) argp->flags &= ~NFSMNT_NFSV3; /* The reply might have only the errno. */ if (m->m_len < 4) goto bad; /* Have at least errno, so check that. */ rdata = mtod(m, struct rdata *); error = fxdr_unsigned(u_int32_t, rdata->errno); if (error) goto out; /* Have errno==0, so the fh must be there. */ if (mntver == 3) { argp->fhsize = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen); if (argp->fhsize > NFSX_V3FHMAX) goto bad; minlen = 2 * sizeof(u_int32_t) + argp->fhsize; } else { argp->fhsize = NFSX_V2FH; minlen = sizeof(u_int32_t) + argp->fhsize; } if (m->m_len < minlen) { m = m_pullup(m, minlen); if (m == NULL) return(EBADRPC); rdata = mtod(m, struct rdata *); }