RFC 1833

Binding Protocols for ONC RPC Version 2

πŸ“… published August 1995, from ietf

RFC 1833 πŸ“œβœ¨ Binding Protocols for ONC RPC Version 2

okay bestie, this one is by R. Srinivasan at Sun Microsystems πŸŽ€ it's a Standards Track doc, dated August 1995, and it's all about binding protocols for ONC RPC Version 2 πŸ¦‹ the header doesn't list anything it obsoletes or updates, so it's a clean slate, iykyk πŸ’…


Status of This Memo 🌸

this doc specifies an Internet standards track protocol for the Internet community, and it's literally asking for discussion and suggestions for improvements 🫢 if you want the current standardization state and status of this protocol, check the current edition of the "Internet Official Protocol Standards" (STD 1) πŸ”₯ distribution of this memo is unlimited, so share the vibes ✨

Abstract πŸ¦‹

this doc describes the binding protocols used together with the ONC Remote Procedure Call (ONC RPC Version 2) protocols πŸ’œ

Table of Contents 🧚

   1. Introduction                                               1
   2. RPCBIND Program Protocol                                   2
   2.1 RPCBIND Protocol Specification (in RPC Language)          3
   2.2 RPCBIND Operation                                         9
   2.2.1 RPCBIND Version 3                                       9
   2.2.2 RPCBIND, Version 4                                     10
   3. Port Mapper Program Protocol                              11
   3.1 Port Mapper Protocol Specification (in RPC Language)     11
   3.2 Port Mapper Operation                                    13
   References                                                   14
   Security Considerations                                      14
   Author's Address                                             14

1. Introduction ✨

okay so this doc specifies the binding protocols used with ONC RPC Version 2 πŸ’– as a prereq, you should already know your stuff from [1] and [2], which describe ONC RPC Version 2 and XDR (eXternal Data Representation) πŸŽ€

here's the vibe. an RPC service is identified by its RPC program number, its version number, and the transport address where it can be reached πŸ¦‹ that transport address is made of a network address plus a transport selector 🫢 over TCP/IP or UDP/IP, the network address is an IP address and the transport selector is a TCP or UDP port number πŸ’…

a client program needs the RPC program number, the version number, and the transport address of a service to use it 🌈 the program number and version number are usually baked right into the client as part of the service definition πŸ’œ the network address part is usually sitting in a name service, or it gets passed in as a parameter to the client πŸ“ the transport selector (aka the TCP or UDP port) is usually picked dynamically and changes every single time the service is invoked πŸ’€ so servers grab a transport address, then register it with a well-known lookup service ✨ it's "well-known" because it uses a fixed transport selector and lives at the same network address as the server πŸ‘‘ clients then ask the lookup service for the server's transport address 🧚

a lookup service is honestly so necessary, bc the range of well-known transport selectors is super tiny for some transports, but the number of services can be huge 😭 if only the lookup service sits on a well-known transport selector, you can find out the transport addresses of all your other remote programs just by asking it πŸ”₯

this doc describes three versions of a lookup service, and they all use the same RPC program number (100000) 🌸 they all use port 111 over TCP and UDP πŸ¦‹ Versions 3 and 4 are described in Section 2 ("RPCBIND Program Protocol") ✨ Version 2 is described in Section 3 ("Port Mapper Program Protocol") πŸ’–

the thing that makes RPCBIND (versions 3 and 4) stand out is that it uses a transport-independent format for the transport address, called the universal address format πŸ‘‘ a universal address is an ASCII string representation of the transport-dependent address πŸŽ€ the string representations for a transport are defined by that transport's addressing authority πŸ’… so the RPCBIND protocol works for binding ONC RPC clients and servers over any transport, no cap 🫢

the Port Mapper (version 2), on the other hand, is the older protocol 🌈 it's specific to TCP and UDP, and it handles TCP and UDP ports directly 🧚


2. RPCBIND Program Protocol πŸ’œ

the RPCBIND program maps RPC program and version numbers to universal addresses, which is what makes dynamic binding of remote programs possible πŸ“

RPCBIND is bound to a well-known address on each supported transport πŸ¦‹ other programs register their dynamically allocated transport addresses with it, and then RPCBIND makes those addresses publicly available ✨

RPCBIND also helps with broadcast RPC πŸ’– a given RPC program will usually have different transport address bindings on different machines, so you can't just broadcast straight to all of them 😭 but RPCBIND does have a well-known address πŸ‘‘ so to broadcast to a program, the client sends its message to the RPCBIND program sitting at the broadcast address πŸŽ€ every RPCBIND instance that picks up the broadcast then calls the local service the client asked for πŸ’… once RPCBIND gets the reply from that local service, it sends the reply back to the client 🫢

