/* $NetBSD: krpc_subr.c,v 1.12.4.1 1996/06/07 00:52:26 cgd Exp $ */12/*-3* SPDX-License-Identifier: BSD-4-Clause4*5* Copyright (c) 1995 Gordon Ross, Adam Glass6* Copyright (c) 1992 Regents of the University of California.7* All rights reserved.8*9* This software was developed by the Computer Systems Engineering group10* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and11* contributed to Berkeley.12*13* Redistribution and use in source and binary forms, with or without14* modification, are permitted provided that the following conditions15* are met:16* 1. Redistributions of source code must retain the above copyright17* notice, this list of conditions and the following disclaimer.18* 2. Redistributions in binary form must reproduce the above copyright19* notice, this list of conditions and the following disclaimer in the20* documentation and/or other materials provided with the distribution.21* 3. All advertising materials mentioning features or use of this software22* must display the following acknowledgement:23* This product includes software developed by the University of24* California, Lawrence Berkeley Laboratory and its contributors.25* 4. Neither the name of the University nor the names of its contributors26* may be used to endorse or promote products derived from this software27* without specific prior written permission.28*29* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND30* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE31* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE32* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE33* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL34* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS35* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)36* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT37* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY38* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF39* SUCH DAMAGE.40*41* partially based on:42* libnetboot/rpc.c43*/4445#include <sys/param.h>46#include <sys/systm.h>47#include <sys/jail.h>48#include <sys/malloc.h>49#include <sys/mbuf.h>50#include <sys/proc.h>51#include <sys/socket.h>52#include <sys/socketvar.h>53#include <sys/uio.h>5455#include <net/if.h>56#include <net/vnet.h>5758#include <netinet/in.h>5960#include <rpc/types.h>61#include <rpc/auth.h>62#include <rpc/rpc_msg.h>63#include <nfs/krpc.h>64#include <nfs/xdr_subs.h>6566/*67* Kernel support for Sun RPC68*69* Used currently for bootstrapping in nfs diskless configurations.70*/7172/*73* Generic RPC headers74*/7576struct auth_info {77u_int32_t authtype; /* auth type */78u_int32_t authlen; /* auth length */79};8081struct auth_unix {82int32_t ua_time;83int32_t ua_hostname; /* null */84int32_t ua_uid;85int32_t ua_gid;86int32_t ua_gidlist; /* null */87};8889struct krpc_call {90u_int32_t rp_xid; /* request transaction id */91int32_t rp_direction; /* call direction (0) */92u_int32_t rp_rpcvers; /* rpc version (2) */93u_int32_t rp_prog; /* program */94u_int32_t rp_vers; /* version */95u_int32_t rp_proc; /* procedure */96struct auth_info rpc_auth;97struct auth_unix rpc_unix;98struct auth_info rpc_verf;99};100101struct krpc_reply {102u_int32_t rp_xid; /* request transaction id */103int32_t rp_direction; /* call direction (1) */104int32_t rp_astatus; /* accept status (0: accepted) */105union {106u_int32_t rpu_errno;107struct {108struct auth_info rok_auth;109u_int32_t rok_status;110} rpu_rok;111} rp_u;112};113#define rp_errno rp_u.rpu_errno114#define rp_auth rp_u.rpu_rok.rok_auth115#define rp_status rp_u.rpu_rok.rok_status116117#define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */118119/*120* What is the longest we will wait before re-sending a request?121* Note this is also the frequency of "RPC timeout" messages.122* The re-send loop count sup linearly to this maximum, so the123* first complaint will happen after (1+2+3+4+5)=15 seconds.124*/125#define MAX_RESEND_DELAY 5 /* seconds */126127/*128* Call portmap to lookup a port number for a particular rpc program129* Returns non-zero error on failure.130*/131int132krpc_portmap(struct sockaddr_in *sin, u_int prog, u_int vers, u_int16_t *portp,133struct thread *td)134{135struct sdata {136u_int32_t prog; /* call program */137u_int32_t vers; /* call version */138u_int32_t proto; /* call protocol */139u_int32_t port; /* call port (unused) */140} *sdata;141struct rdata {142u_int16_t pad;143u_int16_t port;144} *rdata;145struct mbuf *m;146int error;147148/* The portmapper port is fixed. */149if (prog == PMAPPROG) {150*portp = htons(PMAPPORT);151return 0;152}153154m = m_get(M_WAITOK, MT_DATA);155sdata = mtod(m, struct sdata *);156m->m_len = sizeof(*sdata);157158/* Do the RPC to get it. */159sdata->prog = txdr_unsigned(prog);160sdata->vers = txdr_unsigned(vers);161sdata->proto = txdr_unsigned(IPPROTO_UDP);162sdata->port = 0;163164sin->sin_port = htons(PMAPPORT);165error = krpc_call(sin, PMAPPROG, PMAPVERS,166PMAPPROC_GETPORT, &m, NULL, td);167if (error)168return error;169170if (m->m_len < sizeof(*rdata)) {171m = m_pullup(m, sizeof(*rdata));172if (m == NULL)173return ENOBUFS;174}175rdata = mtod(m, struct rdata *);176*portp = rdata->port;177178m_freem(m);179return 0;180}181182/*183* Do a remote procedure call (RPC) and wait for its reply.184* If from_p is non-null, then we are doing broadcast, and185* the address from whence the response came is saved there.186*/187int188krpc_call(struct sockaddr_in *sa, u_int prog, u_int vers, u_int func,189struct mbuf **data, struct sockaddr **from_p, struct thread *td)190{191struct socket *so;192struct sockaddr_in *sin, ssin;193struct sockaddr *from;194struct mbuf *m, *mhead;195struct krpc_call *call;196struct krpc_reply *reply;197struct sockopt sopt;198struct timeval tv;199struct uio auio;200int error, rcvflg, timo, secs, len;201static u_int32_t xid = ~0xFF;202u_int16_t tport;203u_int32_t saddr;204205/*206* Validate address family.207* Sorry, this is INET specific...208*/209if (sa->sin_family != AF_INET)210return (EAFNOSUPPORT);211212/* Free at end if not null. */213mhead = NULL;214from = NULL;215216/*217* Create socket and set its receive timeout.218*/219if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td)))220return error;221222tv.tv_sec = 1;223tv.tv_usec = 0;224bzero(&sopt, sizeof sopt);225sopt.sopt_dir = SOPT_SET;226sopt.sopt_level = SOL_SOCKET;227sopt.sopt_name = SO_RCVTIMEO;228sopt.sopt_val = &tv;229sopt.sopt_valsize = sizeof tv;230231if ((error = sosetopt(so, &sopt)) != 0)232goto out;233234/*235* Enable broadcast if necessary.236*/237if (from_p) {238int on = 1;239sopt.sopt_name = SO_BROADCAST;240sopt.sopt_val = &on;241sopt.sopt_valsize = sizeof on;242if ((error = sosetopt(so, &sopt)) != 0)243goto out;244}245246/*247* Bind the local endpoint to a reserved port,248* because some NFS servers refuse requests from249* non-reserved (non-privileged) ports.250*/251sin = &ssin;252bzero(sin, sizeof *sin);253sin->sin_len = sizeof(*sin);254sin->sin_family = AF_INET;255sin->sin_addr.s_addr = INADDR_ANY;256tport = IPPORT_RESERVED;257do {258tport--;259sin->sin_port = htons(tport);260error = sobind(so, (struct sockaddr *)sin, td);261} while (error == EADDRINUSE &&262tport > IPPORT_RESERVED / 2);263if (error) {264printf("bind failed\n");265goto out;266}267268/*269* Setup socket address for the server.270*/271272/*273* Prepend RPC message header.274*/275mhead = m_gethdr(M_WAITOK, MT_DATA);276mhead->m_next = *data;277call = mtod(mhead, struct krpc_call *);278mhead->m_len = sizeof(*call);279bzero((caddr_t)call, sizeof(*call));280/* rpc_call part */281xid++;282call->rp_xid = txdr_unsigned(xid);283/* call->rp_direction = 0; */284call->rp_rpcvers = txdr_unsigned(2);285call->rp_prog = txdr_unsigned(prog);286call->rp_vers = txdr_unsigned(vers);287call->rp_proc = txdr_unsigned(func);288/* rpc_auth part (auth_unix as root) */289call->rpc_auth.authtype = txdr_unsigned(AUTH_UNIX);290call->rpc_auth.authlen = txdr_unsigned(sizeof(struct auth_unix));291/* rpc_verf part (auth_null) */292call->rpc_verf.authtype = 0;293call->rpc_verf.authlen = 0;294295/*296* Setup packet header297*/298m_fixhdr(mhead);299mhead->m_pkthdr.rcvif = NULL;300301/*302* Send it, repeatedly, until a reply is received,303* but delay each re-send by an increasing amount.304* If the delay hits the maximum, start complaining.305*/306timo = 0;307for (;;) {308/* Send RPC request (or re-send). */309m = m_copym(mhead, 0, M_COPYALL, M_WAITOK);310error = sosend(so, (struct sockaddr *)sa, NULL, m,311NULL, 0, td);312if (error) {313printf("krpc_call: sosend: %d\n", error);314goto out;315}316m = NULL;317318/* Determine new timeout. */319if (timo < MAX_RESEND_DELAY)320timo++;321else {322saddr = ntohl(sa->sin_addr.s_addr);323printf("RPC timeout for server %d.%d.%d.%d\n",324(saddr >> 24) & 255,325(saddr >> 16) & 255,326(saddr >> 8) & 255,327saddr & 255);328}329330/*331* Wait for up to timo seconds for a reply.332* The socket receive timeout was set to 1 second.333*/334secs = timo;335while (secs > 0) {336if (from) {337free(from, M_SONAME);338from = NULL;339}340if (m) {341m_freem(m);342m = NULL;343}344bzero(&auio, sizeof(auio));345auio.uio_resid = len = 1<<16;346rcvflg = 0;347error = soreceive(so, &from, &auio, &m, NULL, &rcvflg);348if (error == EWOULDBLOCK) {349secs--;350continue;351}352if (error)353goto out;354len -= auio.uio_resid;355356/* Does the reply contain at least a header? */357if (len < MIN_REPLY_HDR)358continue;359if (m->m_len < MIN_REPLY_HDR)360continue;361reply = mtod(m, struct krpc_reply *);362363/* Is it the right reply? */364if (reply->rp_direction != txdr_unsigned(REPLY))365continue;366367if (reply->rp_xid != txdr_unsigned(xid))368continue;369370/* Was RPC accepted? (authorization OK) */371if (reply->rp_astatus != 0) {372error = fxdr_unsigned(u_int32_t, reply->rp_errno);373printf("rpc denied, error=%d\n", error);374continue;375}376377/* Did the call succeed? */378if (reply->rp_status != 0) {379error = fxdr_unsigned(u_int32_t, reply->rp_status);380if (error == PROG_MISMATCH) {381error = EBADRPC;382goto out;383}384printf("rpc denied, status=%d\n", error);385continue;386}387388goto gotreply; /* break two levels */389390} /* while secs */391} /* forever send/receive */392393error = ETIMEDOUT;394goto out;395396gotreply:397398/*399* Get RPC reply header into first mbuf,400* get its length, then strip it off.401*/402len = sizeof(*reply);403if (m->m_len < len) {404m = m_pullup(m, len);405if (m == NULL) {406error = ENOBUFS;407goto out;408}409}410reply = mtod(m, struct krpc_reply *);411if (reply->rp_auth.authtype != 0) {412len += fxdr_unsigned(u_int32_t, reply->rp_auth.authlen);413len = (len + 3) & ~3; /* XXX? */414}415m_adj(m, len);416417/* result */418*data = m;419if (from_p) {420*from_p = from;421from = NULL;422}423424out:425if (mhead) m_freem(mhead);426if (from) free(from, M_SONAME);427soclose(so);428return error;429}430431/*432* eXternal Data Representation routines.433* (but with non-standard args...)434*/435436/*437* String representation for RPC.438*/439struct xdr_string {440u_int32_t len; /* length without null or padding */441char data[4]; /* data (longer, of course) */442/* data is padded to a long-word boundary */443};444445struct mbuf *446xdr_string_encode(char *str, int len)447{448struct mbuf *m;449struct xdr_string *xs;450int dlen; /* padded string length */451int mlen; /* message length */452453dlen = (len + 3) & ~3;454mlen = dlen + 4;455456if (mlen > MCLBYTES) /* If too big, we just can't do it. */457return (NULL);458459m = m_get2(mlen, M_WAITOK, MT_DATA, 0);460xs = mtod(m, struct xdr_string *);461m->m_len = mlen;462xs->len = txdr_unsigned(len);463bcopy(str, xs->data, len);464return (m);465}466467468