2.1 RPCBIND Protocol Specification (in RPC Language) 🌈

here's the RPC Language spec, copied exactly so nobody gets confused 🧚

/*
 * rpcb_prot.x
 * rpcbind protocol, versions 3 and 4, in RPC Language
 */

/*
 * rpcbind address for TCP/UDP
 */
const RPCB_PORT = 111;

/*
 * A mapping of (program, version, network ID) to address
 *
 * The network identifier  (r_netid):
 * This is a string that represents a local identification for a
 * network. This is defined by a system administrator based on local
 * conventions, and cannot be depended on to have the same value on
 * every system.
 */
struct rpcb {
 unsigned long r_prog;    /* program number */
 unsigned long r_vers;    /* version number */
 string r_netid<>;        /* network id */
 string r_addr<>;         /* universal address */
 string r_owner<>;        /* owner of this service */
};

struct rp__list {
 rpcb rpcb_map;
 struct rp__list *rpcb_next;
};
typedef rp__list *rpcblist_ptr;        /* results of RPCBPROC_DUMP */


/*
 * Arguments of remote calls
 */
struct rpcb_rmtcallargs {
 unsigned long prog;        /* program number */
 unsigned long vers;        /* version number */
 unsigned long proc;        /* procedure number */
 opaque args<>;             /* argument */
};


/*
 * Results of the remote call
 */
struct rpcb_rmtcallres {
 string addr<>;            /* remote universal address */
 opaque results<>;         /* result */
};


/*
 * rpcb_entry contains a merged address of a service on a particular
 * transport, plus associated netconfig information.  A list of
 * rpcb_entry items is returned by RPCBPROC_GETADDRLIST.  The meanings
 * and values used for the r_nc_* fields are given below.
 *
 * The network identifier  (r_nc_netid):

 *   This is a string that represents a local identification for a
 *   network.  This is defined by a system administrator based on
 *   local conventions, and cannot be depended on to have the same
 *   value on every system.
 *
 * Transport semantics (r_nc_semantics):
 *  This represents the type of transport, and has the following values:
 *     NC_TPI_CLTS     (1)      Connectionless
 *     NC_TPI_COTS     (2)      Connection oriented
 *     NC_TPI_COTS_ORD (3)      Connection oriented with graceful close
 *     NC_TPI_RAW      (4)      Raw transport
 *
 * Protocol family (r_nc_protofmly):
 *   This identifies the family to which the protocol belongs.  The
 *   following values are defined:
 *     NC_NOPROTOFMLY   "-"
 *     NC_LOOPBACK      "loopback"
 *     NC_INET          "inet"
 *     NC_IMPLINK       "implink"
 *     NC_PUP           "pup"
 *     NC_CHAOS         "chaos"
 *     NC_NS            "ns"
 *     NC_NBS           "nbs"
 *     NC_ECMA          "ecma"
 *     NC_DATAKIT       "datakit"
 *     NC_CCITT         "ccitt"
 *     NC_SNA           "sna"
 *     NC_DECNET        "decnet"
 *     NC_DLI           "dli"
 *     NC_LAT           "lat"
 *     NC_HYLINK        "hylink"
 *     NC_APPLETALK     "appletalk"
 *     NC_NIT           "nit"
 *     NC_IEEE802       "ieee802"
 *     NC_OSI           "osi"
 *     NC_X25           "x25"
 *     NC_OSINET        "osinet"
 *     NC_GOSIP         "gosip"
 *
 * Protocol name (r_nc_proto):
 *   This identifies a protocol within a family.  The following are
 *   currently defined:
 *      NC_NOPROTO      "-"
 *      NC_TCP          "tcp"
 *      NC_UDP          "udp"
 *      NC_ICMP         "icmp"
 */
struct rpcb_entry {
 string          r_maddr<>;            /* merged address of service */
 string          r_nc_netid<>;         /* netid field */
 unsigned long   r_nc_semantics;       /* semantics of transport */
 string          r_nc_protofmly<>;     /* protocol family */
 string          r_nc_proto<>;         /* protocol name */
};

/*
 * A list of addresses supported by a service.
 */
struct rpcb_entry_list {
 rpcb_entry rpcb_entry_map;
 struct rpcb_entry_list *rpcb_entry_next;
};

typedef rpcb_entry_list *rpcb_entry_list_ptr;
/*
 * rpcbind statistics
 */

const rpcb_highproc_2 = RPCBPROC_CALLIT;
const rpcb_highproc_3 = RPCBPROC_TADDR2UADDR;
const rpcb_highproc_4 = RPCBPROC_GETSTAT;

const RPCBSTAT_HIGHPROC = 13; /* # of procs in rpcbind V4 plus one */
const RPCBVERS_STAT     = 3; /* provide only for rpcbind V2, V3 and V4 */
const RPCBVERS_4_STAT   = 2;
const RPCBVERS_3_STAT   = 1;
const RPCBVERS_2_STAT   = 0;

/* Link list of all the stats about getport and getaddr */
struct rpcbs_addrlist {
 unsigned long prog;
 unsigned long vers;
 int success;
 int failure;
 string netid<>;
 struct rpcbs_addrlist *next;
};

/* Link list of all the stats about rmtcall */
struct rpcbs_rmtcalllist {
 unsigned long prog;
 unsigned long vers;
 unsigned long proc;
 int success;
 int failure;
 int indirect;    /* whether callit or indirect */
 string netid<>;
 struct rpcbs_rmtcalllist *next;
};

typedef int rpcbs_proc[RPCBSTAT_HIGHPROC];
typedef rpcbs_addrlist *rpcbs_addrlist_ptr;
typedef rpcbs_rmtcalllist *rpcbs_rmtcalllist_ptr;

struct rpcb_stat {
 rpcbs_proc              info;
 int                     setinfo;
 int                     unsetinfo;
 rpcbs_addrlist_ptr      addrinfo;
 rpcbs_rmtcalllist_ptr   rmtinfo;
};
/*
 * One rpcb_stat structure is returned for each version of rpcbind
 * being monitored.
 */

typedef rpcb_stat rpcb_stat_byvers[RPCBVERS_STAT];

/*
 * netbuf structure, used to store the transport specific form of
 * a universal transport address.
 */
struct netbuf {
 unsigned int maxlen;
 opaque buf<>;
};
program RPCBPROG {
 version RPCBVERS {
     bool
     RPCBPROC_SET(rpcb) = 1;

     bool
     RPCBPROC_UNSET(rpcb) = 2;

     string
     RPCBPROC_GETADDR(rpcb) = 3;

     rpcblist_ptr
     RPCBPROC_DUMP(void) = 4;

     rpcb_rmtcallres
     RPCBPROC_CALLIT(rpcb_rmtcallargs) = 5;

     unsigned int
     RPCBPROC_GETTIME(void) = 6;

     netbuf
     RPCBPROC_UADDR2TADDR(string) = 7;

     string
     RPCBPROC_TADDR2UADDR(netbuf) = 8;
 } = 3;
 version RPCBVERS4 {
     bool
     RPCBPROC_SET(rpcb) = 1;

     bool
     RPCBPROC_UNSET(rpcb) = 2;

     string
     RPCBPROC_GETADDR(rpcb) = 3;

     rpcblist_ptr
     RPCBPROC_DUMP(void) = 4;

     /*
      * NOTE: RPCBPROC_BCAST has the same functionality as CALLIT;
      * the new name is intended to indicate that this
      * procedure should be used for broadcast RPC, and
      * RPCBPROC_INDIRECT should be used for indirect calls.
      */
     rpcb_rmtcallres
     RPCBPROC_BCAST(rpcb_rmtcallargs) = RPCBPROC_CALLIT;

     unsigned int

     RPCBPROC_GETTIME(void) = 6;

     netbuf
     RPCBPROC_UADDR2TADDR(string) = 7;

     string
     RPCBPROC_TADDR2UADDR(netbuf) = 8;

     string
     RPCBPROC_GETVERSADDR(rpcb) = 9;

     rpcb_rmtcallres
     RPCBPROC_INDIRECT(rpcb_rmtcallargs) = 10;

     rpcb_entry_list_ptr
     RPCBPROC_GETADDRLIST(rpcb) = 11;

     rpcb_stat_byvers
     RPCBPROC_GETSTAT(void) = 12;
 } = 4;
} = 100000;

2.2 RPCBIND Operation πŸ¦‹

okay so RPCBIND gets contacted through an assigned address that's specific to whatever transport you're using πŸ’… for TCP/IP and UDP/IP, for example, that's port number 111. every transport has its own assigned, well-known address, so it's giving consistency πŸŽ€ below is a breakdown of each of the procedures RPCBIND supports.

2.2.1 RPCBIND Version 3 🌸

RPCBPROC_SET:

when a program first shows up on a machine, it registers itself with RPCBIND running on that same machine ✨ the program passes its program number r_prog, version number r_vers, network identifier r_netid, universal address r_addr, and the owner of the service r_owner. the procedure returns a boolean response: TRUE if it successfully made the mapping, and FALSE otherwise. it refuses to make a mapping if one already exists for the ordered set (r_prog, r_vers, r_netid) πŸ™…β€β™€οΈ also, neither r_netid nor r_addr can be NULL, and r_netid should be a valid network identifier on the machine making the call. not me being picky, it's just the rules πŸ’€

RPCBPROC_UNSET:

when a program goes offline, it should unregister itself with the RPCBIND program on the same machine 🧹 the parameters and results mean exactly the same thing as they do for RPCBPROC_SET. the mapping of the (r_prog, r_vers, r_netid) tuple with r_addr gets deleted. if r_netid is NULL, then all mappings for the ordered set (r_prog, r_vers, *) and their corresponding universal addresses get deleted, which is a whole glow-up of a cleanup 😭 only the owner of the service or the super-user is allowed to unset a service, so no random gremlins allowed πŸ‘‘

RPCBPROC_GETADDR:

give it a program number r_prog, a version number r_vers, and a network identifier r_netid, and this procedure hands back the universal address where the program is waiting for call requests πŸ” the r_netid field in the argument gets ignored, because the r_netid is inferred from the network identifier of the transport the request came in on. iykyk, it's the smart move 🧠

RPCBPROC_DUMP:

this one lists every single entry in RPCBIND's database πŸ“‹ it takes no parameters and returns a list of program, version, network identifier, and universal addresses. full tea spill β˜•

RPCBPROC_CALLIT:

this procedure lets a caller invoke another remote procedure on the same machine without needing to know that remote procedure's universal address πŸ¦‹ it's meant for supporting broadcasts to arbitrary remote programs through RPCBIND's universal address. the parameters prog, vers, proc, and args are the program number, version number, procedure number, and parameters of the remote procedure. it's giving multitasking queen energy πŸ‘‘

Note - This procedure only sends a response if the procedure was successfully executed and is silent (no response) otherwise. 🀫

the procedure returns the remote program's universal address, and the results of the remote procedure 🎁

RPCBPROC_GETTIME:

this procedure returns the local time on its own machine, in seconds since midnight of the First day of January, 1970 ⏰ literally the original timestamp era πŸ•°οΈ

RPCBPROC_UADDR2TADDR:

this one converts universal addresses into transport specific addresses πŸ”

RPCBPROC_TADDR2UADDR:

and this one goes the other way, converting transport specific addresses into universal addresses πŸ”„ two-way street, we love a symmetrical glow-up πŸ’œ

2.2.2 RPCBIND, Version 4 πŸ’–

version 4 of the RPCBIND protocol includes all of the procedures above, and then adds several more ✨ the new era has arrived 🌈

RPCBPROC_BCAST:

this procedure is identical to the version 3 RPCBPROC_CALLIT procedure πŸ‘― the new name signals that the procedure should be used for broadcast RPCs only. RPCBPROC_INDIRECT, defined below, should be used for indirect RPC calls. so they split the job, and honestly the naming is a whole vibe πŸ’…

RPCBPROC_GETVERSADDR:

this one's similar to RPCBPROC_GETADDR, but the difference is that the r_vers field of the rpcb structure can be used to specify the version you're looking for 🎯 if that version isn't registered, no address is returned. no version, no address, no drama πŸ™…β€β™€οΈ

RPCBPROC_INDIRECT:

this is similar to RPCBPROC_CALLIT, except instead of staying silent about errors (like the program not being registered on the system), this procedure actually returns an indication of the error 🚨 it should not be used for broadcast RPC. it's meant to be used with indirect RPC calls only. know your lane, bestie πŸ’…

RPCBPROC_GETADDRLIST:

this procedure returns a list of addresses for the given rpcb entry πŸ“œ the client may be able to use the results to figure out alternate transports it can use to talk to the server. options, options, options πŸ¦‹

RPCBPROC_GETSTAT:

this one returns statistics on what the RPCBIND server has been up to πŸ“Š the info lists the number and kind of requests the server has received. receipts, bestie 🧾

Note - All procedures except RPCBPROC_SET and RPCBPROC_UNSET can be called by clients running on a machine other than a machine on which RPCBIND is running. RPCBIND only accepts RPCBPROC_SET and RPCBPROC_UNSET requests by clients running on the same machine as the RPCBIND program. πŸ”’ the boundary is real, bestie, so respect it πŸ’œ

3. Port Mapper Program Protocol 🧚

the port mapper program maps RPC program and version numbers to transport-specific port numbers πŸ—ΊοΈ this is what makes dynamic binding of remote programs possible, which is so iconic. the port mapper protocol is different from the newer RPCBIND protocols in that it's transport specific in how it handles addresses. older, but still serving πŸ‘΅βœ¨

3.1 Port Mapper Protocol Specification (in RPC Language) πŸ’–

const PMAP_PORT = 111; /* portmapper port number */

const PMAP_PORT = 111;      /* portmapper port number */

a mapping of (program, version, protocol) to port number:

struct mapping {
   unsigned int prog;
   unsigned int vers;
   unsigned int prot;
   unsigned int port;
};

supported values for the prot field:

const IPPROTO_TCP = 6;      /* protocol number for TCP/IP */
const IPPROTO_UDP = 17;     /* protocol number for UDP/IP */

a list of mappings:

struct *pmaplist {
   mapping map;
   pmaplist next;
};

arguments to callit:

struct call_args {
   unsigned int prog;
   unsigned int vers;
   unsigned int proc;
   opaque args<>;
};

results of callit:

struct call_result {
   unsigned int port;
   opaque res<>;
};

port mapper procedures:

program PMAP_PROG {
   version PMAP_VERS {
      void
      PMAPPROC_NULL(void)         = 0;

      bool
      PMAPPROC_SET(mapping)       = 1;

      bool
      PMAPPROC_UNSET(mapping)     = 2;

      unsigned int
      PMAPPROC_GETPORT(mapping)   = 3;

      pmaplist
      PMAPPROC_DUMP(void)         = 4;

      call_result
      PMAPPROC_CALLIT(call_args)  = 5;
   } = 2;
} = 100000;

3.2 Port Mapper Operation 🌸

the portmapper program currently supports two protocols, UDP and TCP πŸ”Œ you contact it by talking to it on assigned port number 111 (SUNRPC) over either of these protocols. here's a rundown of each portmapper procedure:

PMAPPROC_NULL:

this procedure does no work 😴 by convention, procedure zero of any protocol takes no parameters and returns no results. the quiet icon of the group 🫢

PMAPPROC_SET:

when a program first becomes available on a machine, it registers itself with the port mapper program on the same machine ✨ the program passes its program number prog, version number vers, transport protocol number prot, and the port port on which it awaits service requests. the procedure returns a boolean reply: "TRUE" if it successfully made the mapping, and "FALSE" otherwise. it refuses to make a mapping if one already exists for the tuple (prog, vers, prot) πŸ™…β€β™€οΈ

PMAPPROC_UNSET:

when a program becomes unavailable, it should unregister itself with the port mapper program on the same machine 🧹 the parameters and results mean the same thing as they do for PMAPPROC_SET. the protocol and port number fields of the argument are ignored. 🫠

PMAPPROC_GETPORT:

give it a program number prog, a version number vers, and a transport protocol number prot, and this procedure returns the port number where the program is waiting for call requests 🎯 a port value of zeros means the program has not been registered. the port field of the argument gets ignored. zeros = not registered, no shame πŸ’€

PMAPPROC_DUMP:

this procedure enumerates all entries in the port mapper's database πŸ“‹ it takes no parameters and returns a list of program, version, protocol, and port values. full receipts 🧾

PMAPPROC_CALLIT:

this procedure lets a client call another remote procedure on the same machine without knowing that remote procedure's port number πŸ¦‹ it's meant for supporting broadcasts to arbitrary remote programs through the well-known port mapper's port. the parameters prog, vers, proc, and the bytes of args are the program number, version number, procedure number, and parameters of the remote procedure. a couple of notes, bestie:

(1) This procedure only sends a reply if the procedure was successfully executed and is silent (no reply) otherwise. 🀫 (2) The port mapper communicates with the remote program using UDP only. πŸ“‘

the procedure returns the remote program's port number, and the reply is the reply of the remote procedure 🎁

References πŸ“š

  • [1] Srinivasan, R., "Remote Procedure Call Protocol Version 2", RFC 1831, Sun Microsystems, Inc., August 1995.
  • [2] Srinivasan, R., "XDR: External Data Representation Standard", RFC 1832, Sun Microsystems, Inc., August 1995.

Security Considerations 🚨

Security issues are not discussed in this memo. πŸ’€ bestie, this memo is not going there, so keep your guard up and look elsewhere for that tea πŸ”’

Author's Address πŸŽ€

Raj Srinivasan
Sun Microsystems, Inc.
ONC Technologies
2550 Garcia Avenue
M/S MTV-5-40
Mountain View, CA  94043
USA

Phone: 415-336-2478
Fax:   415-336-6015
EMail: raj@eng.sun.com

🫢 and that's the whole spec, bestie, you literally did it πŸ‘‘πŸŒˆ

πŸ“¦ where this page comes from