URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/rtems-20020807/cpukit/librpc/src/rpc
- from Rev 1028 to Rev 1765
- ↔ Reverse comparison
Rev 1028 → Rev 1765
/PSD.doc/xdr.rfc.ms
0,0 → 1,1058
.\" |
.\" Must use -- tbl -- with this one |
.\" |
.\" @(#)xdr.rfc.ms 2.2 88/08/05 4.0 RPCSRC |
.de BT |
.if \\n%=1 .tl ''- % -'' |
.. |
.ND |
.\" prevent excess underlining in nroff |
.if n .fp 2 R |
.OH 'External Data Representation Standard''Page %' |
.EH 'Page %''External Data Representation Standard' |
.IX "External Data Representation" |
.if \\n%=1 .bp |
.SH |
\&External Data Representation Standard: Protocol Specification |
.IX XDR RFC |
.IX XDR "protocol specification" |
.LP |
.NH 0 |
\&Status of this Standard |
.nr OF 1 |
.IX XDR "RFC status" |
.LP |
Note: This chapter specifies a protocol that Sun Microsystems, Inc., and |
others are using. It has been designated RFC1014 by the ARPA Network |
Information Center. |
.NH 1 |
Introduction |
\& |
.LP |
XDR is a standard for the description and encoding of data. It is |
useful for transferring data between different computer |
architectures, and has been used to communicate data between such |
diverse machines as the Sun Workstation, VAX, IBM-PC, and Cray. |
XDR fits into the ISO presentation layer, and is roughly analogous in |
purpose to X.409, ISO Abstract Syntax Notation. The major difference |
between these two is that XDR uses implicit typing, while X.409 uses |
explicit typing. |
.LP |
XDR uses a language to describe data formats. The language can only |
be used only to describe data; it is not a programming language. |
This language allows one to describe intricate data formats in a |
concise manner. The alternative of using graphical representations |
(itself an informal language) quickly becomes incomprehensible when |
faced with complexity. The XDR language itself is similar to the C |
language [1], just as Courier [4] is similar to Mesa. Protocols such |
as Sun RPC (Remote Procedure Call) and the NFS (Network File System) |
use XDR to describe the format of their data. |
.LP |
The XDR standard makes the following assumption: that bytes (or |
octets) are portable, where a byte is defined to be 8 bits of data. |
A given hardware device should encode the bytes onto the various |
media in such a way that other hardware devices may decode the bytes |
without loss of meaning. For example, the Ethernet standard |
suggests that bytes be encoded in "little-endian" style [2], or least |
significant bit first. |
.NH 2 |
\&Basic Block Size |
.IX XDR "basic block size" |
.IX XDR "block size" |
.LP |
The representation of all items requires a multiple of four bytes (or |
32 bits) of data. The bytes are numbered 0 through n-1. The bytes |
are read or written to some byte stream such that byte m always |
precedes byte m+1. If the n bytes needed to contain the data are not |
a multiple of four, then the n bytes are followed by enough (0 to 3) |
residual zero bytes, r, to make the total byte count a multiple of 4. |
.LP |
We include the familiar graphic box notation for illustration and |
comparison. In most illustrations, each box (delimited by a plus |
sign at the 4 corners and vertical bars and dashes) depicts a byte. |
Ellipses (...) between boxes show zero or more additional bytes where |
required. |
.ie t .DS |
.el .DS L |
\fIA Block\fP |
|
\f(CW+--------+--------+...+--------+--------+...+--------+ |
| byte 0 | byte 1 |...|byte n-1| 0 |...| 0 | |
+--------+--------+...+--------+--------+...+--------+ |
|<-----------n bytes---------->|<------r bytes------>| |
|<-----------n+r (where (n+r) mod 4 = 0)>----------->|\fP |
|
.DE |
.NH 1 |
\&XDR Data Types |
.IX XDR "data types" |
.IX "XDR data types" |
.LP |
Each of the sections that follow describes a data type defined in the |
XDR standard, shows how it is declared in the language, and includes |
a graphic illustration of its encoding. |
.LP |
For each data type in the language we show a general paradigm |
declaration. Note that angle brackets (< and >) denote |
variable length sequences of data and square brackets ([ and ]) denote |
fixed-length sequences of data. "n", "m" and "r" denote integers. |
For the full language specification and more formal definitions of |
terms such as "identifier" and "declaration", refer to |
.I "The XDR Language Specification" , |
below. |
.LP |
For some data types, more specific examples are included. |
A more extensive example of a data description is in |
.I "An Example of an XDR Data Description" |
below. |
.NH 2 |
\&Integer |
.IX XDR integer |
.LP |
An XDR signed integer is a 32-bit datum that encodes an integer in |
the range [-2147483648,2147483647]. The integer is represented in |
two's complement notation. The most and least significant bytes are |
0 and 3, respectively. Integers are declared as follows: |
.ie t .DS |
.el .DS L |
\fIInteger\fP |
|
\f(CW(MSB) (LSB) |
+-------+-------+-------+-------+ |
|byte 0 |byte 1 |byte 2 |byte 3 | |
+-------+-------+-------+-------+ |
<------------32 bits------------>\fP |
.DE |
.NH 2 |
\&Unsigned Integer |
.IX XDR "unsigned integer" |
.IX XDR "integer, unsigned" |
.LP |
An XDR unsigned integer is a 32-bit datum that encodes a nonnegative |
integer in the range [0,4294967295]. It is represented by an |
unsigned binary number whose most and least significant bytes are 0 |
and 3, respectively. An unsigned integer is declared as follows: |
.ie t .DS |
.el .DS L |
\fIUnsigned Integer\fP |
|
\f(CW(MSB) (LSB) |
+-------+-------+-------+-------+ |
|byte 0 |byte 1 |byte 2 |byte 3 | |
+-------+-------+-------+-------+ |
<------------32 bits------------>\fP |
.DE |
.NH 2 |
\&Enumeration |
.IX XDR enumeration |
.LP |
Enumerations have the same representation as signed integers. |
Enumerations are handy for describing subsets of the integers. |
Enumerated data is declared as follows: |
.ft CW |
.DS |
enum { name-identifier = constant, ... } identifier; |
.DE |
For example, the three colors red, yellow, and blue could be |
described by an enumerated type: |
.DS |
.ft CW |
enum { RED = 2, YELLOW = 3, BLUE = 5 } colors; |
.DE |
It is an error to encode as an enum any other integer than those that |
have been given assignments in the enum declaration. |
.NH 2 |
\&Boolean |
.IX XDR boolean |
.LP |
Booleans are important enough and occur frequently enough to warrant |
their own explicit type in the standard. Booleans are declared as |
follows: |
.DS |
.ft CW |
bool identifier; |
.DE |
This is equivalent to: |
.DS |
.ft CW |
enum { FALSE = 0, TRUE = 1 } identifier; |
.DE |
.NH 2 |
\&Hyper Integer and Unsigned Hyper Integer |
.IX XDR "hyper integer" |
.IX XDR "integer, hyper" |
.LP |
The standard also defines 64-bit (8-byte) numbers called hyper |
integer and unsigned hyper integer. Their representations are the |
obvious extensions of integer and unsigned integer defined above. |
They are represented in two's complement notation. The most and |
least significant bytes are 0 and 7, respectively. Their |
declarations: |
.ie t .DS |
.el .DS L |
\fIHyper Integer\fP |
\fIUnsigned Hyper Integer\fP |
|
\f(CW(MSB) (LSB) |
+-------+-------+-------+-------+-------+-------+-------+-------+ |
|byte 0 |byte 1 |byte 2 |byte 3 |byte 4 |byte 5 |byte 6 |byte 7 | |
+-------+-------+-------+-------+-------+-------+-------+-------+ |
<----------------------------64 bits---------------------------->\fP |
.DE |
.NH 2 |
\&Floating-point |
.IX XDR "integer, floating point" |
.IX XDR "floating-point integer" |
.LP |
The standard defines the floating-point data type "float" (32 bits or |
4 bytes). The encoding used is the IEEE standard for normalized |
single-precision floating-point numbers [3]. The following three |
fields describe the single-precision floating-point number: |
.RS |
.IP \fBS\fP: |
The sign of the number. Values 0 and 1 represent positive and |
negative, respectively. One bit. |
.IP \fBE\fP: |
The exponent of the number, base 2. 8 bits are devoted to this |
field. The exponent is biased by 127. |
.IP \fBF\fP: |
The fractional part of the number's mantissa, base 2. 23 bits |
are devoted to this field. |
.RE |
.LP |
Therefore, the floating-point number is described by: |
.DS |
(-1)**S * 2**(E-Bias) * 1.F |
.DE |
It is declared as follows: |
.ie t .DS |
.el .DS L |
\fISingle-Precision Floating-Point\fP |
|
\f(CW+-------+-------+-------+-------+ |
|byte 0 |byte 1 |byte 2 |byte 3 | |
S| E | F | |
+-------+-------+-------+-------+ |
1|<- 8 ->|<-------23 bits------>| |
<------------32 bits------------>\fP |
.DE |
Just as the most and least significant bytes of a number are 0 and 3, |
the most and least significant bits of a single-precision floating- |
point number are 0 and 31. The beginning bit (and most significant |
bit) offsets of S, E, and F are 0, 1, and 9, respectively. Note that |
these numbers refer to the mathematical positions of the bits, and |
NOT to their actual physical locations (which vary from medium to |
medium). |
.LP |
The IEEE specifications should be consulted concerning the encoding |
for signed zero, signed infinity (overflow), and denormalized numbers |
(underflow) [3]. According to IEEE specifications, the "NaN" (not a |
number) is system dependent and should not be used externally. |
.NH 2 |
\&Double-precision Floating-point |
.IX XDR "integer, double-precision floating point" |
.IX XDR "double-precision floating-point integer" |
.LP |
The standard defines the encoding for the double-precision floating- |
point data type "double" (64 bits or 8 bytes). The encoding used is |
the IEEE standard for normalized double-precision floating-point |
numbers [3]. The standard encodes the following three fields, which |
describe the double-precision floating-point number: |
.RS |
.IP \fBS\fP: |
The sign of the number. Values 0 and 1 represent positive and |
negative, respectively. One bit. |
.IP \fBE\fP: |
The exponent of the number, base 2. 11 bits are devoted to this |
field. The exponent is biased by 1023. |
.IP \fBF\fP: |
The fractional part of the number's mantissa, base 2. 52 bits |
are devoted to this field. |
.RE |
.LP |
Therefore, the floating-point number is described by: |
.DS |
(-1)**S * 2**(E-Bias) * 1.F |
.DE |
It is declared as follows: |
.ie t .DS |
.el .DS L |
\fIDouble-Precision Floating-Point\fP |
|
\f(CW+------+------+------+------+------+------+------+------+ |
|byte 0|byte 1|byte 2|byte 3|byte 4|byte 5|byte 6|byte 7| |
S| E | F | |
+------+------+------+------+------+------+------+------+ |
1|<--11-->|<-----------------52 bits------------------->| |
<-----------------------64 bits------------------------->\fP |
.DE |
Just as the most and least significant bytes of a number are 0 and 3, |
the most and least significant bits of a double-precision floating- |
point number are 0 and 63. The beginning bit (and most significant |
bit) offsets of S, E , and F are 0, 1, and 12, respectively. Note |
that these numbers refer to the mathematical positions of the bits, |
and NOT to their actual physical locations (which vary from medium to |
medium). |
.LP |
The IEEE specifications should be consulted concerning the encoding |
for signed zero, signed infinity (overflow), and denormalized numbers |
(underflow) [3]. According to IEEE specifications, the "NaN" (not a |
number) is system dependent and should not be used externally. |
.NH 2 |
\&Fixed-length Opaque Data |
.IX XDR "fixed-length opaque data" |
.IX XDR "opaque data, fixed length" |
.LP |
At times, fixed-length uninterpreted data needs to be passed among |
machines. This data is called "opaque" and is declared as follows: |
.DS |
.ft CW |
opaque identifier[n]; |
.DE |
where the constant n is the (static) number of bytes necessary to |
contain the opaque data. If n is not a multiple of four, then the n |
bytes are followed by enough (0 to 3) residual zero bytes, r, to make |
the total byte count of the opaque object a multiple of four. |
.ie t .DS |
.el .DS L |
\fIFixed-Length Opaque\fP |
|
\f(CW0 1 ... |
+--------+--------+...+--------+--------+...+--------+ |
| byte 0 | byte 1 |...|byte n-1| 0 |...| 0 | |
+--------+--------+...+--------+--------+...+--------+ |
|<-----------n bytes---------->|<------r bytes------>| |
|<-----------n+r (where (n+r) mod 4 = 0)------------>|\fP |
.DE |
.NH 2 |
\&Variable-length Opaque Data |
.IX XDR "variable-length opaque data" |
.IX XDR "opaque data, variable length" |
.LP |
The standard also provides for variable-length (counted) opaque data, |
defined as a sequence of n (numbered 0 through n-1) arbitrary bytes |
to be the number n encoded as an unsigned integer (as described |
below), and followed by the n bytes of the sequence. |
.LP |
Byte m of the sequence always precedes byte m+1 of the sequence, and |
byte 0 of the sequence always follows the sequence's length (count). |
enough (0 to 3) residual zero bytes, r, to make the total byte count |
a multiple of four. Variable-length opaque data is declared in the |
following way: |
.DS |
.ft CW |
opaque identifier<m>; |
.DE |
or |
.DS |
.ft CW |
opaque identifier<>; |
.DE |
The constant m denotes an upper bound of the number of bytes that the |
sequence may contain. If m is not specified, as in the second |
declaration, it is assumed to be (2**32) - 1, the maximum length. |
The constant m would normally be found in a protocol specification. |
For example, a filing protocol may state that the maximum data |
transfer size is 8192 bytes, as follows: |
.DS |
.ft CW |
opaque filedata<8192>; |
.DE |
This can be illustrated as follows: |
.ie t .DS |
.el .DS L |
\fIVariable-Length Opaque\fP |
|
\f(CW0 1 2 3 4 5 ... |
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+ |
| length n |byte0|byte1|...| n-1 | 0 |...| 0 | |
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+ |
|<-------4 bytes------->|<------n bytes------>|<---r bytes--->| |
|<----n+r (where (n+r) mod 4 = 0)---->|\fP |
.DE |
.LP |
It is an error to encode a length greater than the maximum |
described in the specification. |
.NH 2 |
\&String |
.IX XDR string |
.LP |
The standard defines a string of n (numbered 0 through n-1) ASCII |
bytes to be the number n encoded as an unsigned integer (as described |
above), and followed by the n bytes of the string. Byte m of the |
string always precedes byte m+1 of the string, and byte 0 of the |
string always follows the string's length. If n is not a multiple of |
four, then the n bytes are followed by enough (0 to 3) residual zero |
bytes, r, to make the total byte count a multiple of four. Counted |
byte strings are declared as follows: |
.DS |
.ft CW |
string object<m>; |
.DE |
or |
.DS |
.ft CW |
string object<>; |
.DE |
The constant m denotes an upper bound of the number of bytes that a |
string may contain. If m is not specified, as in the second |
declaration, it is assumed to be (2**32) - 1, the maximum length. |
The constant m would normally be found in a protocol specification. |
For example, a filing protocol may state that a file name can be no |
longer than 255 bytes, as follows: |
.DS |
.ft CW |
string filename<255>; |
.DE |
Which can be illustrated as: |
.ie t .DS |
.el .DS L |
\fIA String\fP |
|
\f(CW0 1 2 3 4 5 ... |
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+ |
| length n |byte0|byte1|...| n-1 | 0 |...| 0 | |
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+ |
|<-------4 bytes------->|<------n bytes------>|<---r bytes--->| |
|<----n+r (where (n+r) mod 4 = 0)---->|\fP |
.DE |
.LP |
It is an error to encode a length greater than the maximum |
described in the specification. |
.NH 2 |
\&Fixed-length Array |
.IX XDR "fixed-length array" |
.IX XDR "array, fixed length" |
.LP |
Declarations for fixed-length arrays of homogeneous elements are in |
the following form: |
.DS |
.ft CW |
type-name identifier[n]; |
.DE |
Fixed-length arrays of elements numbered 0 through n-1 are encoded by |
individually encoding the elements of the array in their natural |
order, 0 through n-1. Each element's size is a multiple of four |
bytes. Though all elements are of the same type, the elements may |
have different sizes. For example, in a fixed-length array of |
strings, all elements are of type "string", yet each element will |
vary in its length. |
.ie t .DS |
.el .DS L |
\fIFixed-Length Array\fP |
|
\f(CW+---+---+---+---+---+---+---+---+...+---+---+---+---+ |
| element 0 | element 1 |...| element n-1 | |
+---+---+---+---+---+---+---+---+...+---+---+---+---+ |
|<--------------------n elements------------------->|\fP |
.DE |
.NH 2 |
\&Variable-length Array |
.IX XDR "variable-length array" |
.IX XDR "array, variable length" |
.LP |
Counted arrays provide the ability to encode variable-length arrays |
of homogeneous elements. The array is encoded as the element count n |
(an unsigned integer) followed by the encoding of each of the array's |
elements, starting with element 0 and progressing through element n- |
1. The declaration for variable-length arrays follows this form: |
.DS |
.ft CW |
type-name identifier<m>; |
.DE |
or |
.DS |
.ft CW |
type-name identifier<>; |
.DE |
The constant m specifies the maximum acceptable element count of an |
array; if m is not specified, as in the second declaration, it is |
assumed to be (2**32) - 1. |
.ie t .DS |
.el .DS L |
\fICounted Array\fP |
|
\f(CW0 1 2 3 |
+--+--+--+--+--+--+--+--+--+--+--+--+...+--+--+--+--+ |
| n | element 0 | element 1 |...|element n-1| |
+--+--+--+--+--+--+--+--+--+--+--+--+...+--+--+--+--+ |
|<-4 bytes->|<--------------n elements------------->|\fP |
.DE |
It is an error to encode a value of n that is greater than the |
maximum described in the specification. |
.NH 2 |
\&Structure |
.IX XDR structure |
.LP |
Structures are declared as follows: |
.DS |
.ft CW |
struct { |
component-declaration-A; |
component-declaration-B; |
\&... |
} identifier; |
.DE |
The components of the structure are encoded in the order of their |
declaration in the structure. Each component's size is a multiple of |
four bytes, though the components may be different sizes. |
.ie t .DS |
.el .DS L |
\fIStructure\fP |
|
\f(CW+-------------+-------------+... |
| component A | component B |... |
+-------------+-------------+...\fP |
.DE |
.NH 2 |
\&Discriminated Union |
.IX XDR "discriminated union" |
.IX XDR union discriminated |
.LP |
A discriminated union is a type composed of a discriminant followed |
by a type selected from a set of prearranged types according to the |
value of the discriminant. The type of discriminant is either "int", |
"unsigned int", or an enumerated type, such as "bool". The component |
types are called "arms" of the union, and are preceded by the value |
of the discriminant which implies their encoding. Discriminated |
unions are declared as follows: |
.DS |
.ft CW |
union switch (discriminant-declaration) { |
case discriminant-value-A: |
arm-declaration-A; |
case discriminant-value-B: |
arm-declaration-B; |
\&... |
default: default-declaration; |
} identifier; |
.DE |
Each "case" keyword is followed by a legal value of the discriminant. |
The default arm is optional. If it is not specified, then a valid |
encoding of the union cannot take on unspecified discriminant values. |
The size of the implied arm is always a multiple of four bytes. |
.LP |
The discriminated union is encoded as its discriminant followed by |
the encoding of the implied arm. |
.ie t .DS |
.el .DS L |
\fIDiscriminated Union\fP |
|
\f(CW0 1 2 3 |
+---+---+---+---+---+---+---+---+ |
| discriminant | implied arm | |
+---+---+---+---+---+---+---+---+ |
|<---4 bytes--->|\fP |
.DE |
.NH 2 |
\&Void |
.IX XDR void |
.LP |
An XDR void is a 0-byte quantity. Voids are useful for describing |
operations that take no data as input or no data as output. They are |
also useful in unions, where some arms may contain data and others do |
not. The declaration is simply as follows: |
.DS |
.ft CW |
void; |
.DE |
Voids are illustrated as follows: |
.ie t .DS |
.el .DS L |
\fIVoid\fP |
|
\f(CW ++ |
|| |
++ |
--><-- 0 bytes\fP |
.DE |
.NH 2 |
\&Constant |
.IX XDR constant |
.LP |
The data declaration for a constant follows this form: |
.DS |
.ft CW |
const name-identifier = n; |
.DE |
"const" is used to define a symbolic name for a constant; it does not |
declare any data. The symbolic constant may be used anywhere a |
regular constant may be used. For example, the following defines a |
symbolic constant DOZEN, equal to 12. |
.DS |
.ft CW |
const DOZEN = 12; |
.DE |
.NH 2 |
\&Typedef |
.IX XDR typedef |
.LP |
"typedef" does not declare any data either, but serves to define new |
identifiers for declaring data. The syntax is: |
.DS |
.ft CW |
typedef declaration; |
.DE |
The new type name is actually the variable name in the declaration |
part of the typedef. For example, the following defines a new type |
called "eggbox" using an existing type called "egg": |
.DS |
.ft CW |
typedef egg eggbox[DOZEN]; |
.DE |
Variables declared using the new type name have the same type as the |
new type name would have in the typedef, if it was considered a |
variable. For example, the following two declarations are equivalent |
in declaring the variable "fresheggs": |
.DS |
.ft CW |
eggbox fresheggs; |
egg fresheggs[DOZEN]; |
.DE |
When a typedef involves a struct, enum, or union definition, there is |
another (preferred) syntax that may be used to define the same type. |
In general, a typedef of the following form: |
.DS |
.ft CW |
typedef <<struct, union, or enum definition>> identifier; |
.DE |
may be converted to the alternative form by removing the "typedef" |
part and placing the identifier after the "struct", "union", or |
"enum" keyword, instead of at the end. For example, here are the two |
ways to define the type "bool": |
.DS |
.ft CW |
typedef enum { /* \fIusing typedef\fP */ |
FALSE = 0, |
TRUE = 1 |
} bool; |
|
enum bool { /* \fIpreferred alternative\fP */ |
FALSE = 0, |
TRUE = 1 |
}; |
.DE |
The reason this syntax is preferred is one does not have to wait |
until the end of a declaration to figure out the name of the new |
type. |
.NH 2 |
\&Optional-data |
.IX XDR "optional data" |
.IX XDR "data, optional" |
.LP |
Optional-data is one kind of union that occurs so frequently that we |
give it a special syntax of its own for declaring it. It is declared |
as follows: |
.DS |
.ft CW |
type-name *identifier; |
.DE |
This is equivalent to the following union: |
.DS |
.ft CW |
union switch (bool opted) { |
case TRUE: |
type-name element; |
case FALSE: |
void; |
} identifier; |
.DE |
It is also equivalent to the following variable-length array |
declaration, since the boolean "opted" can be interpreted as the |
length of the array: |
.DS |
.ft CW |
type-name identifier<1>; |
.DE |
Optional-data is not so interesting in itself, but it is very useful |
for describing recursive data-structures such as linked-lists and |
trees. For example, the following defines a type "stringlist" that |
encodes lists of arbitrary length strings: |
.DS |
.ft CW |
struct *stringlist { |
string item<>; |
stringlist next; |
}; |
.DE |
It could have been equivalently declared as the following union: |
.DS |
.ft CW |
union stringlist switch (bool opted) { |
case TRUE: |
struct { |
string item<>; |
stringlist next; |
} element; |
case FALSE: |
void; |
}; |
.DE |
or as a variable-length array: |
.DS |
.ft CW |
struct stringlist<1> { |
string item<>; |
stringlist next; |
}; |
.DE |
Both of these declarations obscure the intention of the stringlist |
type, so the optional-data declaration is preferred over both of |
them. The optional-data type also has a close correlation to how |
recursive data structures are represented in high-level languages |
such as Pascal or C by use of pointers. In fact, the syntax is the |
same as that of the C language for pointers. |
.NH 2 |
\&Areas for Future Enhancement |
.IX XDR futures |
.LP |
The XDR standard lacks representations for bit fields and bitmaps, |
since the standard is based on bytes. Also missing are packed (or |
binary-coded) decimals. |
.LP |
The intent of the XDR standard was not to describe every kind of data |
that people have ever sent or will ever want to send from machine to |
machine. Rather, it only describes the most commonly used data-types |
of high-level languages such as Pascal or C so that applications |
written in these languages will be able to communicate easily over |
some medium. |
.LP |
One could imagine extensions to XDR that would let it describe almost |
any existing protocol, such as TCP. The minimum necessary for this |
are support for different block sizes and byte-orders. The XDR |
discussed here could then be considered the 4-byte big-endian member |
of a larger XDR family. |
.NH 1 |
\&Discussion |
.sp 2 |
.NH 2 |
\&Why a Language for Describing Data? |
.IX XDR language |
.LP |
There are many advantages in using a data-description language such |
as XDR versus using diagrams. Languages are more formal than |
diagrams and lead to less ambiguous descriptions of data. |
Languages are also easier to understand and allow one to think of |
other issues instead of the low-level details of bit-encoding. |
Also, there is a close analogy between the types of XDR and a |
high-level language such as C or Pascal. This makes the |
implementation of XDR encoding and decoding modules an easier task. |
Finally, the language specification itself is an ASCII string that |
can be passed from machine to machine to perform on-the-fly data |
interpretation. |
.NH 2 |
\&Why Only one Byte-Order for an XDR Unit? |
.IX XDR "byte order" |
.LP |
Supporting two byte-orderings requires a higher level protocol for |
determining in which byte-order the data is encoded. Since XDR is |
not a protocol, this can't be done. The advantage of this, though, |
is that data in XDR format can be written to a magnetic tape, for |
example, and any machine will be able to interpret it, since no |
higher level protocol is necessary for determining the byte-order. |
.NH 2 |
\&Why does XDR use Big-Endian Byte-Order? |
.LP |
Yes, it is unfair, but having only one byte-order means you have to |
be unfair to somebody. Many architectures, such as the Motorola |
68000 and IBM 370, support the big-endian byte-order. |
.NH 2 |
\&Why is the XDR Unit Four Bytes Wide? |
.LP |
There is a tradeoff in choosing the XDR unit size. Choosing a small |
size such as two makes the encoded data small, but causes alignment |
problems for machines that aren't aligned on these boundaries. A |
large size such as eight means the data will be aligned on virtually |
every machine, but causes the encoded data to grow too big. We chose |
four as a compromise. Four is big enough to support most |
architectures efficiently, except for rare machines such as the |
eight-byte aligned Cray. Four is also small enough to keep the |
encoded data restricted to a reasonable size. |
.NH 2 |
\&Why must Variable-Length Data be Padded with Zeros? |
.IX XDR "variable-length data" |
.LP |
It is desirable that the same data encode into the same thing on all |
machines, so that encoded data can be meaningfully compared or |
checksummed. Forcing the padded bytes to be zero ensures this. |
.NH 2 |
\&Why is there No Explicit Data-Typing? |
.LP |
Data-typing has a relatively high cost for what small advantages it |
may have. One cost is the expansion of data due to the inserted type |
fields. Another is the added cost of interpreting these type fields |
and acting accordingly. And most protocols already know what type |
they expect, so data-typing supplies only redundant information. |
However, one can still get the benefits of data-typing using XDR. One |
way is to encode two things: first a string which is the XDR data |
description of the encoded data, and then the encoded data itself. |
Another way is to assign a value to all the types in XDR, and then |
define a universal type which takes this value as its discriminant |
and for each value, describes the corresponding data type. |
.NH 1 |
\&The XDR Language Specification |
.IX XDR language |
.sp 1 |
.NH 2 |
\&Notational Conventions |
.IX "XDR language" notation |
.LP |
This specification uses an extended Backus-Naur Form notation for |
describing the XDR language. Here is a brief description of the |
notation: |
.IP 1. |
The characters |
.I | , |
.I ( , |
.I ) , |
.I [ , |
.I ] , |
.I " , |
and |
.I * |
are special. |
.IP 2. |
Terminal symbols are strings of any characters surrounded by |
double quotes. |
.IP 3. |
Non-terminal symbols are strings of non-special characters. |
.IP 4. |
Alternative items are separated by a vertical bar ("\fI|\fP"). |
.IP 5. |
Optional items are enclosed in brackets. |
.IP 6. |
Items are grouped together by enclosing them in parentheses. |
.IP 7. |
A |
.I * |
following an item means 0 or more occurrences of that item. |
.LP |
For example, consider the following pattern: |
.DS L |
"a " "very" (", " " very")* [" cold " "and"] " rainy " ("day" | "night") |
.DE |
.LP |
An infinite number of strings match this pattern. A few of them |
are: |
.DS |
"a very rainy day" |
"a very, very rainy day" |
"a very cold and rainy day" |
"a very, very, very cold and rainy night" |
.DE |
.NH 2 |
\&Lexical Notes |
.IP 1. |
Comments begin with '/*' and terminate with '*/'. |
.IP 2. |
White space serves to separate items and is otherwise ignored. |
.IP 3. |
An identifier is a letter followed by an optional sequence of |
letters, digits or underbar ('_'). The case of identifiers is |
not ignored. |
.IP 4. |
A constant is a sequence of one or more decimal digits, |
optionally preceded by a minus-sign ('-'). |
.NH 2 |
\&Syntax Information |
.IX "XDR language" syntax |
.DS |
.ft CW |
declaration: |
type-specifier identifier |
| type-specifier identifier "[" value "]" |
| type-specifier identifier "<" [ value ] ">" |
| "opaque" identifier "[" value "]" |
| "opaque" identifier "<" [ value ] ">" |
| "string" identifier "<" [ value ] ">" |
| type-specifier "*" identifier |
| "void" |
.DE |
.DS |
.ft CW |
value: |
constant |
| identifier |
|
type-specifier: |
[ "unsigned" ] "int" |
| [ "unsigned" ] "hyper" |
| "float" |
| "double" |
| "bool" |
| enum-type-spec |
| struct-type-spec |
| union-type-spec |
| identifier |
.DE |
.DS |
.ft CW |
enum-type-spec: |
"enum" enum-body |
|
enum-body: |
"{" |
( identifier "=" value ) |
( "," identifier "=" value )* |
"}" |
.DE |
.DS |
.ft CW |
struct-type-spec: |
"struct" struct-body |
|
struct-body: |
"{" |
( declaration ";" ) |
( declaration ";" )* |
"}" |
.DE |
.DS |
.ft CW |
union-type-spec: |
"union" union-body |
|
union-body: |
"switch" "(" declaration ")" "{" |
( "case" value ":" declaration ";" ) |
( "case" value ":" declaration ";" )* |
[ "default" ":" declaration ";" ] |
"}" |
|
constant-def: |
"const" identifier "=" constant ";" |
.DE |
.DS |
.ft CW |
type-def: |
"typedef" declaration ";" |
| "enum" identifier enum-body ";" |
| "struct" identifier struct-body ";" |
| "union" identifier union-body ";" |
|
definition: |
type-def |
| constant-def |
|
specification: |
definition * |
.DE |
.NH 3 |
\&Syntax Notes |
.IX "XDR language" syntax |
.LP |
.IP 1. |
The following are keywords and cannot be used as identifiers: |
"bool", "case", "const", "default", "double", "enum", "float", |
"hyper", "opaque", "string", "struct", "switch", "typedef", "union", |
"unsigned" and "void". |
.IP 2. |
Only unsigned constants may be used as size specifications for |
arrays. If an identifier is used, it must have been declared |
previously as an unsigned constant in a "const" definition. |
.IP 3. |
Constant and type identifiers within the scope of a specification |
are in the same name space and must be declared uniquely within this |
scope. |
.IP 4. |
Similarly, variable names must be unique within the scope of |
struct and union declarations. Nested struct and union declarations |
create new scopes. |
.IP 5. |
The discriminant of a union must be of a type that evaluates to |
an integer. That is, "int", "unsigned int", "bool", an enumerated |
type or any typedefed type that evaluates to one of these is legal. |
Also, the case values must be one of the legal values of the |
discriminant. Finally, a case value may not be specified more than |
once within the scope of a union declaration. |
.NH 1 |
\&An Example of an XDR Data Description |
.LP |
Here is a short XDR data description of a thing called a "file", |
which might be used to transfer files from one machine to another. |
.ie t .DS |
.el .DS L |
.ft CW |
|
const MAXUSERNAME = 32; /*\fI max length of a user name \fP*/ |
const MAXFILELEN = 65535; /*\fI max length of a file \fP*/ |
const MAXNAMELEN = 255; /*\fI max length of a file name \fP*/ |
|
.ft I |
/* |
* Types of files: |
*/ |
.ft CW |
|
enum filekind { |
TEXT = 0, /*\fI ascii data \fP*/ |
DATA = 1, /*\fI raw data \fP*/ |
EXEC = 2 /*\fI executable \fP*/ |
}; |
|
.ft I |
/* |
* File information, per kind of file: |
*/ |
.ft CW |
|
union filetype switch (filekind kind) { |
case TEXT: |
void; /*\fI no extra information \fP*/ |
case DATA: |
string creator<MAXNAMELEN>; /*\fI data creator \fP*/ |
case EXEC: |
string interpretor<MAXNAMELEN>; /*\fI program interpretor \fP*/ |
}; |
|
.ft I |
/* |
* A complete file: |
*/ |
.ft CW |
|
struct file { |
string filename<MAXNAMELEN>; /*\fI name of file \fP*/ |
filetype type; /*\fI info about file \fP*/ |
string owner<MAXUSERNAME>; /*\fI owner of file \fP*/ |
opaque data<MAXFILELEN>; /*\fI file data \fP*/ |
}; |
.DE |
.LP |
Suppose now that there is a user named "john" who wants to store |
his lisp program "sillyprog" that contains just the data "(quit)". |
His file would be encoded as follows: |
.TS |
box tab (&) ; |
lfI lfI lfI lfI |
rfL rfL rfL l . |
Offset&Hex Bytes&ASCII&Description |
_ |
0&00 00 00 09&....&Length of filename = 9 |
4&73 69 6c 6c&sill&Filename characters |
8&79 70 72 6f&ypro& ... and more characters ... |
12&67 00 00 00&g...& ... and 3 zero-bytes of fill |
16&00 00 00 02&....&Filekind is EXEC = 2 |
20&00 00 00 04&....&Length of interpretor = 4 |
24&6c 69 73 70&lisp&Interpretor characters |
28&00 00 00 04&....&Length of owner = 4 |
32&6a 6f 68 6e&john&Owner characters |
36&00 00 00 06&....&Length of file data = 6 |
40&28 71 75 69&(qui&File data bytes ... |
44&74 29 00 00&t)..& ... and 2 zero-bytes of fill |
.TE |
.NH 1 |
\&References |
.LP |
[1] Brian W. Kernighan & Dennis M. Ritchie, "The C Programming |
Language", Bell Laboratories, Murray Hill, New Jersey, 1978. |
.LP |
[2] Danny Cohen, "On Holy Wars and a Plea for Peace", IEEE Computer, |
October 1981. |
.LP |
[3] "IEEE Standard for Binary Floating-Point Arithmetic", ANSI/IEEE |
Standard 754-1985, Institute of Electrical and Electronics |
Engineers, August 1985. |
.LP |
[4] "Courier: The Remote Procedure Call Protocol", XEROX |
Corporation, XSIS 038112, December 1981. |
/PSD.doc/rpc.prog.ms
0,0 → 1,2684
.\" |
.\" Must use -- tbl and pic -- with this one |
.\" |
.\" @(#)rpc.prog.ms 2.3 88/08/11 4.0 RPCSRC |
.de BT |
.if \\n%=1 .tl ''- % -'' |
.. |
.IX "Network Programming" "" "" "" PAGE MAJOR |
.nr OF 0 |
.ND |
.\" prevent excess underlining in nroff |
.if n .fp 2 R |
.OH 'Remote Procedure Call Programming Guide''Page %' |
.EH 'Page %''Remote Procedure Call Programming Guide' |
.SH |
\&Remote Procedure Call Programming Guide |
.nr OF 1 |
.IX "RPC Programming Guide" |
.LP |
This document assumes a working knowledge of network theory. It is |
intended for programmers who wish to write network applications using |
remote procedure calls (explained below), and who want to understand |
the RPC mechanisms usually hidden by the |
.I rpcgen(1) |
protocol compiler. |
.I rpcgen |
is described in detail in the previous chapter, the |
.I "\fBrpcgen\fP \fIProgramming Guide\fP". |
.SH |
Note: |
.I |
.IX rpcgen "" \fIrpcgen\fP |
Before attempting to write a network application, or to convert an |
existing non-network application to run over the network, you may want to |
understand the material in this chapter. However, for most applications, |
you can circumvent the need to cope with the details presented here by using |
.I rpcgen . |
The |
.I "Generating XDR Routines" |
section of that chapter contains the complete source for a working RPC |
service\(ema remote directory listing service which uses |
.I rpcgen |
to generate XDR routines as well as client and server stubs. |
.LP |
.LP |
What are remote procedure calls? Simply put, they are the high-level |
communications paradigm used in the operating system. |
RPC presumes the existence of |
low-level networking mechanisms (such as TCP/IP and UDP/IP), and upon them |
it implements a logical client to server communications system designed |
specifically for the support of network applications. With RPC, the client |
makes a procedure call to send a data packet to the server. When the |
packet arrives, the server calls a dispatch routine, performs whatever |
service is requested, sends back the reply, and the procedure call returns |
to the client. |
.NH 0 |
\&Layers of RPC |
.IX "layers of RPC" |
.IX "RPC" "layers" |
.LP |
The RPC interface can be seen as being divided into three layers.\** |
.FS |
For a complete specification of the routines in the remote procedure |
call Library, see the |
.I rpc(3N) |
manual page. |
.FE |
.LP |
.I "The Highest Layer:" |
.IX RPC "The Highest Layer" |
The highest layer is totally transparent to the operating system, |
machine and network upon which is is run. It's probably best to |
think of this level as a way of |
.I using |
RPC, rather than as |
a \fIpart of\fP RPC proper. Programmers who write RPC routines |
should (almost) always make this layer available to others by way |
of a simple C front end that entirely hides the networking. |
.LP |
To illustrate, at this level a program can simply make a call to |
.I rnusers (), |
a C routine which returns the number of users on a remote machine. |
The user is not explicitly aware of using RPC \(em they simply |
call a procedure, just as they would call |
.I malloc() . |
.LP |
.I "The Middle Layer:" |
.IX RPC "The Middle Layer" |
The middle layer is really \*QRPC proper.\*U Here, the user doesn't |
need to consider details about sockets, the UNIX system, or other low-level |
implementation mechanisms. They simply make remote procedure calls |
to routines on other machines. The selling point here is simplicity. |
It's this layer that allows RPC to pass the \*Qhello world\*U test \(em |
simple things should be simple. The middle-layer routines are used |
for most applications. |
.LP |
RPC calls are made with the system routines |
.I registerrpc() |
.I callrpc() |
and |
.I svc_run (). |
The first two of these are the most fundamental: |
.I registerrpc() |
obtains a unique system-wide procedure-identification number, and |
.I callrpc() |
actually executes a remote procedure call. At the middle level, a |
call to |
.I rnusers() |
is implemented by way of these two routines. |
.LP |
The middle layer is unfortunately rarely used in serious programming |
due to its inflexibility (simplicity). It does not allow timeout |
specifications or the choice of transport. It allows no UNIX |
process control or flexibility in case of errors. It doesn't support |
multiple kinds of call authentication. The programmer rarely needs |
all these kinds of control, but one or two of them is often necessary. |
.LP |
.I "The Lowest Layer:" |
.IX RPC "The Lowest Layer" |
The lowest layer does allow these details to be controlled by the |
programmer, and for that reason it is often necessary. Programs |
written at this level are also most efficient, but this is rarely a |
real issue \(em since RPC clients and servers rarely generate |
heavy network loads. |
.LP |
Although this document only discusses the interface to C, |
remote procedure calls can be made from any language. |
Even though this document discusses RPC |
when it is used to communicate |
between processes on different machines, |
it works just as well for communication |
between different processes on the same machine. |
.br |
.KS |
.NH 2 |
\&The RPC Paradigm |
.IX RPC paradigm |
.LP |
Here is a diagram of the RPC paradigm: |
.LP |
\fBFigure 1-1\fI Network Communication with the Remote Reocedure Call\fR |
.LP |
.PS |
L1: arrow down 1i "client " rjust "program " rjust |
L2: line right 1.5i "\fIcallrpc\fP" "function" |
move up 1.5i; line dotted down 6i; move up 4.5i |
arrow right 1i |
L3: arrow down 1i "invoke " rjust "service " rjust |
L4: arrow right 1.5i "call" "service" |
L5: arrow down 1i " service" ljust " executes" ljust |
L6: arrow left 1.5i "\fIreturn\fP" "answer" |
L7: arrow down 1i "request " rjust "completed " rjust |
L8: line left 1i |
arrow left 1.5i "\fIreturn\fP" "reply" |
L9: arrow down 1i "program " rjust "continues " rjust |
line dashed down from L2 to L9 |
line dashed down from L4 to L7 |
line dashed up 1i from L3 "service " rjust "daemon " rjust |
arrow dashed down 1i from L8 |
move right 1i from L3 |
box invis "Machine B" |
move left 1.2i from L2; move down |
box invis "Machine A" |
.PE |
.KE |
.KS |
.NH 1 |
\&Higher Layers of RPC |
.NH 2 |
\&Highest Layer |
.IX "highest layer of RPC" |
.IX RPC "highest layer" |
.LP |
Imagine you're writing a program that needs to know |
how many users are logged into a remote machine. |
You can do this by calling the RPC library routine |
.I rnusers() |
as illustrated below: |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
|
main(argc, argv) |
int argc; |
char **argv; |
{ |
int num; |
|
if (argc != 2) { |
fprintf(stderr, "usage: rnusers hostname\en"); |
exit(1); |
} |
if ((num = rnusers(argv[1])) < 0) { |
fprintf(stderr, "error: rnusers\en"); |
exit(-1); |
} |
printf("%d users on %s\en", num, argv[1]); |
exit(0); |
} |
.DE |
.KE |
RPC library routines such as |
.I rnusers() |
are in the RPC services library |
.I librpcsvc.a |
Thus, the program above should be compiled with |
.DS |
.ft CW |
% cc \fIprogram.c -lrpcsvc\fP |
.DE |
.I rnusers (), |
like the other RPC library routines, is documented in section 3R |
of the |
.I "System Interface Manual for the Sun Workstation" , |
the same section which documents the standard Sun RPC services. |
.IX "RPC Services" |
See the |
.I intro(3R) |
manual page for an explanation of the documentation strategy |
for these services and their RPC protocols. |
.LP |
Here are some of the RPC service library routines available to the |
C programmer: |
.LP |
\fBTable 3-3\fI RPC Service Library Routines\RP |
.TS |
box tab (&) ; |
cfI cfI |
lfL l . |
Routine&Description |
_ |
.sp.5 |
rnusers&Return number of users on remote machine |
rusers&Return information about users on remote machine |
havedisk&Determine if remote machine has disk |
rstats&Get performance data from remote kernel |
rwall&Write to specified remote machines |
yppasswd&Update user password in Yellow Pages |
.TE |
.LP |
Other RPC services \(em for example |
.I ether() |
.I mount |
.I rquota() |
and |
.I spray |
\(em are not available to the C programmer as library routines. |
They do, however, |
have RPC program numbers so they can be invoked with |
.I callrpc() |
which will be discussed in the next section. Most of them also |
have compilable |
.I rpcgen(1) |
protocol description files. (The |
.I rpcgen |
protocol compiler radically simplifies the process of developing |
network applications. |
See the \fBrpcgen\fI Programming Guide\fR |
for detailed information about |
.I rpcgen |
and |
.I rpcgen |
protocol description files). |
.KS |
.NH 2 |
\&Intermediate Layer |
.IX "intermediate layer of RPC" |
.IX "RPC" "intermediate layer" |
.LP |
The simplest interface, which explicitly makes RPC calls, uses the |
functions |
.I callrpc() |
and |
.I registerrpc() |
Using this method, the number of remote users can be gotten as follows: |
.ie t .DS |
.el .DS L |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <utmp.h> |
#include <rpcsvc/rusers.h> |
|
main(argc, argv) |
int argc; |
char **argv; |
{ |
unsigned long nusers; |
int stat; |
|
if (argc != 2) { |
fprintf(stderr, "usage: nusers hostname\en"); |
exit(-1); |
} |
if (stat = callrpc(argv[1], |
RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, |
xdr_void, 0, xdr_u_long, &nusers) != 0) { |
clnt_perrno(stat); |
exit(1); |
} |
printf("%d users on %s\en", nusers, argv[1]); |
exit(0); |
} |
.DE |
.KE |
Each RPC procedure is uniquely defined by a program number, |
version number, and procedure number. The program number |
specifies a group of related remote procedures, each of |
which has a different procedure number. Each program also |
has a version number, so when a minor change is made to a |
remote service (adding a new procedure, for example), a new |
program number doesn't have to be assigned. When you want |
to call a procedure to find the number of remote users, you |
look up the appropriate program, version and procedure numbers |
in a manual, just as you look up the name of a memory allocator |
when you want to allocate memory. |
.LP |
The simplest way of making remote procedure calls is with the the RPC |
library routine |
.I callrpc() |
It has eight parameters. The first is the name of the remote server |
machine. The next three parameters are the program, version, and procedure |
numbers\(emtogether they identify the procedure to be called. |
The fifth and sixth parameters are an XDR filter and an argument to |
be encoded and passed to the remote procedure. |
The final two parameters are a filter for decoding the results |
returned by the remote procedure and a pointer to the place where |
the procedure's results are to be stored. Multiple arguments and |
results are handled by embedding them in structures. If |
.I callrpc() |
completes successfully, it returns zero; else it returns a nonzero |
value. The return codes (of type |
.IX "enum clnt_stat (in RPC programming)" "" "\fIenum clnt_stat\fP (in RPC programming)" |
cast into an integer) are found in |
.I <rpc/clnt.h> . |
.LP |
Since data types may be represented differently on different machines, |
.I callrpc() |
needs both the type of the RPC argument, as well as |
a pointer to the argument itself (and similarly for the result). For |
.I RUSERSPROC_NUM , |
the return value is an |
.I "unsigned long" |
so |
.I callrpc() |
has |
.I xdr_u_long() |
as its first return parameter, which says |
that the result is of type |
.I "unsigned long" |
and |
.I &nusers |
as its second return parameter, |
which is a pointer to where the long result will be placed. Since |
.I RUSERSPROC_NUM |
takes no argument, the argument parameter of |
.I callrpc() |
is |
.I xdr_void (). |
.LP |
After trying several times to deliver a message, if |
.I callrpc() |
gets no answer, it returns with an error code. |
The delivery mechanism is UDP, |
which stands for User Datagram Protocol. |
Methods for adjusting the number of retries |
or for using a different protocol require you to use the lower |
layer of the RPC library, discussed later in this document. |
The remote server procedure |
corresponding to the above might look like this: |
.ie t .DS |
.el .DS L |
.ft CW |
.ft CW |
char * |
nuser(indata) |
char *indata; |
{ |
unsigned long nusers; |
|
.ft I |
/* |
* Code here to compute the number of users |
* and place result in variable \fInusers\fP. |
*/ |
.ft CW |
return((char *)&nusers); |
} |
.DE |
.LP |
It takes one argument, which is a pointer to the input |
of the remote procedure call (ignored in our example), |
and it returns a pointer to the result. |
In the current version of C, |
character pointers are the generic pointers, |
so both the input argument and the return value are cast to |
.I "char *" . |
.LP |
Normally, a server registers all of the RPC calls it plans |
to handle, and then goes into an infinite loop waiting to service requests. |
In this example, there is only a single procedure |
to register, so the main body of the server would look like this: |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <utmp.h> |
#include <rpcsvc/rusers.h> |
|
char *nuser(); |
|
main() |
{ |
registerrpc(RUSERSPROG, RUSERSVERS, RUSERSPROC_NUM, |
nuser, xdr_void, xdr_u_long); |
svc_run(); /* \fINever returns\fP */ |
fprintf(stderr, "Error: svc_run returned!\en"); |
exit(1); |
} |
.DE |
.LP |
The |
.I registerrpc() |
routine registers a C procedure as corresponding to a |
given RPC procedure number. The first three parameters, |
.I RUSERPROG , |
.I RUSERSVERS , |
and |
.I RUSERSPROC_NUM |
are the program, version, and procedure numbers |
of the remote procedure to be registered; |
.I nuser() |
is the name of the local procedure that implements the remote |
procedure; and |
.I xdr_void() |
and |
.I xdr_u_long() |
are the XDR filters for the remote procedure's arguments and |
results, respectively. (Multiple arguments or multiple results |
are passed as structures). |
.LP |
Only the UDP transport mechanism can use |
.I registerrpc() |
thus, it is always safe in conjunction with calls generated by |
.I callrpc() . |
.SH |
.IX "UDP 8K warning" |
Warning: the UDP transport mechanism can only deal with |
arguments and results less than 8K bytes in length. |
.LP |
.LP |
After registering the local procedure, the server program's |
main procedure calls |
.I svc_run (), |
the RPC library's remote procedure dispatcher. It is this |
function that calls the remote procedures in response to RPC |
call messages. Note that the dispatcher takes care of decoding |
remote procedure arguments and encoding results, using the XDR |
filters specified when the remote procedure was registered. |
.NH 2 |
\&Assigning Program Numbers |
.IX "program number assignment" |
.IX "assigning program numbers" |
.LP |
Program numbers are assigned in groups of |
.I 0x20000000 |
according to the following chart: |
.DS |
.ft CW |
0x0 - 0x1fffffff \fRDefined by Sun\fP |
0x20000000 - 0x3fffffff \fRDefined by user\fP |
0x40000000 - 0x5fffffff \fRTransient\fP |
0x60000000 - 0x7fffffff \fRReserved\fP |
0x80000000 - 0x9fffffff \fRReserved\fP |
0xa0000000 - 0xbfffffff \fRReserved\fP |
0xc0000000 - 0xdfffffff \fRReserved\fP |
0xe0000000 - 0xffffffff \fRReserved\fP |
.ft R |
.DE |
Sun Microsystems administers the first group of numbers, which |
should be identical for all Sun customers. If a customer |
develops an application that might be of general interest, that |
application should be given an assigned number in the first |
range. The second group of numbers is reserved for specific |
customer applications. This range is intended primarily for |
debugging new programs. The third group is reserved for |
applications that generate program numbers dynamically. The |
final groups are reserved for future use, and should not be |
used. |
.LP |
To register a protocol specification, send a request by network |
mail to |
.I rpc@sun |
or write to: |
.DS |
RPC Administrator |
Sun Microsystems |
2550 Garcia Ave. |
Mountain View, CA 94043 |
.DE |
Please include a compilable |
.I rpcgen |
\*Q.x\*U file describing your protocol. |
You will be given a unique program number in return. |
.IX RPC administration |
.IX administration "of RPC" |
.LP |
The RPC program numbers and protocol specifications |
of standard Sun RPC services can be |
found in the include files in |
.I "/usr/include/rpcsvc" . |
These services, however, constitute only a small subset |
of those which have been registered. The complete list of |
registered programs, as of the time when this manual was |
printed, is: |
.LP |
\fBTable 3-2\fI RPC Registered Programs\fR |
.TS H |
box tab (&) ; |
lfBI lfBI lfBI |
lfL lfL lfI . |
RPC Number&Program&Description |
_ |
.TH |
.sp.5 |
100000&PMAPPROG&portmapper |
100001&RSTATPROG&remote stats |
100002&RUSERSPROG&remote users |
100003&NFSPROG&nfs |
100004&YPPROG&Yellow Pages |
100005&MOUNTPROG&mount demon |
100006&DBXPROG&remote dbx |
100007&YPBINDPROG&yp binder |
100008&WALLPROG&shutdown msg |
100009&YPPASSWDPROG&yppasswd server |
100010ÐERSTATPROGðer stats |
100011&RQUOTAPROG&disk quotas |
100012&SPRAYPROG&spray packets |
100013&IBM3270PROG&3270 mapper |
100014&IBMRJEPROG&RJE mapper |
100015&SELNSVCPROG&selection service |
100016&RDATABASEPROG&remote database access |
100017&REXECPROG&remote execution |
100018&ALICEPROG&Alice Office Automation |
100019&SCHEDPROG&scheduling service |
100020&LOCKPROG&local lock manager |
100021&NETLOCKPROG&network lock manager |
100022&X25PROG&x.25 inr protocol |
100023&STATMON1PROG&status monitor 1 |
100024&STATMON2PROG&status monitor 2 |
100025&SELNLIBPROG&selection library |
100026&BOOTPARAMPROG&boot parameters service |
100027&MAZEPROG&mazewars game |
100028&YPUPDATEPROG&yp update |
100029&KEYSERVEPROG&key server |
100030&SECURECMDPROG&secure login |
100031&NETFWDIPROG&nfs net forwarder init |
100032&NETFWDTPROG&nfs net forwarder trans |
100033&SUNLINKMAP_PROG&sunlink MAP |
100034&NETMONPROG&network monitor |
100035&DBASEPROG&lightweight database |
100036&PWDAUTHPROG&password authorization |
100037&TFSPROG&translucent file svc |
100038&NSEPROG&nse server |
100039&NSE_ACTIVATE_PROG&nse activate daemon |
.sp .2i |
150001&PCNFSDPROG&pc passwd authorization |
.sp .2i |
200000&PYRAMIDLOCKINGPROG&Pyramid-locking |
200001&PYRAMIDSYS5&Pyramid-sys5 |
200002&CADDS_IMAGE&CV cadds_image |
.sp .2i |
300001&ADT_RFLOCKPROG&ADT file locking |
.TE |
.NH 2 |
\&Passing Arbitrary Data Types |
.IX "arbitrary data types" |
.LP |
In the previous example, the RPC call passes a single |
.I "unsigned long" |
RPC can handle arbitrary data structures, regardless of |
different machines' byte orders or structure layout conventions, |
by always converting them to a network standard called |
.I "External Data Representation" |
(XDR) before |
sending them over the wire. |
The process of converting from a particular machine representation |
to XDR format is called |
.I serializing , |
and the reverse process is called |
.I deserializing . |
The type field parameters of |
.I callrpc() |
and |
.I registerrpc() |
can be a built-in procedure like |
.I xdr_u_long() |
in the previous example, or a user supplied one. |
XDR has these built-in type routines: |
.IX RPC "built-in routines" |
.DS |
.ft CW |
xdr_int() xdr_u_int() xdr_enum() |
xdr_long() xdr_u_long() xdr_bool() |
xdr_short() xdr_u_short() xdr_wrapstring() |
xdr_char() xdr_u_char() |
.DE |
Note that the routine |
.I xdr_string() |
exists, but cannot be used with |
.I callrpc() |
and |
.I registerrpc (), |
which only pass two parameters to their XDR routines. |
.I xdr_wrapstring() |
has only two parameters, and is thus OK. It calls |
.I xdr_string (). |
.LP |
As an example of a user-defined type routine, |
if you wanted to send the structure |
.DS |
.ft CW |
struct simple { |
int a; |
short b; |
} simple; |
.DE |
then you would call |
.I callrpc() |
as |
.DS |
.ft CW |
callrpc(hostname, PROGNUM, VERSNUM, PROCNUM, |
xdr_simple, &simple ...); |
.DE |
where |
.I xdr_simple() |
is written as: |
.ie t .DS |
.el .DS L |
.ft CW |
#include <rpc/rpc.h> |
|
xdr_simple(xdrsp, simplep) |
XDR *xdrsp; |
struct simple *simplep; |
{ |
if (!xdr_int(xdrsp, &simplep->a)) |
return (0); |
if (!xdr_short(xdrsp, &simplep->b)) |
return (0); |
return (1); |
} |
.DE |
.LP |
An XDR routine returns nonzero (true in the sense of C) if it |
completes successfully, and zero otherwise. |
A complete description of XDR is in the |
.I "XDR Protocol Specification" |
section of this manual, only few implementation examples are |
given here. |
.LP |
In addition to the built-in primitives, |
there are also the prefabricated building blocks: |
.DS |
.ft CW |
xdr_array() xdr_bytes() xdr_reference() |
xdr_vector() xdr_union() xdr_pointer() |
xdr_string() xdr_opaque() |
.DE |
To send a variable array of integers, |
you might package them up as a structure like this |
.DS |
.ft CW |
struct varintarr { |
int *data; |
int arrlnth; |
} arr; |
.DE |
and make an RPC call such as |
.DS |
.ft CW |
callrpc(hostname, PROGNUM, VERSNUM, PROCNUM, |
xdr_varintarr, &arr...); |
.DE |
with |
.I xdr_varintarr() |
defined as: |
.ie t .DS |
.el .DS L |
.ft CW |
xdr_varintarr(xdrsp, arrp) |
XDR *xdrsp; |
struct varintarr *arrp; |
{ |
return (xdr_array(xdrsp, &arrp->data, &arrp->arrlnth, |
MAXLEN, sizeof(int), xdr_int)); |
} |
.DE |
This routine takes as parameters the XDR handle, |
a pointer to the array, a pointer to the size of the array, |
the maximum allowable array size, |
the size of each array element, |
and an XDR routine for handling each array element. |
.KS |
.LP |
If the size of the array is known in advance, one can use |
.I xdr_vector (), |
which serializes fixed-length arrays. |
.ie t .DS |
.el .DS L |
.ft CW |
int intarr[SIZE]; |
|
xdr_intarr(xdrsp, intarr) |
XDR *xdrsp; |
int intarr[]; |
{ |
int i; |
|
return (xdr_vector(xdrsp, intarr, SIZE, sizeof(int), |
xdr_int)); |
} |
.DE |
.KE |
.LP |
XDR always converts quantities to 4-byte multiples when serializing. |
Thus, if either of the examples above involved characters |
instead of integers, each character would occupy 32 bits. |
That is the reason for the XDR routine |
.I xdr_bytes() |
which is like |
.I xdr_array() |
except that it packs characters; |
.I xdr_bytes() |
has four parameters, similar to the first four parameters of |
.I xdr_array (). |
For null-terminated strings, there is also the |
.I xdr_string() |
routine, which is the same as |
.I xdr_bytes() |
without the length parameter. |
On serializing it gets the string length from |
.I strlen (), |
and on deserializing it creates a null-terminated string. |
.LP |
Here is a final example that calls the previously written |
.I xdr_simple() |
as well as the built-in functions |
.I xdr_string() |
and |
.I xdr_reference (), |
which chases pointers: |
.ie t .DS |
.el .DS L |
.ft CW |
struct finalexample { |
char *string; |
struct simple *simplep; |
} finalexample; |
|
xdr_finalexample(xdrsp, finalp) |
XDR *xdrsp; |
struct finalexample *finalp; |
{ |
|
if (!xdr_string(xdrsp, &finalp->string, MAXSTRLEN)) |
return (0); |
if (!xdr_reference(xdrsp, &finalp->simplep, |
sizeof(struct simple), xdr_simple); |
return (0); |
return (1); |
} |
.DE |
Note that we could as easily call |
.I xdr_simple() |
here instead of |
.I xdr_reference (). |
.NH 1 |
\&Lowest Layer of RPC |
.IX "lowest layer of RPC" |
.IX "RPC" "lowest layer" |
.LP |
In the examples given so far, |
RPC takes care of many details automatically for you. |
In this section, we'll show you how you can change the defaults |
by using lower layers of the RPC library. |
It is assumed that you are familiar with sockets |
and the system calls for dealing with them. |
.LP |
There are several occasions when you may need to use lower layers of |
RPC. First, you may need to use TCP, since the higher layer uses UDP, |
which restricts RPC calls to 8K bytes of data. Using TCP permits calls |
to send long streams of data. |
For an example, see the |
.I TCP |
section below. Second, you may want to allocate and free memory |
while serializing or deserializing with XDR routines. |
There is no call at the higher level to let |
you free memory explicitly. |
For more explanation, see the |
.I "Memory Allocation with XDR" |
section below. |
Third, you may need to perform authentication |
on either the client or server side, by supplying |
credentials or verifying them. |
See the explanation in the |
.I Authentication |
section below. |
.NH 2 |
\&More on the Server Side |
.IX RPC "server side" |
.LP |
The server for the |
.I nusers() |
program shown below does the same thing as the one using |
.I registerrpc() |
above, but is written using a lower layer of the RPC package: |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <utmp.h> |
#include <rpcsvc/rusers.h> |
|
main() |
{ |
SVCXPRT *transp; |
int nuser(); |
|
transp = svcudp_create(RPC_ANYSOCK); |
if (transp == NULL){ |
fprintf(stderr, "can't create an RPC server\en"); |
exit(1); |
} |
pmap_unset(RUSERSPROG, RUSERSVERS); |
if (!svc_register(transp, RUSERSPROG, RUSERSVERS, |
nuser, IPPROTO_UDP)) { |
fprintf(stderr, "can't register RUSER service\en"); |
exit(1); |
} |
svc_run(); /* \fINever returns\fP */ |
fprintf(stderr, "should never reach this point\en"); |
} |
|
nuser(rqstp, transp) |
struct svc_req *rqstp; |
SVCXPRT *transp; |
{ |
unsigned long nusers; |
|
switch (rqstp->rq_proc) { |
case NULLPROC: |
if (!svc_sendreply(transp, xdr_void, 0)) |
fprintf(stderr, "can't reply to RPC call\en"); |
return; |
case RUSERSPROC_NUM: |
.ft I |
/* |
* Code here to compute the number of users |
* and assign it to the variable \fInusers\fP |
*/ |
.ft CW |
if (!svc_sendreply(transp, xdr_u_long, &nusers)) |
fprintf(stderr, "can't reply to RPC call\en"); |
return; |
default: |
svcerr_noproc(transp); |
return; |
} |
} |
.DE |
.LP |
First, the server gets a transport handle, which is used |
for receiving and replying to RPC messages. |
.I registerrpc() |
uses |
.I svcudp_create() |
to get a UDP handle. |
If you require a more reliable protocol, call |
.I svctcp_create() |
instead. |
If the argument to |
.I svcudp_create() |
is |
.I RPC_ANYSOCK |
the RPC library creates a socket |
on which to receive and reply to RPC calls. Otherwise, |
.I svcudp_create() |
expects its argument to be a valid socket number. |
If you specify your own socket, it can be bound or unbound. |
If it is bound to a port by the user, the port numbers of |
.I svcudp_create() |
and |
.I clnttcp_create() |
(the low-level client routine) must match. |
.LP |
If the user specifies the |
.I RPC_ANYSOCK |
argument, the RPC library routines will open sockets. |
Otherwise they will expect the user to do so. The routines |
.I svcudp_create() |
and |
.I clntudp_create() |
will cause the RPC library routines to |
.I bind() |
their socket if it is not bound already. |
.LP |
A service may choose to register its port number with the |
local portmapper service. This is done is done by specifying |
a non-zero protocol number in |
.I svc_register (). |
Incidently, a client can discover the server's port number by |
consulting the portmapper on their server's machine. This can |
be done automatically by specifying a zero port number in |
.I clntudp_create() |
or |
.I clnttcp_create (). |
.LP |
After creating an |
.I SVCXPRT , |
the next step is to call |
.I pmap_unset() |
so that if the |
.I nusers() |
server crashed earlier, |
any previous trace of it is erased before restarting. |
More precisely, |
.I pmap_unset() |
erases the entry for |
.I RUSERSPROG |
from the port mapper's tables. |
.LP |
Finally, we associate the program number for |
.I nusers() |
with the procedure |
.I nuser (). |
The final argument to |
.I svc_register() |
is normally the protocol being used, |
which, in this case, is |
.I IPPROTO_UDP |
Notice that unlike |
.I registerrpc (), |
there are no XDR routines involved |
in the registration process. |
Also, registration is done on the program, |
rather than procedure, level. |
.LP |
The user routine |
.I nuser() |
must call and dispatch the appropriate XDR routines |
based on the procedure number. |
Note that |
two things are handled by |
.I nuser() |
that |
.I registerrpc() |
handles automatically. |
The first is that procedure |
.I NULLPROC |
(currently zero) returns with no results. |
This can be used as a simple test |
for detecting if a remote program is running. |
Second, there is a check for invalid procedure numbers. |
If one is detected, |
.I svcerr_noproc() |
is called to handle the error. |
.KS |
.LP |
The user service routine serializes the results and returns |
them to the RPC caller via |
.I svc_sendreply() |
Its first parameter is the |
.I SVCXPRT |
handle, the second is the XDR routine, |
and the third is a pointer to the data to be returned. |
Not illustrated above is how a server |
handles an RPC program that receives data. |
As an example, we can add a procedure |
.I RUSERSPROC_BOOL |
which has an argument |
.I nusers (), |
and returns |
.I TRUE |
or |
.I FALSE |
depending on whether there are nusers logged on. |
It would look like this: |
.ie t .DS |
.el .DS L |
.ft CW |
case RUSERSPROC_BOOL: { |
int bool; |
unsigned nuserquery; |
|
if (!svc_getargs(transp, xdr_u_int, &nuserquery) { |
svcerr_decode(transp); |
return; |
} |
.ft I |
/* |
* Code to set \fInusers\fP = number of users |
*/ |
.ft CW |
if (nuserquery == nusers) |
bool = TRUE; |
else |
bool = FALSE; |
if (!svc_sendreply(transp, xdr_bool, &bool)) { |
fprintf(stderr, "can't reply to RPC call\en"); |
return (1); |
} |
return; |
} |
.DE |
.KE |
.LP |
The relevant routine is |
.I svc_getargs() |
which takes an |
.I SVCXPRT |
handle, the XDR routine, |
and a pointer to where the input is to be placed as arguments. |
.NH 2 |
\&Memory Allocation with XDR |
.IX "memory allocation with XDR" |
.IX XDR "memory allocation" |
.LP |
XDR routines not only do input and output, |
they also do memory allocation. |
This is why the second parameter of |
.I xdr_array() |
is a pointer to an array, rather than the array itself. |
If it is |
.I NULL , |
then |
.I xdr_array() |
allocates space for the array and returns a pointer to it, |
putting the size of the array in the third argument. |
As an example, consider the following XDR routine |
.I xdr_chararr1() |
which deals with a fixed array of bytes with length |
.I SIZE . |
.ie t .DS |
.el .DS L |
.ft CW |
xdr_chararr1(xdrsp, chararr) |
XDR *xdrsp; |
char chararr[]; |
{ |
char *p; |
int len; |
|
p = chararr; |
len = SIZE; |
return (xdr_bytes(xdrsp, &p, &len, SIZE)); |
} |
.DE |
If space has already been allocated in |
.I chararr , |
it can be called from a server like this: |
.ie t .DS |
.el .DS L |
.ft CW |
char chararr[SIZE]; |
|
svc_getargs(transp, xdr_chararr1, chararr); |
.DE |
If you want XDR to do the allocation, |
you would have to rewrite this routine in the following way: |
.ie t .DS |
.el .DS L |
.ft CW |
xdr_chararr2(xdrsp, chararrp) |
XDR *xdrsp; |
char **chararrp; |
{ |
int len; |
|
len = SIZE; |
return (xdr_bytes(xdrsp, charrarrp, &len, SIZE)); |
} |
.DE |
Then the RPC call might look like this: |
.ie t .DS |
.el .DS L |
.ft CW |
char *arrptr; |
|
arrptr = NULL; |
svc_getargs(transp, xdr_chararr2, &arrptr); |
.ft I |
/* |
* Use the result here |
*/ |
.ft CW |
svc_freeargs(transp, xdr_chararr2, &arrptr); |
.DE |
Note that, after being used, the character array can be freed with |
.I svc_freeargs() |
.I svc_freeargs() |
will not attempt to free any memory if the variable indicating it |
is NULL. For example, in the the routine |
.I xdr_finalexample (), |
given earlier, if |
.I finalp->string |
was NULL, then it would not be freed. The same is true for |
.I finalp->simplep . |
.LP |
To summarize, each XDR routine is responsible |
for serializing, deserializing, and freeing memory. |
When an XDR routine is called from |
.I callrpc() |
the serializing part is used. |
When called from |
.I svc_getargs() |
the deserializer is used. |
And when called from |
.I svc_freeargs() |
the memory deallocator is used. When building simple examples like those |
in this section, a user doesn't have to worry |
about the three modes. |
See the |
.I "External Data Representation: Sun Technical Notes" |
for examples of more sophisticated XDR routines that determine |
which of the three modes they are in and adjust their behavior accordingly. |
.KS |
.NH 2 |
\&The Calling Side |
.IX RPC "calling side" |
.LP |
When you use |
.I callrpc() |
you have no control over the RPC delivery |
mechanism or the socket used to transport the data. |
To illustrate the layer of RPC that lets you adjust these |
parameters, consider the following code to call the |
.I nusers |
service: |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <utmp.h> |
#include <rpcsvc/rusers.h> |
#include <sys/socket.h> |
#include <sys/time.h> |
#include <netdb.h> |
|
main(argc, argv) |
int argc; |
char **argv; |
{ |
struct hostent *hp; |
struct timeval pertry_timeout, total_timeout; |
struct sockaddr_in server_addr; |
int sock = RPC_ANYSOCK; |
register CLIENT *client; |
enum clnt_stat clnt_stat; |
unsigned long nusers; |
|
if (argc != 2) { |
fprintf(stderr, "usage: nusers hostname\en"); |
exit(-1); |
} |
if ((hp = gethostbyname(argv[1])) == NULL) { |
fprintf(stderr, "can't get addr for %s\en",argv[1]); |
exit(-1); |
} |
pertry_timeout.tv_sec = 3; |
pertry_timeout.tv_usec = 0; |
bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, |
hp->h_length); |
server_addr.sin_family = AF_INET; |
server_addr.sin_port = 0; |
if ((client = clntudp_create(&server_addr, RUSERSPROG, |
RUSERSVERS, pertry_timeout, &sock)) == NULL) { |
clnt_pcreateerror("clntudp_create"); |
exit(-1); |
} |
total_timeout.tv_sec = 20; |
total_timeout.tv_usec = 0; |
clnt_stat = clnt_call(client, RUSERSPROC_NUM, xdr_void, |
0, xdr_u_long, &nusers, total_timeout); |
if (clnt_stat != RPC_SUCCESS) { |
clnt_perror(client, "rpc"); |
exit(-1); |
} |
clnt_destroy(client); |
close(sock); |
exit(0); |
} |
.vs |
.DE |
.KE |
The low-level version of |
.I callrpc() |
is |
.I clnt_call() |
which takes a |
.I CLIENT |
pointer rather than a host name. The parameters to |
.I clnt_call() |
are a |
.I CLIENT |
pointer, the procedure number, |
the XDR routine for serializing the argument, |
a pointer to the argument, |
the XDR routine for deserializing the return value, |
a pointer to where the return value will be placed, |
and the time in seconds to wait for a reply. |
.LP |
The |
.I CLIENT |
pointer is encoded with the transport mechanism. |
.I callrpc() |
uses UDP, thus it calls |
.I clntudp_create() |
to get a |
.I CLIENT |
pointer. To get TCP (Transmission Control Protocol), you would use |
.I clnttcp_create() . |
.LP |
The parameters to |
.I clntudp_create() |
are the server address, the program number, the version number, |
a timeout value (between tries), and a pointer to a socket. |
The final argument to |
.I clnt_call() |
is the total time to wait for a response. |
Thus, the number of tries is the |
.I clnt_call() |
timeout divided by the |
.I clntudp_create() |
timeout. |
.LP |
Note that the |
.I clnt_destroy() |
call |
always deallocates the space associated with the |
.I CLIENT |
handle. It closes the socket associated with the |
.I CLIENT |
handle, however, only if the RPC library opened it. It the |
socket was opened by the user, it stays open. This makes it |
possible, in cases where there are multiple client handles |
using the same socket, to destroy one handle without closing |
the socket that other handles are using. |
.LP |
To make a stream connection, the call to |
.I clntudp_create() |
is replaced with a call to |
.I clnttcp_create() . |
.DS |
.ft CW |
clnttcp_create(&server_addr, prognum, versnum, &sock, |
inputsize, outputsize); |
.DE |
There is no timeout argument; instead, the receive and send buffer |
sizes must be specified. When the |
.I clnttcp_create() |
call is made, a TCP connection is established. |
All RPC calls using that |
.I CLIENT |
handle would use this connection. |
The server side of an RPC call using TCP has |
.I svcudp_create() |
replaced by |
.I svctcp_create() . |
.DS |
.ft CW |
transp = svctcp_create(RPC_ANYSOCK, 0, 0); |
.DE |
The last two arguments to |
.I svctcp_create() |
are send and receive sizes respectively. If `0' is specified for |
either of these, the system chooses a reasonable default. |
.KS |
.NH 1 |
\&Other RPC Features |
.IX "RPC" "miscellaneous features" |
.IX "miscellaneous RPC features" |
.LP |
This section discusses some other aspects of RPC |
that are occasionally useful. |
.NH 2 |
\&Select on the Server Side |
.IX RPC select() RPC \fIselect()\fP |
.IX select() "" \fIselect()\fP "on the server side" |
.LP |
Suppose a process is processing RPC requests |
while performing some other activity. |
If the other activity involves periodically updating a data structure, |
the process can set an alarm signal before calling |
.I svc_run() |
But if the other activity |
involves waiting on a a file descriptor, the |
.I svc_run() |
call won't work. |
The code for |
.I svc_run() |
is as follows: |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
void |
svc_run() |
{ |
fd_set readfds; |
int dtbsz = getdtablesize(); |
|
for (;;) { |
readfds = svc_fds; |
switch (select(dtbsz, &readfds, NULL,NULL,NULL)) { |
|
case -1: |
if (errno == EINTR) |
continue; |
perror("select"); |
return; |
case 0: |
break; |
default: |
svc_getreqset(&readfds); |
} |
} |
} |
.vs |
.DE |
.KE |
.LP |
You can bypass |
.I svc_run() |
and call |
.I svc_getreqset() |
yourself. |
All you need to know are the file descriptors |
of the socket(s) associated with the programs you are waiting on. |
Thus you can have your own |
.I select() |
.IX select() "" \fIselect()\fP |
that waits on both the RPC socket, |
and your own descriptors. Note that |
.I svc_fds() |
is a bit mask of all the file descriptors that RPC is using for |
services. It can change everytime that |
.I any |
RPC library routine is called, because descriptors are constantly |
being opened and closed, for example for TCP connections. |
.NH 2 |
\&Broadcast RPC |
.IX "broadcast RPC" |
.IX RPC "broadcast" |
.LP |
The |
.I portmapper |
is a daemon that converts RPC program numbers |
into DARPA protocol port numbers; see the |
.I portmap |
man page. You can't do broadcast RPC without the portmapper. |
Here are the main differences between |
broadcast RPC and normal RPC calls: |
.IP 1. |
Normal RPC expects one answer, whereas |
broadcast RPC expects many answers |
(one or more answer from each responding machine). |
.IP 2. |
Broadcast RPC can only be supported by packet-oriented (connectionless) |
transport protocols like UPD/IP. |
.IP 3. |
The implementation of broadcast RPC |
treats all unsuccessful responses as garbage by filtering them out. |
Thus, if there is a version mismatch between the |
broadcaster and a remote service, |
the user of broadcast RPC never knows. |
.IP 4. |
All broadcast messages are sent to the portmap port. |
Thus, only services that register themselves with their portmapper |
are accessible via the broadcast RPC mechanism. |
.IP 5. |
Broadcast requests are limited in size to the MTU (Maximum Transfer |
Unit) of the local network. For Ethernet, the MTU is 1500 bytes. |
.KS |
.NH 3 |
\&Broadcast RPC Synopsis |
.IX "broadcast RPC" synopsis |
.IX "RPC" "broadcast synopsis" |
.ie t .DS |
.el .DS L |
.ft CW |
#include <rpc/pmap_clnt.h> |
. . . |
enum clnt_stat clnt_stat; |
. . . |
clnt_stat = clnt_broadcast(prognum, versnum, procnum, |
inproc, in, outproc, out, eachresult) |
u_long prognum; /* \fIprogram number\fP */ |
u_long versnum; /* \fIversion number\fP */ |
u_long procnum; /* \fIprocedure number\fP */ |
xdrproc_t inproc; /* \fIxdr routine for args\fP */ |
caddr_t in; /* \fIpointer to args\fP */ |
xdrproc_t outproc; /* \fIxdr routine for results\fP */ |
caddr_t out; /* \fIpointer to results\fP */ |
bool_t (*eachresult)();/* \fIcall with each result gotten\fP */ |
.DE |
.KE |
The procedure |
.I eachresult() |
is called each time a valid result is obtained. |
It returns a boolean that indicates |
whether or not the user wants more responses. |
.ie t .DS |
.el .DS L |
.ft CW |
bool_t done; |
. . . |
done = eachresult(resultsp, raddr) |
caddr_t resultsp; |
struct sockaddr_in *raddr; /* \fIAddr of responding machine\fP */ |
.DE |
If |
.I done |
is |
.I TRUE , |
then broadcasting stops and |
.I clnt_broadcast() |
returns successfully. |
Otherwise, the routine waits for another response. |
The request is rebroadcast |
after a few seconds of waiting. |
If no responses come back, |
the routine returns with |
.I RPC_TIMEDOUT . |
.NH 2 |
\&Batching |
.IX "batching" |
.IX RPC "batching" |
.LP |
The RPC architecture is designed so that clients send a call message, |
and wait for servers to reply that the call succeeded. |
This implies that clients do not compute |
while servers are processing a call. |
This is inefficient if the client does not want or need |
an acknowledgement for every message sent. |
It is possible for clients to continue computing |
while waiting for a response, |
using RPC batch facilities. |
.LP |
RPC messages can be placed in a \*Qpipeline\*U of calls |
to a desired server; this is called batching. |
Batching assumes that: |
1) each RPC call in the pipeline requires no response from the server, |
and the server does not send a response message; and |
2) the pipeline of calls is transported on a reliable |
byte stream transport such as TCP/IP. |
Since the server does not respond to every call, |
the client can generate new calls in parallel |
with the server executing previous calls. |
Furthermore, the TCP/IP implementation can buffer up |
many call messages, and send them to the server in one |
.I write() |
system call. This overlapped execution |
greatly decreases the interprocess communication overhead of |
the client and server processes, |
and the total elapsed time of a series of calls. |
.LP |
Since the batched calls are buffered, |
the client should eventually do a nonbatched call |
in order to flush the pipeline. |
.LP |
A contrived example of batching follows. |
Assume a string rendering service (like a window system) |
has two similar calls: one renders a string and returns void results, |
while the other renders a string and remains silent. |
The service (using the TCP/IP transport) may look like: |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <suntool/windows.h> |
|
void windowdispatch(); |
|
main() |
{ |
SVCXPRT *transp; |
|
transp = svctcp_create(RPC_ANYSOCK, 0, 0); |
if (transp == NULL){ |
fprintf(stderr, "can't create an RPC server\en"); |
exit(1); |
} |
pmap_unset(WINDOWPROG, WINDOWVERS); |
if (!svc_register(transp, WINDOWPROG, WINDOWVERS, |
windowdispatch, IPPROTO_TCP)) { |
fprintf(stderr, "can't register WINDOW service\en"); |
exit(1); |
} |
svc_run(); /* \fINever returns\fP */ |
fprintf(stderr, "should never reach this point\en"); |
} |
|
void |
windowdispatch(rqstp, transp) |
struct svc_req *rqstp; |
SVCXPRT *transp; |
{ |
char *s = NULL; |
|
switch (rqstp->rq_proc) { |
case NULLPROC: |
if (!svc_sendreply(transp, xdr_void, 0)) |
fprintf(stderr, "can't reply to RPC call\en"); |
return; |
case RENDERSTRING: |
if (!svc_getargs(transp, xdr_wrapstring, &s)) { |
fprintf(stderr, "can't decode arguments\en"); |
.ft I |
/* |
* Tell caller he screwed up |
*/ |
.ft CW |
svcerr_decode(transp); |
break; |
} |
.ft I |
/* |
* Code here to render the string \fIs\fP |
*/ |
.ft CW |
if (!svc_sendreply(transp, xdr_void, NULL)) |
fprintf(stderr, "can't reply to RPC call\en"); |
break; |
case RENDERSTRING_BATCHED: |
if (!svc_getargs(transp, xdr_wrapstring, &s)) { |
fprintf(stderr, "can't decode arguments\en"); |
.ft I |
/* |
* We are silent in the face of protocol errors |
*/ |
.ft CW |
break; |
} |
.ft I |
/* |
* Code here to render string s, but send no reply! |
*/ |
.ft CW |
break; |
default: |
svcerr_noproc(transp); |
return; |
} |
.ft I |
/* |
* Now free string allocated while decoding arguments |
*/ |
.ft CW |
svc_freeargs(transp, xdr_wrapstring, &s); |
} |
.DE |
Of course the service could have one procedure |
that takes the string and a boolean |
to indicate whether or not the procedure should respond. |
.LP |
In order for a client to take advantage of batching, |
the client must perform RPC calls on a TCP-based transport |
and the actual calls must have the following attributes: |
1) the result's XDR routine must be zero |
.I NULL ), |
and 2) the RPC call's timeout must be zero. |
.KS |
.LP |
Here is an example of a client that uses batching to render a |
bunch of strings; the batching is flushed when the client gets |
a null string (EOF): |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <sys/socket.h> |
#include <sys/time.h> |
#include <netdb.h> |
#include <suntool/windows.h> |
|
main(argc, argv) |
int argc; |
char **argv; |
{ |
struct hostent *hp; |
struct timeval pertry_timeout, total_timeout; |
struct sockaddr_in server_addr; |
int sock = RPC_ANYSOCK; |
register CLIENT *client; |
enum clnt_stat clnt_stat; |
char buf[1000], *s = buf; |
|
if ((client = clnttcp_create(&server_addr, |
WINDOWPROG, WINDOWVERS, &sock, 0, 0)) == NULL) { |
perror("clnttcp_create"); |
exit(-1); |
} |
total_timeout.tv_sec = 0; |
total_timeout.tv_usec = 0; |
while (scanf("%s", s) != EOF) { |
clnt_stat = clnt_call(client, RENDERSTRING_BATCHED, |
xdr_wrapstring, &s, NULL, NULL, total_timeout); |
if (clnt_stat != RPC_SUCCESS) { |
clnt_perror(client, "batched rpc"); |
exit(-1); |
} |
} |
|
/* \fINow flush the pipeline\fP */ |
|
total_timeout.tv_sec = 20; |
clnt_stat = clnt_call(client, NULLPROC, xdr_void, NULL, |
xdr_void, NULL, total_timeout); |
if (clnt_stat != RPC_SUCCESS) { |
clnt_perror(client, "rpc"); |
exit(-1); |
} |
clnt_destroy(client); |
exit(0); |
} |
.vs |
.DE |
.KE |
Since the server sends no message, |
the clients cannot be notified of any of the failures that may occur. |
Therefore, clients are on their own when it comes to handling errors. |
.LP |
The above example was completed to render |
all of the (2000) lines in the file |
.I /etc/termcap . |
The rendering service did nothing but throw the lines away. |
The example was run in the following four configurations: |
1) machine to itself, regular RPC; |
2) machine to itself, batched RPC; |
3) machine to another, regular RPC; and |
4) machine to another, batched RPC. |
The results are as follows: |
1) 50 seconds; |
2) 16 seconds; |
3) 52 seconds; |
4) 10 seconds. |
Running |
.I fscanf() |
on |
.I /etc/termcap |
only requires six seconds. |
These timings show the advantage of protocols |
that allow for overlapped execution, |
though these protocols are often hard to design. |
.NH 2 |
\&Authentication |
.IX "authentication" |
.IX "RPC" "authentication" |
.LP |
In the examples presented so far, |
the caller never identified itself to the server, |
and the server never required an ID from the caller. |
Clearly, some network services, such as a network filesystem, |
require stronger security than what has been presented so far. |
.LP |
In reality, every RPC call is authenticated by |
the RPC package on the server, and similarly, |
the RPC client package generates and sends authentication parameters. |
Just as different transports (TCP/IP or UDP/IP) |
can be used when creating RPC clients and servers, |
different forms of authentication can be associated with RPC clients; |
the default authentication type used as a default is type |
.I none . |
.LP |
The authentication subsystem of the RPC package is open ended. |
That is, numerous types of authentication are easy to support. |
.NH 3 |
\&UNIX Authentication |
.IX "UNIX Authentication" |
.IP "\fIThe Client Side\fP" |
.LP |
When a caller creates a new RPC client handle as in: |
.DS |
.ft CW |
clnt = clntudp_create(address, prognum, versnum, |
wait, sockp) |
.DE |
the appropriate transport instance defaults |
the associate authentication handle to be |
.DS |
.ft CW |
clnt->cl_auth = authnone_create(); |
.DE |
The RPC client can choose to use |
.I UNIX |
style authentication by setting |
.I clnt\->cl_auth |
after creating the RPC client handle: |
.DS |
.ft CW |
clnt->cl_auth = authunix_create_default(); |
.DE |
This causes each RPC call associated with |
.I clnt |
to carry with it the following authentication credentials structure: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* UNIX style credentials. |
*/ |
.ft CW |
struct authunix_parms { |
u_long aup_time; /* \fIcredentials creation time\fP */ |
char *aup_machname; /* \fIhost name where client is\fP */ |
int aup_uid; /* \fIclient's UNIX effective uid\fP */ |
int aup_gid; /* \fIclient's current group id\fP */ |
u_int aup_len; /* \fIelement length of aup_gids\fP */ |
int *aup_gids; /* \fIarray of groups user is in\fP */ |
}; |
.DE |
These fields are set by |
.I authunix_create_default() |
by invoking the appropriate system calls. |
Since the RPC user created this new style of authentication, |
the user is responsible for destroying it with: |
.DS |
.ft CW |
auth_destroy(clnt->cl_auth); |
.DE |
This should be done in all cases, to conserve memory. |
.sp |
.IP "\fIThe Server Side\fP" |
.LP |
Service implementors have a harder time dealing with authentication issues |
since the RPC package passes the service dispatch routine a request |
that has an arbitrary authentication style associated with it. |
Consider the fields of a request handle passed to a service dispatch routine: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* An RPC Service request |
*/ |
.ft CW |
struct svc_req { |
u_long rq_prog; /* \fIservice program number\fP */ |
u_long rq_vers; /* \fIservice protocol vers num\fP */ |
u_long rq_proc; /* \fIdesired procedure number\fP */ |
struct opaque_auth rq_cred; /* \fIraw credentials from wire\fP */ |
caddr_t rq_clntcred; /* \fIcredentials (read only)\fP */ |
}; |
.DE |
The |
.I rq_cred |
is mostly opaque, except for one field of interest: |
the style or flavor of authentication credentials: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* Authentication info. Mostly opaque to the programmer. |
*/ |
.ft CW |
struct opaque_auth { |
enum_t oa_flavor; /* \fIstyle of credentials\fP */ |
caddr_t oa_base; /* \fIaddress of more auth stuff\fP */ |
u_int oa_length; /* \fInot to exceed \fIMAX_AUTH_BYTES */ |
}; |
.DE |
.IX RPC guarantees |
The RPC package guarantees the following |
to the service dispatch routine: |
.IP 1. |
That the request's |
.I rq_cred |
is well formed. Thus the service implementor may inspect the request's |
.I rq_cred.oa_flavor |
to determine which style of authentication the caller used. |
The service implementor may also wish to inspect the other fields of |
.I rq_cred |
if the style is not one of the styles supported by the RPC package. |
.IP 2. |
That the request's |
.I rq_clntcred |
field is either |
.I NULL |
or points to a well formed structure |
that corresponds to a supported style of authentication credentials. |
Remember that only |
.I unix |
style is currently supported, so (currently) |
.I rq_clntcred |
could be cast to a pointer to an |
.I authunix_parms |
structure. If |
.I rq_clntcred |
is |
.I NULL , |
the service implementor may wish to inspect the other (opaque) fields of |
.I rq_cred |
in case the service knows about a new type of authentication |
that the RPC package does not know about. |
.LP |
Our remote users service example can be extended so that |
it computes results for all users except UID 16: |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
nuser(rqstp, transp) |
struct svc_req *rqstp; |
SVCXPRT *transp; |
{ |
struct authunix_parms *unix_cred; |
int uid; |
unsigned long nusers; |
|
.ft I |
/* |
* we don't care about authentication for null proc |
*/ |
.ft CW |
if (rqstp->rq_proc == NULLPROC) { |
if (!svc_sendreply(transp, xdr_void, 0)) { |
fprintf(stderr, "can't reply to RPC call\en"); |
return (1); |
} |
return; |
} |
.ft I |
/* |
* now get the uid |
*/ |
.ft CW |
switch (rqstp->rq_cred.oa_flavor) { |
case AUTH_UNIX: |
unix_cred = |
(struct authunix_parms *)rqstp->rq_clntcred; |
uid = unix_cred->aup_uid; |
break; |
case AUTH_NULL: |
default: |
svcerr_weakauth(transp); |
return; |
} |
switch (rqstp->rq_proc) { |
case RUSERSPROC_NUM: |
.ft I |
/* |
* make sure caller is allowed to call this proc |
*/ |
.ft CW |
if (uid == 16) { |
svcerr_systemerr(transp); |
return; |
} |
.ft I |
/* |
* Code here to compute the number of users |
* and assign it to the variable \fInusers\fP |
*/ |
.ft CW |
if (!svc_sendreply(transp, xdr_u_long, &nusers)) { |
fprintf(stderr, "can't reply to RPC call\en"); |
return (1); |
} |
return; |
default: |
svcerr_noproc(transp); |
return; |
} |
} |
.vs |
.DE |
A few things should be noted here. |
First, it is customary not to check |
the authentication parameters associated with the |
.I NULLPROC |
(procedure number zero). |
Second, if the authentication parameter's type is not suitable |
for your service, you should call |
.I svcerr_weakauth() . |
And finally, the service protocol itself should return status |
for access denied; in the case of our example, the protocol |
does not have such a status, so we call the service primitive |
.I svcerr_systemerr() |
instead. |
.LP |
The last point underscores the relation between |
the RPC authentication package and the services; |
RPC deals only with |
.I authentication |
and not with individual services' |
.I "access control" . |
The services themselves must implement their own access control policies |
and reflect these policies as return statuses in their protocols. |
.NH 2 |
\&DES Authentication |
.IX RPC DES |
.IX RPC authentication |
.LP |
UNIX authentication is quite easy to defeat. Instead of using |
.I authunix_create_default (), |
one can call |
.I authunix_create() |
and then modify the RPC authentication handle it returns by filling in |
whatever user ID and hostname they wish the server to think they have. |
DES authentication is thus recommended for people who want more security |
than UNIX authentication offers. |
.LP |
The details of the DES authentication protocol are complicated and |
are not explained here. |
See |
.I "Remote Procedure Calls: Protocol Specification" |
for the details. |
.LP |
In order for DES authentication to work, the |
.I keyserv(8c) |
daemon must be running on both the server and client machines. The |
users on these machines need public keys assigned by the network |
administrator in the |
.I publickey(5) |
database. And, they need to have decrypted their secret keys |
using their login password. This automatically happens when one |
logs in using |
.I login(1) , |
or can be done manually using |
.I keylogin(1) . |
The |
.I "Network Services" |
chapter |
./" XXX |
explains more how to setup secure networking. |
.sp |
.IP "\fIClient Side\fP" |
.LP |
If a client wishes to use DES authentication, it must set its |
authentication handle appropriately. Here is an example: |
.DS |
cl->cl_auth = |
authdes_create(servername, 60, &server_addr, NULL); |
.DE |
The first argument is the network name or \*Qnetname\*U of the owner of |
the server process. Typically, server processes are root processes |
and their netname can be derived using the following call: |
.DS |
char servername[MAXNETNAMELEN]; |
|
host2netname(servername, rhostname, NULL); |
.DE |
Here, |
.I rhostname |
is the hostname of the machine the server process is running on. |
.I host2netname() |
fills in |
.I servername |
to contain this root process's netname. If the |
server process was run by a regular user, one could use the call |
.I user2netname() |
instead. Here is an example for a server process with the same user |
ID as the client: |
.DS |
char servername[MAXNETNAMELEN]; |
|
user2netname(servername, getuid(), NULL); |
.DE |
The last argument to both of these calls, |
.I user2netname() |
and |
.I host2netname (), |
is the name of the naming domain where the server is located. The |
.I NULL |
used here means \*Quse the local domain name.\*U |
.LP |
The second argument to |
.I authdes_create() |
is a lifetime for the credential. Here it is set to sixty |
seconds. What that means is that the credential will expire 60 |
seconds from now. If some mischievous user tries to reuse the |
credential, the server RPC subsystem will recognize that it has |
expired and not grant any requests. If the same mischievous user |
tries to reuse the credential within the sixty second lifetime, |
he will still be rejected because the server RPC subsystem |
remembers which credentials it has already seen in the near past, |
and will not grant requests to duplicates. |
.LP |
The third argument to |
.I authdes_create() |
is the address of the host to synchronize with. In order for DES |
authentication to work, the server and client must agree upon the |
time. Here we pass the address of the server itself, so the |
client and server will both be using the same time: the server's |
time. The argument can be |
.I NULL , |
which means \*Qdon't bother synchronizing.\*U You should only do this |
if you are sure the client and server are already synchronized. |
.LP |
The final argument to |
.I authdes_create() |
is the address of a DES encryption key to use for encrypting |
timestamps and data. If this argument is |
.I NULL , |
as it is in this example, a random key will be chosen. The client |
may find out the encryption key being used by consulting the |
.I ah_key |
field of the authentication handle. |
.sp |
.IP "\fIServer Side\fP" |
.LP |
The server side is a lot simpler than the client side. Here is the |
previous example rewritten to use |
.I AUTH_DES |
instead of |
.I AUTH_UNIX : |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
#include <sys/time.h> |
#include <rpc/auth_des.h> |
. . . |
. . . |
nuser(rqstp, transp) |
struct svc_req *rqstp; |
SVCXPRT *transp; |
{ |
struct authdes_cred *des_cred; |
int uid; |
int gid; |
int gidlen; |
int gidlist[10]; |
.ft I |
/* |
* we don't care about authentication for null proc |
*/ |
.ft CW |
|
if (rqstp->rq_proc == NULLPROC) { |
/* \fIsame as before\fP */ |
} |
|
.ft I |
/* |
* now get the uid |
*/ |
.ft CW |
switch (rqstp->rq_cred.oa_flavor) { |
case AUTH_DES: |
des_cred = |
(struct authdes_cred *) rqstp->rq_clntcred; |
if (! netname2user(des_cred->adc_fullname.name, |
&uid, &gid, &gidlen, gidlist)) |
{ |
fprintf(stderr, "unknown user: %s\n", |
des_cred->adc_fullname.name); |
svcerr_systemerr(transp); |
return; |
} |
break; |
case AUTH_NULL: |
default: |
svcerr_weakauth(transp); |
return; |
} |
|
.ft I |
/* |
* The rest is the same as before |
*/ |
.ft CW |
.vs |
.DE |
Note the use of the routine |
.I netname2user (), |
the inverse of |
.I user2netname (): |
it takes a network ID and converts to a unix ID. |
.I netname2user () |
also supplies the group IDs which we don't use in this example, |
but which may be useful to other UNIX programs. |
.NH 2 |
\&Using Inetd |
.IX inetd "" "using \fIinetd\fP" |
.LP |
An RPC server can be started from |
.I inetd |
The only difference from the usual code is that the service |
creation routine should be called in the following form: |
.ie t .DS |
.el .DS L |
.ft CW |
transp = svcudp_create(0); /* \fIFor UDP\fP */ |
transp = svctcp_create(0,0,0); /* \fIFor listener TCP sockets\fP */ |
transp = svcfd_create(0,0,0); /* \fIFor connected TCP sockets\fP */ |
.DE |
since |
.I inet |
passes a socket as file descriptor 0. |
Also, |
.I svc_register() |
should be called as |
.ie t .DS |
.el .DS L |
.ft CW |
svc_register(transp, PROGNUM, VERSNUM, service, 0); |
.DE |
with the final flag as 0, |
since the program would already be registered by |
.I inetd |
Remember that if you want to exit |
from the server process and return control to |
.I inet |
you need to explicitly exit, since |
.I svc_run() |
never returns. |
.LP |
The format of entries in |
.I /etc/inetd.conf |
for RPC services is in one of the following two forms: |
.ie t .DS |
.el .DS L |
.ft CW |
p_name/version dgram rpc/udp wait/nowait user server args |
p_name/version stream rpc/tcp wait/nowait user server args |
.DE |
where |
.I p_name |
is the symbolic name of the program as it appears in |
.I rpc(5) , |
.I server |
is the program implementing the server, |
and |
.I program |
and |
.I version |
are the program and version numbers of the service. |
For more information, see |
.I inetd.conf(5) . |
.LP |
If the same program handles multiple versions, |
then the version number can be a range, |
as in this example: |
.ie t .DS |
.el .DS L |
.ft CW |
rstatd/1-2 dgram rpc/udp wait root /usr/etc/rpc.rstatd |
.DE |
.NH 1 |
\&More Examples |
.sp 1 |
.NH 2 |
\&Versions |
.IX "versions" |
.IX "RPC" "versions" |
.LP |
By convention, the first version number of program |
.I PROG |
is |
.I PROGVERS_ORIG |
and the most recent version is |
.I PROGVERS |
Suppose there is a new version of the |
.I user |
program that returns an |
.I "unsigned short" |
rather than a |
.I long . |
If we name this version |
.I RUSERSVERS_SHORT |
then a server that wants to support both versions |
would do a double register. |
.ie t .DS |
.el .DS L |
.ft CW |
if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, |
nuser, IPPROTO_TCP)) { |
fprintf(stderr, "can't register RUSER service\en"); |
exit(1); |
} |
if (!svc_register(transp, RUSERSPROG, RUSERSVERS_SHORT, |
nuser, IPPROTO_TCP)) { |
fprintf(stderr, "can't register RUSER service\en"); |
exit(1); |
} |
.DE |
Both versions can be handled by the same C procedure: |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
nuser(rqstp, transp) |
struct svc_req *rqstp; |
SVCXPRT *transp; |
{ |
unsigned long nusers; |
unsigned short nusers2; |
|
switch (rqstp->rq_proc) { |
case NULLPROC: |
if (!svc_sendreply(transp, xdr_void, 0)) { |
fprintf(stderr, "can't reply to RPC call\en"); |
return (1); |
} |
return; |
case RUSERSPROC_NUM: |
.ft I |
/* |
* Code here to compute the number of users |
* and assign it to the variable \fInusers\fP |
*/ |
.ft CW |
nusers2 = nusers; |
switch (rqstp->rq_vers) { |
case RUSERSVERS_ORIG: |
if (!svc_sendreply(transp, xdr_u_long, |
&nusers)) { |
fprintf(stderr,"can't reply to RPC call\en"); |
} |
break; |
case RUSERSVERS_SHORT: |
if (!svc_sendreply(transp, xdr_u_short, |
&nusers2)) { |
fprintf(stderr,"can't reply to RPC call\en"); |
} |
break; |
} |
default: |
svcerr_noproc(transp); |
return; |
} |
} |
.vs |
.DE |
.KS |
.NH 2 |
\&TCP |
.IX "TCP" |
.LP |
Here is an example that is essentially |
.I rcp. |
The initiator of the RPC |
.I snd |
call takes its standard input and sends it to the server |
.I rcv |
which prints it on standard output. |
The RPC call uses TCP. |
This also illustrates an XDR procedure that behaves differently |
on serialization than on deserialization. |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* The xdr routine: |
* on decode, read from wire, write onto fp |
* on encode, read from fp, write onto wire |
*/ |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> |
|
xdr_rcp(xdrs, fp) |
XDR *xdrs; |
FILE *fp; |
{ |
unsigned long size; |
char buf[BUFSIZ], *p; |
|
if (xdrs->x_op == XDR_FREE)/* nothing to free */ |
return 1; |
while (1) { |
if (xdrs->x_op == XDR_ENCODE) { |
if ((size = fread(buf, sizeof(char), BUFSIZ, |
fp)) == 0 && ferror(fp)) { |
fprintf(stderr, "can't fread\en"); |
return (1); |
} |
} |
p = buf; |
if (!xdr_bytes(xdrs, &p, &size, BUFSIZ)) |
return 0; |
if (size == 0) |
return 1; |
if (xdrs->x_op == XDR_DECODE) { |
if (fwrite(buf, sizeof(char), size, |
fp) != size) { |
fprintf(stderr, "can't fwrite\en"); |
return (1); |
} |
} |
} |
} |
.vs |
.DE |
.KE |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* The sender routines |
*/ |
.ft CW |
#include <stdio.h> |
#include <netdb.h> |
#include <rpc/rpc.h> |
#include <sys/socket.h> |
#include <sys/time.h> |
|
main(argc, argv) |
int argc; |
char **argv; |
{ |
int xdr_rcp(); |
int err; |
|
if (argc < 2) { |
fprintf(stderr, "usage: %s servername\en", argv[0]); |
exit(-1); |
} |
if ((err = callrpctcp(argv[1], RCPPROG, RCPPROC, |
RCPVERS, xdr_rcp, stdin, xdr_void, 0) != 0)) { |
clnt_perrno(err); |
fprintf(stderr, "can't make RPC call\en"); |
exit(1); |
} |
exit(0); |
} |
|
callrpctcp(host, prognum, procnum, versnum, |
inproc, in, outproc, out) |
char *host, *in, *out; |
xdrproc_t inproc, outproc; |
{ |
struct sockaddr_in server_addr; |
int socket = RPC_ANYSOCK; |
enum clnt_stat clnt_stat; |
struct hostent *hp; |
register CLIENT *client; |
struct timeval total_timeout; |
|
if ((hp = gethostbyname(host)) == NULL) { |
fprintf(stderr, "can't get addr for '%s'\en", host); |
return (-1); |
} |
bcopy(hp->h_addr, (caddr_t)&server_addr.sin_addr, |
hp->h_length); |
server_addr.sin_family = AF_INET; |
server_addr.sin_port = 0; |
if ((client = clnttcp_create(&server_addr, prognum, |
versnum, &socket, BUFSIZ, BUFSIZ)) == NULL) { |
perror("rpctcp_create"); |
return (-1); |
} |
total_timeout.tv_sec = 20; |
total_timeout.tv_usec = 0; |
clnt_stat = clnt_call(client, procnum, |
inproc, in, outproc, out, total_timeout); |
clnt_destroy(client); |
return (int)clnt_stat; |
} |
.vs |
.DE |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* The receiving routines |
*/ |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> |
|
main() |
{ |
register SVCXPRT *transp; |
int rcp_service(), xdr_rcp(); |
|
if ((transp = svctcp_create(RPC_ANYSOCK, |
BUFSIZ, BUFSIZ)) == NULL) { |
fprintf("svctcp_create: error\en"); |
exit(1); |
} |
pmap_unset(RCPPROG, RCPVERS); |
if (!svc_register(transp, |
RCPPROG, RCPVERS, rcp_service, IPPROTO_TCP)) { |
fprintf(stderr, "svc_register: error\en"); |
exit(1); |
} |
svc_run(); /* \fInever returns\fP */ |
fprintf(stderr, "svc_run should never return\en"); |
} |
|
rcp_service(rqstp, transp) |
register struct svc_req *rqstp; |
register SVCXPRT *transp; |
{ |
switch (rqstp->rq_proc) { |
case NULLPROC: |
if (svc_sendreply(transp, xdr_void, 0) == 0) { |
fprintf(stderr, "err: rcp_service"); |
return (1); |
} |
return; |
case RCPPROC_FP: |
if (!svc_getargs(transp, xdr_rcp, stdout)) { |
svcerr_decode(transp); |
return; |
} |
if (!svc_sendreply(transp, xdr_void, 0)) { |
fprintf(stderr, "can't reply\en"); |
return; |
} |
return (0); |
default: |
svcerr_noproc(transp); |
return; |
} |
} |
.vs |
.DE |
.NH 2 |
\&Callback Procedures |
.IX RPC "callback procedures" |
.LP |
Occasionally, it is useful to have a server become a client, |
and make an RPC call back to the process which is its client. |
An example is remote debugging, |
where the client is a window system program, |
and the server is a debugger running on the remote machine. |
Most of the time, |
the user clicks a mouse button at the debugging window, |
which converts this to a debugger command, |
and then makes an RPC call to the server |
(where the debugger is actually running), |
telling it to execute that command. |
However, when the debugger hits a breakpoint, the roles are reversed, |
and the debugger wants to make an rpc call to the window program, |
so that it can inform the user that a breakpoint has been reached. |
.LP |
In order to do an RPC callback, |
you need a program number to make the RPC call on. |
Since this will be a dynamically generated program number, |
it should be in the transient range, |
.I "0x40000000 - 0x5fffffff" . |
The routine |
.I gettransient() |
returns a valid program number in the transient range, |
and registers it with the portmapper. |
It only talks to the portmapper running on the same machine as the |
.I gettransient() |
routine itself. The call to |
.I pmap_set() |
is a test and set operation, |
in that it indivisibly tests whether a program number |
has already been registered, |
and if it has not, then reserves it. On return, the |
.I sockp |
argument will contain a socket that can be used |
as the argument to an |
.I svcudp_create() |
or |
.I svctcp_create() |
call. |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <sys/socket.h> |
|
gettransient(proto, vers, sockp) |
int proto, vers, *sockp; |
{ |
static int prognum = 0x40000000; |
int s, len, socktype; |
struct sockaddr_in addr; |
|
switch(proto) { |
case IPPROTO_UDP: |
socktype = SOCK_DGRAM; |
break; |
case IPPROTO_TCP: |
socktype = SOCK_STREAM; |
break; |
default: |
fprintf(stderr, "unknown protocol type\en"); |
return 0; |
} |
if (*sockp == RPC_ANYSOCK) { |
if ((s = socket(AF_INET, socktype, 0)) < 0) { |
perror("socket"); |
return (0); |
} |
*sockp = s; |
} |
else |
s = *sockp; |
addr.sin_addr.s_addr = 0; |
addr.sin_family = AF_INET; |
addr.sin_port = 0; |
len = sizeof(addr); |
.ft I |
/* |
* may be already bound, so don't check for error |
*/ |
.ft CW |
bind(s, &addr, len); |
if (getsockname(s, &addr, &len)< 0) { |
perror("getsockname"); |
return (0); |
} |
while (!pmap_set(prognum++, vers, proto, |
ntohs(addr.sin_port))) continue; |
return (prognum-1); |
} |
.vs |
.DE |
.SH |
Note: |
.I |
The call to |
.I ntohs() |
is necessary to ensure that the port number in |
.I "addr.sin_port" , |
which is in |
.I network |
byte order, is passed in |
.I host |
byte order (as |
.I pmap_set() |
expects). See the |
.I byteorder(3N) |
man page for more details on the conversion of network |
addresses from network to host byte order. |
.KS |
.LP |
The following pair of programs illustrate how to use the |
.I gettransient() |
routine. |
The client makes an RPC call to the server, |
passing it a transient program number. |
Then the client waits around to receive a callback |
from the server at that program number. |
The server registers the program |
.I EXAMPLEPROG |
so that it can receive the RPC call |
informing it of the callback program number. |
Then at some random time (on receiving an |
.I ALRM |
signal in this example), it sends a callback RPC call, |
using the program number it received earlier. |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* client |
*/ |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> |
|
int callback(); |
char hostname[256]; |
|
main() |
{ |
int x, ans, s; |
SVCXPRT *xprt; |
|
gethostname(hostname, sizeof(hostname)); |
s = RPC_ANYSOCK; |
x = gettransient(IPPROTO_UDP, 1, &s); |
fprintf(stderr, "client gets prognum %d\en", x); |
if ((xprt = svcudp_create(s)) == NULL) { |
fprintf(stderr, "rpc_server: svcudp_create\en"); |
exit(1); |
} |
.ft I |
/* protocol is 0 - gettransient does registering |
*/ |
.ft CW |
(void)svc_register(xprt, x, 1, callback, 0); |
ans = callrpc(hostname, EXAMPLEPROG, EXAMPLEVERS, |
EXAMPLEPROC_CALLBACK, xdr_int, &x, xdr_void, 0); |
if ((enum clnt_stat) ans != RPC_SUCCESS) { |
fprintf(stderr, "call: "); |
clnt_perrno(ans); |
fprintf(stderr, "\en"); |
} |
svc_run(); |
fprintf(stderr, "Error: svc_run shouldn't return\en"); |
} |
|
callback(rqstp, transp) |
register struct svc_req *rqstp; |
register SVCXPRT *transp; |
{ |
switch (rqstp->rq_proc) { |
case 0: |
if (!svc_sendreply(transp, xdr_void, 0)) { |
fprintf(stderr, "err: exampleprog\en"); |
return (1); |
} |
return (0); |
case 1: |
if (!svc_getargs(transp, xdr_void, 0)) { |
svcerr_decode(transp); |
return (1); |
} |
fprintf(stderr, "client got callback\en"); |
if (!svc_sendreply(transp, xdr_void, 0)) { |
fprintf(stderr, "err: exampleprog"); |
return (1); |
} |
} |
} |
.vs |
.DE |
.KE |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* server |
*/ |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> |
#include <sys/signal.h> |
|
char *getnewprog(); |
char hostname[256]; |
int docallback(); |
int pnum; /* \fIprogram number for callback routine\fP */ |
|
main() |
{ |
gethostname(hostname, sizeof(hostname)); |
registerrpc(EXAMPLEPROG, EXAMPLEVERS, |
EXAMPLEPROC_CALLBACK, getnewprog, xdr_int, xdr_void); |
fprintf(stderr, "server going into svc_run\en"); |
signal(SIGALRM, docallback); |
alarm(10); |
svc_run(); |
fprintf(stderr, "Error: svc_run shouldn't return\en"); |
} |
|
char * |
getnewprog(pnump) |
char *pnump; |
{ |
pnum = *(int *)pnump; |
return NULL; |
} |
|
docallback() |
{ |
int ans; |
|
ans = callrpc(hostname, pnum, 1, 1, xdr_void, 0, |
xdr_void, 0); |
if (ans != 0) { |
fprintf(stderr, "server: "); |
clnt_perrno(ans); |
fprintf(stderr, "\en"); |
} |
} |
.vs |
.DE |
/PSD.doc/rpc.rfc.ms
0,0 → 1,1302
.\" |
.\" Must use -- tbl -- with this one |
.\" |
.\" @(#)rpc.rfc.ms 2.2 88/08/05 4.0 RPCSRC |
.de BT |
.if \\n%=1 .tl ''- % -'' |
.. |
.ND |
.\" prevent excess underlining in nroff |
.if n .fp 2 R |
.OH 'Remote Procedure Calls: Protocol Specification''Page %' |
.EH 'Page %''Remote Procedure Calls: Protocol Specification' |
.if \\n%=1 .bp |
.SH |
\&Remote Procedure Calls: Protocol Specification |
.LP |
.NH 0 |
\&Status of this Memo |
.LP |
Note: This chapter specifies a protocol that Sun Microsystems, Inc., |
and others are using. |
It has been designated RFC1050 by the ARPA Network |
Information Center. |
.LP |
.NH 1 |
\&Introduction |
.LP |
This chapter specifies a message protocol used in implementing |
Sun's Remote Procedure Call (RPC) package. (The message protocol is |
specified with the External Data Representation (XDR) language. |
See the |
.I "External Data Representation Standard: Protocol Specification" |
for the details. Here, we assume that the reader is familiar |
with XDR and do not attempt to justify it or its uses). The paper |
by Birrell and Nelson [1] is recommended as an excellent background |
to and justification of RPC. |
.NH 2 |
\&Terminology |
.LP |
This chapter discusses servers, services, programs, procedures, |
clients, and versions. A server is a piece of software where network |
services are implemented. A network service is a collection of one |
or more remote programs. A remote program implements one or more |
remote procedures; the procedures, their parameters, and results are |
documented in the specific program's protocol specification (see the |
\fIPort Mapper Program Protocol\fP\, below, for an example). Network |
clients are pieces of software that initiate remote procedure calls |
to services. A server may support more than one version of a remote |
program in order to be forward compatible with changing protocols. |
.LP |
For example, a network file service may be composed of two programs. |
One program may deal with high-level applications such as file system |
access control and locking. The other may deal with low-level file |
IO and have procedures like "read" and "write". A client machine of |
the network file service would call the procedures associated with |
the two programs of the service on behalf of some user on the client |
machine. |
.NH 2 |
\&The RPC Model |
.LP |
The remote procedure call model is similar to the local procedure |
call model. In the local case, the caller places arguments to a |
procedure in some well-specified location (such as a result |
register). It then transfers control to the procedure, and |
eventually gains back control. At that point, the results of the |
procedure are extracted from the well-specified location, and the |
caller continues execution. |
.LP |
The remote procedure call is similar, in that one thread of control |
logically winds through two processes\(emone is the caller's process, |
the other is a server's process. That is, the caller process sends a |
call message to the server process and waits (blocks) for a reply |
message. The call message contains the procedure's parameters, among |
other things. The reply message contains the procedure's results, |
among other things. Once the reply message is received, the results |
of the procedure are extracted, and caller's execution is resumed. |
.LP |
On the server side, a process is dormant awaiting the arrival of a |
call message. When one arrives, the server process extracts the |
procedure's parameters, computes the results, sends a reply message, |
and then awaits the next call message. |
.LP |
Note that in this model, only one of the two processes is active at |
any given time. However, this model is only given as an example. |
The RPC protocol makes no restrictions on the concurrency model |
implemented, and others are possible. For example, an implementation |
may choose to have RPC calls be asynchronous, so that the client may |
do useful work while waiting for the reply from the server. Another |
possibility is to have the server create a task to process an |
incoming request, so that the server can be free to receive other |
requests. |
.NH 2 |
\&Transports and Semantics |
.LP |
The RPC protocol is independent of transport protocols. That is, RPC |
does not care how a message is passed from one process to another. |
The protocol deals only with specification and interpretation of |
messages. |
.LP |
It is important to point out that RPC does not try to implement any |
kind of reliability and that the application must be aware of the |
type of transport protocol underneath RPC. If it knows it is running |
on top of a reliable transport such as TCP/IP[6], then most of the |
work is already done for it. On the other hand, if it is running on |
top of an unreliable transport such as UDP/IP[7], it must implement |
is own retransmission and time-out policy as the RPC layer does not |
provide this service. |
.LP |
Because of transport independence, the RPC protocol does not attach |
specific semantics to the remote procedures or their execution. |
Semantics can be inferred from (but should be explicitly specified |
by) the underlying transport protocol. For example, consider RPC |
running on top of an unreliable transport such as UDP/IP. If an |
application retransmits RPC messages after short time-outs, the only |
thing it can infer if it receives no reply is that the procedure was |
executed zero or more times. If it does receive a reply, then it can |
infer that the procedure was executed at least once. |
.LP |
A server may wish to remember previously granted requests from a |
client and not regrant them in order to insure some degree of |
execute-at-most-once semantics. A server can do this by taking |
advantage of the transaction ID that is packaged with every RPC |
request. The main use of this transaction is by the client RPC layer |
in matching replies to requests. However, a client application may |
choose to reuse its previous transaction ID when retransmitting a |
request. The server application, knowing this fact, may choose to |
remember this ID after granting a request and not regrant requests |
with the same ID in order to achieve some degree of |
execute-at-most-once semantics. The server is not allowed to examine |
this ID in any other way except as a test for equality. |
.LP |
On the other hand, if using a reliable transport such as TCP/IP, the |
application can infer from a reply message that the procedure was |
executed exactly once, but if it receives no reply message, it cannot |
assume the remote procedure was not executed. Note that even if a |
connection-oriented protocol like TCP is used, an application still |
needs time-outs and reconnection to handle server crashes. |
.LP |
There are other possibilities for transports besides datagram- or |
connection-oriented protocols. For example, a request-reply protocol |
such as VMTP[2] is perhaps the most natural transport for RPC. |
.SH |
.I |
NOTE: At Sun, RPC is currently implemented on top of both TCP/IP |
and UDP/IP transports. |
.LP |
.NH 2 |
\&Binding and Rendezvous Independence |
.LP |
The act of binding a client to a service is NOT part of the remote |
procedure call specification. This important and necessary function |
is left up to some higher-level software. (The software may use RPC |
itself\(emsee the \fIPort Mapper Program Protocol\fP\, below). |
.LP |
Implementors should think of the RPC protocol as the jump-subroutine |
instruction ("JSR") of a network; the loader (binder) makes JSR |
useful, and the loader itself uses JSR to accomplish its task. |
Likewise, the network makes RPC useful, using RPC to accomplish this |
task. |
.NH 2 |
\&Authentication |
.LP |
The RPC protocol provides the fields necessary for a client to |
identify itself to a service and vice-versa. Security and access |
control mechanisms can be built on top of the message authentication. |
Several different authentication protocols can be supported. A field |
in the RPC header indicates which protocol is being used. More |
information on specific authentication protocols can be found in the |
\fIAuthentication Protocols\fP\, |
below. |
.KS |
.NH 1 |
\&RPC Protocol Requirements |
.LP |
The RPC protocol must provide for the following: |
.IP 1. |
Unique specification of a procedure to be called. |
.IP 2. |
Provisions for matching response messages to request messages. |
.KE |
.IP 3. |
Provisions for authenticating the caller to service and vice-versa. |
.LP |
Besides these requirements, features that detect the following are |
worth supporting because of protocol roll-over errors, implementation |
bugs, user error, and network administration: |
.IP 1. |
RPC protocol mismatches. |
.IP 2. |
Remote program protocol version mismatches. |
.IP 3. |
Protocol errors (such as misspecification of a procedure's parameters). |
.IP 4. |
Reasons why remote authentication failed. |
.IP 5. |
Any other reasons why the desired procedure was not called. |
.NH 2 |
\&Programs and Procedures |
.LP |
The RPC call message has three unsigned fields: remote program |
number, remote program version number, and remote procedure number. |
The three fields uniquely identify the procedure to be called. |
Program numbers are administered by some central authority (like |
Sun). Once an implementor has a program number, he can implement his |
remote program; the first implementation would most likely have the |
version number of 1. Because most new protocols evolve into better, |
stable, and mature protocols, a version field of the call message |
identifies which version of the protocol the caller is using. |
Version numbers make speaking old and new protocols through the same |
server process possible. |
.LP |
The procedure number identifies the procedure to be called. These |
numbers are documented in the specific program's protocol |
specification. For example, a file service's protocol specification |
may state that its procedure number 5 is "read" and procedure number |
12 is "write". |
.LP |
Just as remote program protocols may change over several versions, |
the actual RPC message protocol could also change. Therefore, the |
call message also has in it the RPC version number, which is always |
equal to two for the version of RPC described here. |
.LP |
The reply message to a request message has enough information to |
distinguish the following error conditions: |
.IP 1. |
The remote implementation of RPC does speak protocol version 2. |
The lowest and highest supported RPC version numbers are returned. |
.IP 2. |
The remote program is not available on the remote system. |
.IP 3. |
The remote program does not support the requested version number. |
The lowest and highest supported remote program version numbers are |
returned. |
.IP 4. |
The requested procedure number does not exist. (This is usually a |
caller side protocol or programming error.) |
.IP 5. |
The parameters to the remote procedure appear to be garbage from the |
server's point of view. (Again, this is usually caused by a |
disagreement about the protocol between client and service.) |
.NH 2 |
\&Authentication |
.LP |
Provisions for authentication of caller to service and vice-versa are |
provided as a part of the RPC protocol. The call message has two |
authentication fields, the credentials and verifier. The reply |
message has one authentication field, the response verifier. The RPC |
protocol specification defines all three fields to be the following |
opaque type: |
.DS |
.ft CW |
.vs 11 |
enum auth_flavor { |
AUTH_NULL = 0, |
AUTH_UNIX = 1, |
AUTH_SHORT = 2, |
AUTH_DES = 3 |
/* \fIand more to be defined\fP */ |
}; |
|
struct opaque_auth { |
auth_flavor flavor; |
opaque body<400>; |
}; |
.DE |
.LP |
In simple English, any |
.I opaque_auth |
structure is an |
.I auth_flavor |
enumeration followed by bytes which are opaque to the RPC protocol |
implementation. |
.LP |
The interpretation and semantics of the data contained within the |
authentication fields is specified by individual, independent |
authentication protocol specifications. (See |
\fIAuthentication Protocols\fP\, |
below, for definitions of the various authentication protocols.) |
.LP |
If authentication parameters were rejected, the response message |
contains information stating why they were rejected. |
.NH 2 |
\&Program Number Assignment |
.LP |
Program numbers are given out in groups of |
.I 0x20000000 |
(decimal 536870912) according to the following chart: |
.TS |
box tab (&) ; |
lfI lfI |
rfL cfI . |
Program Numbers&Description |
_ |
.sp .5 |
0 - 1fffffff&Defined by Sun |
20000000 - 3fffffff&Defined by user |
40000000 - 5fffffff&Transient |
60000000 - 7fffffff&Reserved |
80000000 - 9fffffff&Reserved |
a0000000 - bfffffff&Reserved |
c0000000 - dfffffff&Reserved |
e0000000 - ffffffff&Reserved |
.TE |
.LP |
The first group is a range of numbers administered by Sun |
Microsystems and should be identical for all sites. The second range |
is for applications peculiar to a particular site. This range is |
intended primarily for debugging new programs. When a site develops |
an application that might be of general interest, that application |
should be given an assigned number in the first range. The third |
group is for applications that generate program numbers dynamically. |
The final groups are reserved for future use, and should not be used. |
.NH 2 |
\&Other Uses of the RPC Protocol |
.LP |
The intended use of this protocol is for calling remote procedures. |
That is, each call message is matched with a response message. |
However, the protocol itself is a message-passing protocol with which |
other (non-RPC) protocols can be implemented. Sun currently uses, or |
perhaps abuses, the RPC message protocol for the following two |
(non-RPC) protocols: batching (or pipelining) and broadcast RPC. |
These two protocols are discussed but not defined below. |
.NH 3 |
\&Batching |
.LP |
Batching allows a client to send an arbitrarily large sequence of |
call messages to a server; batching typically uses reliable byte |
stream protocols (like TCP/IP) for its transport. In the case of |
batching, the client never waits for a reply from the server, and the |
server does not send replies to batch requests. A sequence of batch |
calls is usually terminated by a legitimate RPC in order to flush the |
pipeline (with positive acknowledgement). |
.NH 3 |
\&Broadcast RPC |
.LP |
In broadcast RPC-based protocols, the client sends a broadcast packet |
to the network and waits for numerous replies. Broadcast RPC uses |
unreliable, packet-based protocols (like UDP/IP) as its transports. |
Servers that support broadcast protocols only respond when the |
request is successfully processed, and are silent in the face of |
errors. Broadcast RPC uses the Port Mapper RPC service to achieve |
its semantics. See the \fIPort Mapper Program Protocol\fP\, below, |
for more information. |
.KS |
.NH 1 |
\&The RPC Message Protocol |
.LP |
This section defines the RPC message protocol in the XDR data |
description language. The message is defined in a top-down style. |
.ie t .DS |
.el .DS L |
.ft CW |
enum msg_type { |
CALL = 0, |
REPLY = 1 |
}; |
|
.ft I |
/* |
* A reply to a call message can take on two forms: |
* The message was either accepted or rejected. |
*/ |
.ft CW |
enum reply_stat { |
MSG_ACCEPTED = 0, |
MSG_DENIED = 1 |
}; |
|
.ft I |
/* |
* Given that a call message was accepted, the following is the |
* status of an attempt to call a remote procedure. |
*/ |
.ft CW |
enum accept_stat { |
SUCCESS = 0, /* \fIRPC executed successfully \fP*/ |
PROG_UNAVAIL = 1, /* \fIremote hasn't exported program \fP*/ |
PROG_MISMATCH = 2, /* \fIremote can't support version # \fP*/ |
PROC_UNAVAIL = 3, /* \fIprogram can't support procedure \fP*/ |
GARBAGE_ARGS = 4 /* \fIprocedure can't decode params \fP*/ |
}; |
.DE |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* Reasons why a call message was rejected: |
*/ |
.ft CW |
enum reject_stat { |
RPC_MISMATCH = 0, /* \fIRPC version number != 2 \fP*/ |
AUTH_ERROR = 1 /* \fIremote can't authenticate caller \fP*/ |
}; |
|
.ft I |
/* |
* Why authentication failed: |
*/ |
.ft CW |
enum auth_stat { |
AUTH_BADCRED = 1, /* \fIbad credentials \fP*/ |
AUTH_REJECTEDCRED = 2, /* \fIclient must begin new session \fP*/ |
AUTH_BADVERF = 3, /* \fIbad verifier \fP*/ |
AUTH_REJECTEDVERF = 4, /* \fIverifier expired or replayed \fP*/ |
AUTH_TOOWEAK = 5 /* \fIrejected for security reasons \fP*/ |
}; |
.DE |
.KE |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* The RPC message: |
* All messages start with a transaction identifier, xid, |
* followed by a two-armed discriminated union. The union's |
* discriminant is a msg_type which switches to one of the two |
* types of the message. The xid of a \fIREPLY\fP message always |
* matches that of the initiating \fICALL\fP message. NB: The xid |
* field is only used for clients matching reply messages with |
* call messages or for servers detecting retransmissions; the |
* service side cannot treat this id as any type of sequence |
* number. |
*/ |
.ft CW |
struct rpc_msg { |
unsigned int xid; |
union switch (msg_type mtype) { |
case CALL: |
call_body cbody; |
case REPLY: |
reply_body rbody; |
} body; |
}; |
.DE |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* Body of an RPC request call: |
* In version 2 of the RPC protocol specification, rpcvers must |
* be equal to 2. The fields prog, vers, and proc specify the |
* remote program, its version number, and the procedure within |
* the remote program to be called. After these fields are two |
* authentication parameters: cred (authentication credentials) |
* and verf (authentication verifier). The two authentication |
* parameters are followed by the parameters to the remote |
* procedure, which are specified by the specific program |
* protocol. |
*/ |
.ft CW |
struct call_body { |
unsigned int rpcvers; /* \fImust be equal to two (2) \fP*/ |
unsigned int prog; |
unsigned int vers; |
unsigned int proc; |
opaque_auth cred; |
opaque_auth verf; |
/* \fIprocedure specific parameters start here \fP*/ |
}; |
.DE |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* Body of a reply to an RPC request: |
* The call message was either accepted or rejected. |
*/ |
.ft CW |
union reply_body switch (reply_stat stat) { |
case MSG_ACCEPTED: |
accepted_reply areply; |
case MSG_DENIED: |
rejected_reply rreply; |
} reply; |
.DE |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* Reply to an RPC request that was accepted by the server: |
* there could be an error even though the request was accepted. |
* The first field is an authentication verifier that the server |
* generates in order to validate itself to the caller. It is |
* followed by a union whose discriminant is an enum |
* accept_stat. The \fISUCCESS\fP arm of the union is protocol |
* specific. The \fIPROG_UNAVAIL\fP, \fIPROC_UNAVAIL\fP, and \fIGARBAGE_ARGP\fP |
* arms of the union are void. The \fIPROG_MISMATCH\fP arm specifies |
* the lowest and highest version numbers of the remote program |
* supported by the server. |
*/ |
.ft CW |
struct accepted_reply { |
opaque_auth verf; |
union switch (accept_stat stat) { |
case SUCCESS: |
opaque results[0]; |
/* \fIprocedure-specific results start here\fP */ |
case PROG_MISMATCH: |
struct { |
unsigned int low; |
unsigned int high; |
} mismatch_info; |
default: |
.ft I |
/* |
* Void. Cases include \fIPROG_UNAVAIL, PROC_UNAVAIL\fP, |
* and \fIGARBAGE_ARGS\fP. |
*/ |
.ft CW |
void; |
} reply_data; |
}; |
.DE |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* Reply to an RPC request that was rejected by the server: |
* The request can be rejected for two reasons: either the |
* server is not running a compatible version of the RPC |
* protocol (\fIRPC_MISMATCH\fP), or the server refuses to |
* authenticate the caller (\fIAUTH_ERROR\fP). In case of an RPC |
* version mismatch, the server returns the lowest and highest |
* supported RPC version numbers. In case of refused |
* authentication, failure status is returned. |
*/ |
.ft CW |
union rejected_reply switch (reject_stat stat) { |
case RPC_MISMATCH: |
struct { |
unsigned int low; |
unsigned int high; |
} mismatch_info; |
case AUTH_ERROR: |
auth_stat stat; |
}; |
.DE |
.NH 1 |
\&Authentication Protocols |
.LP |
As previously stated, authentication parameters are opaque, but |
open-ended to the rest of the RPC protocol. This section defines |
some "flavors" of authentication implemented at (and supported by) |
Sun. Other sites are free to invent new authentication types, with |
the same rules of flavor number assignment as there is for program |
number assignment. |
.NH 2 |
\&Null Authentication |
.LP |
Often calls must be made where the caller does not know who he is or |
the server does not care who the caller is. In this case, the flavor |
value (the discriminant of the \fIopaque_auth\fP's union) of the RPC |
message's credentials, verifier, and response verifier is |
.I AUTH_NULL . |
The bytes of the opaque_auth's body are undefined. |
It is recommended that the opaque length be zero. |
.NH 2 |
\&UNIX Authentication |
.LP |
The caller of a remote procedure may wish to identify himself as he |
is identified on a UNIX system. The value of the credential's |
discriminant of an RPC call message is |
.I AUTH_UNIX . |
The bytes of |
the credential's opaque body encode the following structure: |
.DS |
.ft CW |
struct auth_unix { |
unsigned int stamp; |
string machinename<255>; |
unsigned int uid; |
unsigned int gid; |
unsigned int gids<10>; |
}; |
.DE |
The |
.I stamp |
is an arbitrary ID which the caller machine may |
generate. The |
.I machinename |
is the name of the caller's machine (like "krypton"). The |
.I uid |
is the caller's effective user ID. The |
.I gid |
is the caller's effective group ID. The |
.I gids |
is a |
counted array of groups which contain the caller as a member. The |
verifier accompanying the credentials should be of |
.I AUTH_NULL |
(defined above). |
.LP |
The value of the discriminant of the response verifier received in |
the reply message from the server may be |
.I AUTH_NULL |
or |
.I AUTH_SHORT . |
In the case of |
.I AUTH_SHORT , |
the bytes of the response verifier's string encode an opaque |
structure. This new opaque structure may now be passed to the server |
instead of the original |
.I AUTH_UNIX |
flavor credentials. The server keeps a cache which maps shorthand |
opaque structures (passed back by way of an |
.I AUTH_SHORT |
style response verifier) to the original credentials of the caller. |
The caller can save network bandwidth and server cpu cycles by using |
the new credentials. |
.LP |
The server may flush the shorthand opaque structure at any time. If |
this happens, the remote procedure call message will be rejected due |
to an authentication error. The reason for the failure will be |
.I AUTH_REJECTEDCRED . |
At this point, the caller may wish to try the original |
.I AUTH_UNIX |
style of credentials. |
.KS |
.NH 2 |
\&DES Authentication |
.LP |
UNIX authentication suffers from two major problems: |
.IP 1. |
The naming is too UNIX-system oriented. |
.IP 2. |
There is no verifier, so credentials can easily be faked. |
.LP |
DES authentication attempts to fix these two problems. |
.KE |
.NH 3 |
\&Naming |
.LP |
The first problem is handled by addressing the caller by a simple |
string of characters instead of by an operating system specific |
integer. This string of characters is known as the "netname" or |
network name of the caller. The server is not allowed to interpret |
the contents of the caller's name in any other way except to |
identify the caller. Thus, netnames should be unique for every |
caller in the internet. |
.LP |
It is up to each operating system's implementation of DES |
authentication to generate netnames for its users that insure this |
uniqueness when they call upon remote servers. Operating systems |
already know how to distinguish users local to their systems. It is |
usually a simple matter to extend this mechanism to the network. |
For example, a UNIX user at Sun with a user ID of 515 might be |
assigned the following netname: "unix.515@sun.com". This netname |
contains three items that serve to insure it is unique. Going |
backwards, there is only one naming domain called "sun.com" in the |
internet. Within this domain, there is only one UNIX user with |
user ID 515. However, there may be another user on another |
operating system, for example VMS, within the same naming domain |
that, by coincidence, happens to have the same user ID. To insure |
that these two users can be distinguished we add the operating |
system name. So one user is "unix.515@sun.com" and the other is |
"vms.515@sun.com". |
.LP |
The first field is actually a naming method rather than an |
operating system name. It just happens that today there is almost |
a one-to-one correspondence between naming methods and operating |
systems. If the world could agree on a naming standard, the first |
field could be the name of that standard, instead of an operating |
system name. |
.LP |
.NH 3 |
\&DES Authentication Verifiers |
.LP |
Unlike UNIX authentication, DES authentication does have a verifier |
so the server can validate the client's credential (and |
vice-versa). The contents of this verifier is primarily an |
encrypted timestamp. The server can decrypt this timestamp, and if |
it is close to what the real time is, then the client must have |
encrypted it correctly. The only way the client could encrypt it |
correctly is to know the "conversation key" of the RPC session. And |
if the client knows the conversation key, then it must be the real |
client. |
.LP |
The conversation key is a DES [5] key which the client generates |
and notifies the server of in its first RPC call. The conversation |
key is encrypted using a public key scheme in this first |
transaction. The particular public key scheme used in DES |
authentication is Diffie-Hellman [3] with 192-bit keys. The |
details of this encryption method are described later. |
.LP |
The client and the server need the same notion of the current time |
in order for all of this to work. If network time synchronization |
cannot be guaranteed, then client can synchronize with the server |
before beginning the conversation, perhaps by consulting the |
Internet Time Server (TIME[4]). |
.LP |
The way a server determines if a client timestamp is valid is |
somewhat complicated. For any other transaction but the first, the |
server just checks for two things: |
.IP 1. |
the timestamp is greater than the one previously seen from the |
same client. |
.IP 2. |
the timestamp has not expired. |
.LP |
A timestamp is expired if the server's time is later than the sum |
of the client's timestamp plus what is known as the client's |
"window". The "window" is a number the client passes (encrypted) |
to the server in its first transaction. You can think of it as a |
lifetime for the credential. |
.LP |
This explains everything but the first transaction. In the first |
transaction, the server checks only that the timestamp has not |
expired. If this was all that was done though, then it would be |
quite easy for the client to send random data in place of the |
timestamp with a fairly good chance of succeeding. As an added |
check, the client sends an encrypted item in the first transaction |
known as the "window verifier" which must be equal to the window |
minus 1, or the server will reject the credential. |
.LP |
The client too must check the verifier returned from the server to |
be sure it is legitimate. The server sends back to the client the |
encrypted timestamp it received from the client, minus one second. |
If the client gets anything different than this, it will reject it. |
.LP |
.NH 3 |
\&Nicknames and Clock Synchronization |
.LP |
After the first transaction, the server's DES authentication |
subsystem returns in its verifier to the client an integer |
"nickname" which the client may use in its further transactions |
instead of passing its netname, encrypted DES key and window every |
time. The nickname is most likely an index into a table on the |
server which stores for each client its netname, decrypted DES key |
and window. |
.LP |
Though they originally were synchronized, the client's and server's |
clocks can get out of sync again. When this happens the client RPC |
subsystem most likely will get back |
.I RPC_AUTHERROR |
at which point it should resynchronize. |
.LP |
A client may still get the |
.I RPC_AUTHERROR |
error even though it is |
synchronized with the server. The reason is that the server's |
nickname table is a limited size, and it may flush entries whenever |
it wants. A client should resend its original credential in this |
case and the server will give it a new nickname. If a server |
crashes, the entire nickname table gets flushed, and all clients |
will have to resend their original credentials. |
.KS |
.NH 3 |
\&DES Authentication Protocol (in XDR language) |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* There are two kinds of credentials: one in which the client uses |
* its full network name, and one in which it uses its "nickname" |
* (just an unsigned integer) given to it by the server. The |
* client must use its fullname in its first transaction with the |
* server, in which the server will return to the client its |
* nickname. The client may use its nickname in all further |
* transactions with the server. There is no requirement to use the |
* nickname, but it is wise to use it for performance reasons. |
*/ |
.ft CW |
enum authdes_namekind { |
ADN_FULLNAME = 0, |
ADN_NICKNAME = 1 |
}; |
|
.ft I |
/* |
* A 64-bit block of encrypted DES data |
*/ |
.ft CW |
typedef opaque des_block[8]; |
|
.ft I |
/* |
* Maximum length of a network user's name |
*/ |
.ft CW |
const MAXNETNAMELEN = 255; |
|
.ft I |
/* |
* A fullname contains the network name of the client, an encrypted |
* conversation key and the window. The window is actually a |
* lifetime for the credential. If the time indicated in the |
* verifier timestamp plus the window has past, then the server |
* should expire the request and not grant it. To insure that |
* requests are not replayed, the server should insist that |
* timestamps are greater than the previous one seen, unless it is |
* the first transaction. In the first transaction, the server |
* checks instead that the window verifier is one less than the |
* window. |
*/ |
.ft CW |
struct authdes_fullname { |
string name<MAXNETNAMELEN>; /* \fIname of client \f(CW*/ |
des_block key; /* \fIPK encrypted conversation key \f(CW*/ |
unsigned int window; /* \fIencrypted window \f(CW*/ |
}; |
|
.ft I |
/* |
* A credential is either a fullname or a nickname |
*/ |
.ft CW |
union authdes_cred switch (authdes_namekind adc_namekind) { |
case ADN_FULLNAME: |
authdes_fullname adc_fullname; |
case ADN_NICKNAME: |
unsigned int adc_nickname; |
}; |
|
.ft I |
/* |
* A timestamp encodes the time since midnight, January 1, 1970. |
*/ |
.ft CW |
struct timestamp { |
unsigned int seconds; /* \fIseconds \fP*/ |
unsigned int useconds; /* \fIand microseconds \fP*/ |
}; |
|
.ft I |
/* |
* Verifier: client variety |
* The window verifier is only used in the first transaction. In |
* conjunction with a fullname credential, these items are packed |
* into the following structure before being encrypted: |
* |
* \f(CWstruct {\fP |
* \f(CWadv_timestamp; \fP-- one DES block |
* \f(CWadc_fullname.window; \fP-- one half DES block |
* \f(CWadv_winverf; \fP-- one half DES block |
* \f(CW}\fP |
* This structure is encrypted using CBC mode encryption with an |
* input vector of zero. All other encryptions of timestamps use |
* ECB mode encryption. |
*/ |
.ft CW |
struct authdes_verf_clnt { |
timestamp adv_timestamp; /* \fIencrypted timestamp \fP*/ |
unsigned int adv_winverf; /* \fIencrypted window verifier \fP*/ |
}; |
|
.ft I |
/* |
* Verifier: server variety |
* The server returns (encrypted) the same timestamp the client |
* gave it minus one second. It also tells the client its nickname |
* to be used in future transactions (unencrypted). |
*/ |
.ft CW |
struct authdes_verf_svr { |
timestamp adv_timeverf; /* \fIencrypted verifier \fP*/ |
unsigned int adv_nickname; /* \fInew nickname for client \fP*/ |
}; |
.DE |
.KE |
.NH 3 |
\&Diffie-Hellman Encryption |
.LP |
In this scheme, there are two constants, |
.I BASE |
and |
.I MODULUS . |
The |
particular values Sun has chosen for these for the DES |
authentication protocol are: |
.ie t .DS |
.el .DS L |
.ft CW |
const BASE = 3; |
const MODULUS = |
"d4a0ba0250b6fd2ec626e7efd637df76c716e22d0944b88b"; /* \fIhex \fP*/ |
.DE |
.ft R |
The way this scheme works is best explained by an example. Suppose |
there are two people "A" and "B" who want to send encrypted |
messages to each other. So, A and B both generate "secret" keys at |
random which they do not reveal to anyone. Let these keys be |
represented as SK(A) and SK(B). They also publish in a public |
directory their "public" keys. These keys are computed as follows: |
.ie t .DS |
.el .DS L |
.ft CW |
PK(A) = ( BASE ** SK(A) ) mod MODULUS |
PK(B) = ( BASE ** SK(B) ) mod MODULUS |
.DE |
.ft R |
The "**" notation is used here to represent exponentiation. Now, |
both A and B can arrive at the "common" key between them, |
represented here as CK(A, B), without revealing their secret keys. |
.LP |
A computes: |
.ie t .DS |
.el .DS L |
.ft CW |
CK(A, B) = ( PK(B) ** SK(A)) mod MODULUS |
.DE |
.ft R |
while B computes: |
.ie t .DS |
.el .DS L |
.ft CW |
CK(A, B) = ( PK(A) ** SK(B)) mod MODULUS |
.DE |
.ft R |
These two can be shown to be equivalent: |
.ie t .DS |
.el .DS L |
.ft CW |
(PK(B) ** SK(A)) mod MODULUS = (PK(A) ** SK(B)) mod MODULUS |
.DE |
.ft R |
We drop the "mod MODULUS" parts and assume modulo arithmetic to |
simplify things: |
.ie t .DS |
.el .DS L |
.ft CW |
PK(B) ** SK(A) = PK(A) ** SK(B) |
.DE |
.ft R |
Then, replace PK(B) by what B computed earlier and likewise for |
PK(A). |
.ie t .DS |
.el .DS L |
.ft CW |
((BASE ** SK(B)) ** SK(A) = (BASE ** SK(A)) ** SK(B) |
.DE |
.ft R |
which leads to: |
.ie t .DS |
.el .DS L |
.ft CW |
BASE ** (SK(A) * SK(B)) = BASE ** (SK(A) * SK(B)) |
.DE |
.ft R |
This common key CK(A, B) is not used to encrypt the timestamps used |
in the protocol. Rather, it is used only to encrypt a conversation |
key which is then used to encrypt the timestamps. The reason for |
doing this is to use the common key as little as possible, for fear |
that it could be broken. Breaking the conversation key is a far |
less serious offense, since conversations are relatively |
short-lived. |
.LP |
The conversation key is encrypted using 56-bit DES keys, yet the |
common key is 192 bits. To reduce the number of bits, 56 bits are |
selected from the common key as follows. The middle-most 8-bytes |
are selected from the common key, and then parity is added to the |
lower order bit of each byte, producing a 56-bit key with 8 bits of |
parity. |
.KS |
.NH 1 |
\&Record Marking Standard |
.LP |
When RPC messages are passed on top of a byte stream protocol (like |
TCP/IP), it is necessary, or at least desirable, to delimit one |
message from another in order to detect and possibly recover from |
user protocol errors. This is called record marking (RM). Sun uses |
this RM/TCP/IP transport for passing RPC messages on TCP streams. |
One RPC message fits into one RM record. |
.LP |
A record is composed of one or more record fragments. A record |
fragment is a four-byte header followed by 0 to (2**31) - 1 bytes of |
fragment data. The bytes encode an unsigned binary number; as with |
XDR integers, the byte order is from highest to lowest. The number |
encodes two values\(ema boolean which indicates whether the fragment |
is the last fragment of the record (bit value 1 implies the fragment |
is the last fragment) and a 31-bit unsigned binary value which is the |
length in bytes of the fragment's data. The boolean value is the |
highest-order bit of the header; the length is the 31 low-order bits. |
(Note that this record specification is NOT in XDR standard form!) |
.KE |
.KS |
.NH 1 |
\&The RPC Language |
.LP |
Just as there was a need to describe the XDR data-types in a formal |
language, there is also need to describe the procedures that operate |
on these XDR data-types in a formal language as well. We use the RPC |
Language for this purpose. It is an extension to the XDR language. |
The following example is used to describe the essence of the |
language. |
.NH 2 |
\&An Example Service Described in the RPC Language |
.LP |
Here is an example of the specification of a simple ping program. |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* Simple ping program |
*/ |
.ft CW |
program PING_PROG { |
/* \fILatest and greatest version\fP */ |
version PING_VERS_PINGBACK { |
void |
PINGPROC_NULL(void) = 0; |
|
.ft I |
/* |
* Ping the caller, return the round-trip time |
* (in microseconds). Returns -1 if the operation |
* timed out. |
*/ |
.ft CW |
int |
PINGPROC_PINGBACK(void) = 1; |
} = 2; |
|
.ft I |
/* |
* Original version |
*/ |
.ft CW |
version PING_VERS_ORIG { |
void |
PINGPROC_NULL(void) = 0; |
} = 1; |
} = 1; |
|
const PING_VERS = 2; /* \fIlatest version \fP*/ |
.vs |
.DE |
.KE |
.LP |
The first version described is |
.I PING_VERS_PINGBACK |
with two procedures, |
.I PINGPROC_NULL |
and |
.I PINGPROC_PINGBACK . |
.I PINGPROC_NULL |
takes no arguments and returns no results, but it is useful for |
computing round-trip times from the client to the server and back |
again. By convention, procedure 0 of any RPC protocol should have |
the same semantics, and never require any kind of authentication. |
The second procedure is used for the client to have the server do a |
reverse ping operation back to the client, and it returns the amount |
of time (in microseconds) that the operation used. The next version, |
.I PING_VERS_ORIG , |
is the original version of the protocol |
and it does not contain |
.I PINGPROC_PINGBACK |
procedure. It is useful |
for compatibility with old client programs, and as this program |
matures it may be dropped from the protocol entirely. |
.KS |
.NH 2 |
\&The RPC Language Specification |
.LP |
The RPC language is identical to the XDR language, except for the |
added definition of a |
.I program-def |
described below. |
.DS |
.ft CW |
program-def: |
"program" identifier "{" |
version-def |
version-def * |
"}" "=" constant ";" |
|
version-def: |
"version" identifier "{" |
procedure-def |
procedure-def * |
"}" "=" constant ";" |
|
procedure-def: |
type-specifier identifier "(" type-specifier ")" |
"=" constant ";" |
.DE |
.KE |
.NH 2 |
\&Syntax Notes |
.IP 1. |
The following keywords are added and cannot be used as |
identifiers: "program" and "version"; |
.IP 2. |
A version name cannot occur more than once within the scope of |
a program definition. Nor can a version number occur more than once |
within the scope of a program definition. |
.IP 3. |
A procedure name cannot occur more than once within the scope |
of a version definition. Nor can a procedure number occur more than |
once within the scope of version definition. |
.IP 4. |
Program identifiers are in the same name space as constant and |
type identifiers. |
.IP 5. |
Only unsigned constants can be assigned to programs, versions |
and procedures. |
.NH 1 |
\&Port Mapper Program Protocol |
.LP |
The port mapper program maps RPC program and version numbers to |
transport-specific port numbers. This program makes dynamic binding |
of remote programs possible. |
.LP |
This is desirable because the range of reserved port numbers is very |
small and the number of potential remote programs is very large. By |
running only the port mapper on a reserved port, the port numbers of |
other remote programs can be ascertained by querying the port mapper. |
.LP |
The port mapper also aids in broadcast RPC. A given RPC program will |
usually have different port number bindings on different machines, so |
there is no way to directly broadcast to all of these programs. The |
port mapper, however, does have a fixed port number. So, to |
broadcast to a given program, the client actually sends its message |
to the port mapper located at the broadcast address. Each port |
mapper that picks up the broadcast then calls the local service |
specified by the client. When the port mapper gets the reply from |
the local service, it sends the reply on back to the client. |
.KS |
.NH 2 |
\&Port Mapper Protocol Specification (in RPC Language) |
.ie t .DS |
.el .DS L |
.ft CW |
.vs 11 |
const PMAP_PORT = 111; /* \fIportmapper port number \fP*/ |
|
.ft I |
/* |
* A mapping of (program, version, protocol) to port number |
*/ |
.ft CW |
struct mapping { |
unsigned int prog; |
unsigned int vers; |
unsigned int prot; |
unsigned int port; |
}; |
|
.ft I |
/* |
* Supported values for the "prot" field |
*/ |
.ft CW |
const IPPROTO_TCP = 6; /* \fIprotocol number for TCP/IP \fP*/ |
const IPPROTO_UDP = 17; /* \fIprotocol number for UDP/IP \fP*/ |
|
.ft I |
/* |
* A list of mappings |
*/ |
.ft CW |
struct *pmaplist { |
mapping map; |
pmaplist next; |
}; |
.vs |
.DE |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* Arguments to callit |
*/ |
.ft CW |
struct call_args { |
unsigned int prog; |
unsigned int vers; |
unsigned int proc; |
opaque args<>; |
}; |
|
.ft I |
/* |
* Results of callit |
*/ |
.ft CW |
struct call_result { |
unsigned int port; |
opaque res<>; |
}; |
.vs |
.DE |
.KE |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* Port mapper procedures |
*/ |
.ft CW |
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; |
.vs |
.DE |
.NH 2 |
\&Port Mapper Operation |
.LP |
The portmapper program currently supports two protocols (UDP/IP and |
TCP/IP). The portmapper is contacted by talking to it on assigned |
port number 111 (SUNRPC [8]) on either of these protocols. The |
following is a description of each of the portmapper procedures: |
.IP \fBPMAPPROC_NULL:\fP |
This procedure does no work. By convention, procedure zero of any |
protocol takes no parameters and returns no results. |
.IP \fBPMAPPROC_SET:\fP |
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 request. The procedure returns a boolean response whose |
value is |
.I TRUE |
if the procedure successfully established the mapping and |
.I FALSE |
otherwise. The procedure refuses to establish |
a mapping if one already exists for the tuple "(prog, vers, prot)". |
.IP \fBPMAPPROC_UNSET:\fP |
When a program becomes unavailable, it should unregister itself with |
the port mapper program on the same machine. The parameters and |
results have meanings identical to those of |
.I PMAPPROC_SET . |
The protocol and port number fields of the argument are ignored. |
.IP \fBPMAPPROC_GETPORT:\fP |
Given a program number "prog", version number "vers", and transport |
protocol number "prot", this procedure returns the port number on |
which the program is awaiting call requests. A port value of zeros |
means the program has not been registered. The "port" field of the |
argument is ignored. |
.IP \fBPMAPPROC_DUMP:\fP |
This procedure enumerates all entries in the port mapper's database. |
The procedure takes no parameters and returns a list of program, |
version, protocol, and port values. |
.IP \fBPMAPPROC_CALLIT:\fP |
This procedure allows a caller to call another remote procedure on |
the same machine without knowing the remote procedure's port number. |
It is intended for supporting broadcasts to arbitrary remote programs |
via 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. |
.LP |
.B Note: |
.RS |
.IP 1. |
This procedure only sends a response if the procedure was |
successfully executed and is silent (no response) otherwise. |
.IP 2. |
The port mapper communicates with the remote program using UDP/IP |
only. |
.RE |
.LP |
The procedure returns the remote program's port number, and the bytes |
of results are the results of the remote procedure. |
.bp |
.NH 1 |
\&References |
.LP |
[1] Birrell, Andrew D. & Nelson, Bruce Jay; "Implementing Remote |
Procedure Calls"; XEROX CSL-83-7, October 1983. |
.LP |
[2] Cheriton, D.; "VMTP: Versatile Message Transaction Protocol", |
Preliminary Version 0.3; Stanford University, January 1987. |
.LP |
[3] Diffie & Hellman; "New Directions in Cryptography"; IEEE |
Transactions on Information Theory IT-22, November 1976. |
.LP |
[4] Harrenstien, K.; "Time Server", RFC 738; Information Sciences |
Institute, October 1977. |
.LP |
[5] National Bureau of Standards; "Data Encryption Standard"; Federal |
Information Processing Standards Publication 46, January 1977. |
.LP |
[6] Postel, J.; "Transmission Control Protocol - DARPA Internet |
Program Protocol Specification", RFC 793; Information Sciences |
Institute, September 1981. |
.LP |
[7] Postel, J.; "User Datagram Protocol", RFC 768; Information Sciences |
Institute, August 1980. |
.LP |
[8] Reynolds, J. & Postel, J.; "Assigned Numbers", RFC 923; Information |
Sciences Institute, October 1984. |
/PSD.doc/rpcgen.ms
0,0 → 1,1299
.\" |
.\" Must use -- tbl -- for this one |
.\" |
.\" @(#)rpcgen.ms 2.2 88/08/04 4.0 RPCSRC |
.de BT |
.if \\n%=1 .tl ''- % -'' |
.. |
.ND |
.\" prevent excess underlining in nroff |
.if n .fp 2 R |
.OH '\fBrpcgen\fP Programming Guide''Page %' |
.EH 'Page %''\fBrpcgen\fP Programming Guide' |
.if \\n%=1 .bp |
.SH |
\&\fBrpcgen\fP Programming Guide |
.NH 0 |
\&The \fBrpcgen\fP Protocol Compiler |
.IX rpcgen "" \fIrpcgen\fP "" PAGE MAJOR |
.LP |
.IX RPC "" "" \fIrpcgen\fP |
The details of programming applications to use Remote Procedure Calls |
can be overwhelming. Perhaps most daunting is the writing of the XDR |
routines necessary to convert procedure arguments and results into |
their network format and vice-versa. |
.LP |
Fortunately, |
.I rpcgen(1) |
exists to help programmers write RPC applications simply and directly. |
.I rpcgen |
does most of the dirty work, allowing programmers to debug |
the main features of their application, instead of requiring them to |
spend most of their time debugging their network interface code. |
.LP |
.I rpcgen |
is a compiler. It accepts a remote program interface definition written |
in a language, called RPC Language, which is similar to C. It produces a C |
language output which includes stub versions of the client routines, a |
server skeleton, XDR filter routines for both parameters and results, and a |
header file that contains common definitions. The client stubs interface |
with the RPC library and effectively hide the network from their callers. |
The server stub similarly hides the network from the server procedures that |
are to be invoked by remote clients. |
.I rpcgen 's |
output files can be compiled and linked in the usual way. The developer |
writes server procedures\(emin any language that observes Sun calling |
conventions\(emand links them with the server skeleton produced by |
.I rpcgen |
to get an executable server program. To use a remote program, a programmer |
writes an ordinary main program that makes local procedure calls to the |
client stubs produced by |
.I rpcgen . |
Linking this program with |
.I rpcgen 's |
stubs creates an executable program. (At present the main program must be |
written in C). |
.I rpcgen |
options can be used to suppress stub generation and to specify the transport |
to be used by the server stub. |
.LP |
Like all compilers, |
.I rpcgen |
reduces development time |
that would otherwise be spent coding and debugging low-level routines. |
All compilers, including |
.I rpcgen , |
do this at a small cost in efficiency |
and flexibility. However, many compilers allow escape hatches for |
programmers to mix low-level code with high-level code. |
.I rpcgen |
is no exception. In speed-critical applications, hand-written routines |
can be linked with the |
.I rpcgen |
output without any difficulty. Also, one may proceed by using |
.I rpcgen |
output as a starting point, and then rewriting it as necessary. |
(If you need a discussion of RPC programming without |
.I rpcgen , |
see the |
.I "Remote Procedure Call Programming Guide)\. |
.NH 1 |
\&Converting Local Procedures into Remote Procedures |
.IX rpcgen "local procedures" \fIrpcgen\fP |
.IX rpcgen "remote procedures" \fIrpcgen\fP |
.LP |
Assume an application that runs on a single machine, one which we want |
to convert to run over the network. Here we will demonstrate such a |
conversion by way of a simple example\(ema program that prints a |
message to the console: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* printmsg.c: print a message on the console |
*/ |
.ft CW |
#include <stdio.h> |
|
main(argc, argv) |
int argc; |
char *argv[]; |
{ |
char *message; |
|
if (argc < 2) { |
fprintf(stderr, "usage: %s <message>\en", argv[0]); |
exit(1); |
} |
message = argv[1]; |
|
if (!printmessage(message)) { |
fprintf(stderr, "%s: couldn't print your message\en", |
argv[0]); |
exit(1); |
} |
printf("Message Delivered!\en"); |
exit(0); |
} |
.ft I |
/* |
* Print a message to the console. |
* Return a boolean indicating whether the message was actually printed. |
*/ |
.ft CW |
printmessage(msg) |
char *msg; |
{ |
FILE *f; |
|
f = fopen("/dev/console", "w"); |
if (f == NULL) { |
return (0); |
} |
fprintf(f, "%s\en", msg); |
fclose(f); |
return(1); |
} |
.DE |
.LP |
And then, of course: |
.ie t .DS |
.el .DS L |
.ft CW |
example% \fBcc printmsg.c -o printmsg\fP |
example% \fBprintmsg "Hello, there."\fP |
Message delivered! |
example% |
.DE |
.LP |
If |
.I printmessage() |
was turned into a remote procedure, |
then it could be called from anywhere in the network. |
Ideally, one would just like to stick a keyword like |
.I remote |
in front of a |
procedure to turn it into a remote procedure. Unfortunately, |
we have to live within the constraints of the C language, since |
it existed long before RPC did. But even without language |
support, it's not very difficult to make a procedure remote. |
.LP |
In general, it's necessary to figure out what the types are for |
all procedure inputs and outputs. In this case, we have a |
procedure |
.I printmessage() |
which takes a string as input, and returns an integer |
as output. Knowing this, we can write a protocol specification in RPC |
language that describes the remote version of |
.I printmessage (). |
Here it is: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* msg.x: Remote message printing protocol |
*/ |
.ft CW |
|
program MESSAGEPROG { |
version MESSAGEVERS { |
int PRINTMESSAGE(string) = 1; |
} = 1; |
} = 99; |
.DE |
.LP |
Remote procedures are part of remote programs, so we actually declared |
an entire remote program here which contains the single procedure |
.I PRINTMESSAGE . |
This procedure was declared to be in version 1 of the |
remote program. No null procedure (procedure 0) is necessary because |
.I rpcgen |
generates it automatically. |
.LP |
Notice that everything is declared with all capital letters. This is |
not required, but is a good convention to follow. |
.LP |
Notice also that the argument type is \*Qstring\*U and not \*Qchar *\*U. This |
is because a \*Qchar *\*U in C is ambiguous. Programmers usually intend it |
to mean a null-terminated string of characters, but it could also |
represent a pointer to a single character or a pointer to an array of |
characters. In RPC language, a null-terminated string is |
unambiguously called a \*Qstring\*U. |
.LP |
There are just two more things to write. First, there is the remote |
procedure itself. Here's the definition of a remote procedure |
to implement the |
.I PRINTMESSAGE |
procedure we declared above: |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* msg_proc.c: implementation of the remote procedure "printmessage" |
*/ |
.ft CW |
|
#include <stdio.h> |
#include <rpc/rpc.h> /* \fIalways needed\fP */ |
#include "msg.h" /* \fIneed this too: msg.h will be generated by rpcgen\fP */ |
|
.ft I |
/* |
* Remote verson of "printmessage" |
*/ |
.ft CW |
int * |
printmessage_1(msg) |
char **msg; |
{ |
static int result; /* \fImust be static!\fP */ |
FILE *f; |
|
f = fopen("/dev/console", "w"); |
if (f == NULL) { |
result = 0; |
return (&result); |
} |
fprintf(f, "%s\en", *msg); |
fclose(f); |
result = 1; |
return (&result); |
} |
.vs |
.DE |
.LP |
Notice here that the declaration of the remote procedure |
.I printmessage_1() |
differs from that of the local procedure |
.I printmessage() |
in three ways: |
.IP 1. |
It takes a pointer to a string instead of a string itself. This |
is true of all remote procedures: they always take pointers to their |
arguments rather than the arguments themselves. |
.IP 2. |
It returns a pointer to an integer instead of an integer itself. This is |
also generally true of remote procedures: they always return a pointer |
to their results. |
.IP 3. |
It has an \*Q_1\*U appended to its name. In general, all remote |
procedures called by |
.I rpcgen |
are named by the following rule: the name in the program definition |
(here |
.I PRINTMESSAGE ) |
is converted to all |
lower-case letters, an underbar (\*Q_\*U) is appended to it, and |
finally the version number (here 1) is appended. |
.LP |
The last thing to do is declare the main client program that will call |
the remote procedure. Here it is: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* rprintmsg.c: remote version of "printmsg.c" |
*/ |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> /* \fIalways needed\fP */ |
#include "msg.h" /* \fIneed this too: msg.h will be generated by rpcgen\fP */ |
|
main(argc, argv) |
int argc; |
char *argv[]; |
{ |
CLIENT *cl; |
int *result; |
char *server; |
char *message; |
|
if (argc < 3) { |
fprintf(stderr, "usage: %s host message\en", argv[0]); |
exit(1); |
} |
|
.ft I |
/* |
* Save values of command line arguments |
*/ |
.ft CW |
server = argv[1]; |
message = argv[2]; |
|
.ft I |
/* |
* Create client "handle" used for calling \fIMESSAGEPROG\fP on the |
* server designated on the command line. We tell the RPC package |
* to use the "tcp" protocol when contacting the server. |
*/ |
.ft CW |
cl = clnt_create(server, MESSAGEPROG, MESSAGEVERS, "tcp"); |
if (cl == NULL) { |
.ft I |
/* |
* Couldn't establish connection with server. |
* Print error message and die. |
*/ |
.ft CW |
clnt_pcreateerror(server); |
exit(1); |
} |
|
.ft I |
/* |
* Call the remote procedure "printmessage" on the server |
*/ |
.ft CW |
result = printmessage_1(&message, cl); |
if (result == NULL) { |
.ft I |
/* |
* An error occurred while calling the server. |
* Print error message and die. |
*/ |
.ft CW |
clnt_perror(cl, server); |
exit(1); |
} |
|
.ft I |
/* |
* Okay, we successfully called the remote procedure. |
*/ |
.ft CW |
if (*result == 0) { |
.ft I |
/* |
* Server was unable to print our message. |
* Print error message and die. |
*/ |
.ft CW |
fprintf(stderr, "%s: %s couldn't print your message\en", |
argv[0], server); |
exit(1); |
} |
|
.ft I |
/* |
* The message got printed on the server's console |
*/ |
.ft CW |
printf("Message delivered to %s!\en", server); |
} |
.DE |
There are two things to note here: |
.IP 1. |
.IX "client handle, used by rpcgen" "" "client handle, used by \fIrpcgen\fP" |
First a client \*Qhandle\*U is created using the RPC library routine |
.I clnt_create (). |
This client handle will be passed to the stub routines |
which call the remote procedure. |
.IP 2. |
The remote procedure |
.I printmessage_1() |
is called exactly the same way as it is declared in |
.I msg_proc.c |
except for the inserted client handle as the first argument. |
.LP |
Here's how to put all of the pieces together: |
.ie t .DS |
.el .DS L |
.ft CW |
example% \fBrpcgen msg.x\fP |
example% \fBcc rprintmsg.c msg_clnt.c -o rprintmsg\fP |
example% \fBcc msg_proc.c msg_svc.c -o msg_server\fP |
.DE |
Two programs were compiled here: the client program |
.I rprintmsg |
and the server program |
.I msg_server . |
Before doing this though, |
.I rpcgen |
was used to fill in the missing pieces. |
.LP |
Here is what |
.I rpcgen |
did with the input file |
.I msg.x : |
.IP 1. |
It created a header file called |
.I msg.h |
that contained |
.I #define 's |
for |
.I MESSAGEPROG , |
.I MESSAGEVERS |
and |
.I PRINTMESSAGE |
for use in the other modules. |
.IP 2. |
It created client \*Qstub\*U routines in the |
.I msg_clnt.c |
file. In this case there is only one, the |
.I printmessage_1() |
that was referred to from the |
.I printmsg |
client program. The name of the output file for |
client stub routines is always formed in this way: if the name of the |
input file is |
.I FOO.x , |
the client stubs output file is called |
.I FOO_clnt.c . |
.IP 3. |
It created the server program which calls |
.I printmessage_1() |
in |
.I msg_proc.c . |
This server program is named |
.I msg_svc.c . |
The rule for naming the server output file is similar to the |
previous one: for an input file called |
.I FOO.x , |
the output server file is named |
.I FOO_svc.c . |
.LP |
Now we're ready to have some fun. First, copy the server to a |
remote machine and run it. For this example, the |
machine is called \*Qmoon\*U. Server processes are run in the |
background, because they never exit. |
.ie t .DS |
.el .DS L |
.ft CW |
moon% \fBmsg_server &\fP |
.DE |
Then on our local machine (\*Qsun\*U) we can print a message on \*Qmoon\*Us |
console. |
.ie t .DS |
.el .DS L |
.ft CW |
sun% \fBprintmsg moon "Hello, moon."\fP |
.DE |
The message will get printed to \*Qmoon\*Us console. You can print a |
message on anybody's console (including your own) with this program if |
you are able to copy the server to their machine and run it. |
.NH 1 |
\&Generating XDR Routines |
.IX RPC "generating XDR routines" |
.LP |
The previous example only demonstrated the automatic generation of |
client and server RPC code. |
.I rpcgen |
may also be used to generate XDR routines, that is, the routines |
necessary to convert local data |
structures into network format and vice-versa. This example presents |
a complete RPC service\(ema remote directory listing service, which uses |
.I rpcgen |
not only to generate stub routines, but also to generate the XDR |
routines. Here is the protocol description file: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* dir.x: Remote directory listing protocol |
*/ |
.ft CW |
const MAXNAMELEN = 255; /* \fImaximum length of a directory entry\fP */ |
|
typedef string nametype<MAXNAMELEN>; /* \fIa directory entry\fP */ |
|
typedef struct namenode *namelist; /* \fIa link in the listing\fP */ |
|
.ft I |
/* |
* A node in the directory listing |
*/ |
.ft CW |
struct namenode { |
nametype name; /* \fIname of directory entry\fP */ |
namelist next; /* \fInext entry\fP */ |
}; |
|
.ft I |
/* |
* The result of a READDIR operation. |
*/ |
.ft CW |
union readdir_res switch (int errno) { |
case 0: |
namelist list; /* \fIno error: return directory listing\fP */ |
default: |
void; /* \fIerror occurred: nothing else to return\fP */ |
}; |
|
.ft I |
/* |
* The directory program definition |
*/ |
.ft CW |
program DIRPROG { |
version DIRVERS { |
readdir_res |
READDIR(nametype) = 1; |
} = 1; |
} = 76; |
.DE |
.SH |
Note: |
.I |
Types (like |
.I readdir_res |
in the example above) can be defined using |
the \*Qstruct\*U, \*Qunion\*U and \*Qenum\*U keywords, but those keywords |
should not be used in subsequent declarations of variables of those types. |
For example, if you define a union \*Qfoo\*U, you should declare using |
only \*Qfoo\*U and not \*Qunion foo\*U. In fact, |
.I rpcgen |
compiles |
RPC unions into C structures and it is an error to declare them using the |
\*Qunion\*U keyword. |
.LP |
Running |
.I rpcgen |
on |
.I dir.x |
creates four output files. Three are the same as before: header file, |
client stub routines and server skeleton. The fourth are the XDR routines |
necessary for converting the data types we declared into XDR format and |
vice-versa. These are output in the file |
.I dir_xdr.c . |
.LP |
Here is the implementation of the |
.I READDIR |
procedure. |
.ie t .DS |
.el .DS L |
.vs 11 |
.ft I |
/* |
* dir_proc.c: remote readdir implementation |
*/ |
.ft CW |
#include <rpc/rpc.h> |
#include <sys/dir.h> |
#include "dir.h" |
|
extern int errno; |
extern char *malloc(); |
extern char *strdup(); |
|
readdir_res * |
readdir_1(dirname) |
nametype *dirname; |
{ |
DIR *dirp; |
struct direct *d; |
namelist nl; |
namelist *nlp; |
static readdir_res res; /* \fImust be static\fP! */ |
|
.ft I |
/* |
* Open directory |
*/ |
.ft CW |
dirp = opendir(*dirname); |
if (dirp == NULL) { |
res.errno = errno; |
return (&res); |
} |
|
.ft I |
/* |
* Free previous result |
*/ |
.ft CW |
xdr_free(xdr_readdir_res, &res); |
|
.ft I |
/* |
* Collect directory entries. |
* Memory allocated here will be freed by \fIxdr_free\fP |
* next time \fIreaddir_1\fP is called |
*/ |
.ft CW |
nlp = &res.readdir_res_u.list; |
while (d = readdir(dirp)) { |
nl = *nlp = (namenode *) malloc(sizeof(namenode)); |
nl->name = strdup(d->d_name); |
nlp = &nl->next; |
} |
*nlp = NULL; |
|
.ft I |
/* |
* Return the result |
*/ |
.ft CW |
res.errno = 0; |
closedir(dirp); |
return (&res); |
} |
.vs |
.DE |
Finally, there is the client side program to call the server: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* rls.c: Remote directory listing client |
*/ |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> /* \fIalways need this\fP */ |
#include "dir.h" /* \fIwill be generated by rpcgen\fI */ |
|
extern int errno; |
|
main(argc, argv) |
int argc; |
char *argv[]; |
{ |
CLIENT *cl; |
char *server; |
char *dir; |
readdir_res *result; |
namelist nl; |
|
|
if (argc != 3) { |
fprintf(stderr, "usage: %s host directory\en", |
argv[0]); |
exit(1); |
} |
|
.ft I |
/* |
* Remember what our command line arguments refer to |
*/ |
.ft CW |
server = argv[1]; |
dir = argv[2]; |
|
.ft I |
/* |
* Create client "handle" used for calling \fIMESSAGEPROG\fP on the |
* server designated on the command line. We tell the RPC package |
* to use the "tcp" protocol when contacting the server. |
*/ |
.ft CW |
cl = clnt_create(server, DIRPROG, DIRVERS, "tcp"); |
if (cl == NULL) { |
.ft I |
/* |
* Couldn't establish connection with server. |
* Print error message and die. |
*/ |
.ft CW |
clnt_pcreateerror(server); |
exit(1); |
} |
|
.ft I |
/* |
* Call the remote procedure \fIreaddir\fP on the server |
*/ |
.ft CW |
result = readdir_1(&dir, cl); |
if (result == NULL) { |
.ft I |
/* |
* An error occurred while calling the server. |
* Print error message and die. |
*/ |
.ft CW |
clnt_perror(cl, server); |
exit(1); |
} |
|
.ft I |
/* |
* Okay, we successfully called the remote procedure. |
*/ |
.ft CW |
if (result->errno != 0) { |
.ft I |
/* |
* A remote system error occurred. |
* Print error message and die. |
*/ |
.ft CW |
errno = result->errno; |
perror(dir); |
exit(1); |
} |
|
.ft I |
/* |
* Successfully got a directory listing. |
* Print it out. |
*/ |
.ft CW |
for (nl = result->readdir_res_u.list; nl != NULL; |
nl = nl->next) { |
printf("%s\en", nl->name); |
} |
exit(0); |
} |
.DE |
Compile everything, and run. |
.DS |
.ft CW |
sun% \fBrpcgen dir.x\fP |
sun% \fBcc rls.c dir_clnt.c dir_xdr.c -o rls\fP |
sun% \fBcc dir_svc.c dir_proc.c dir_xdr.c -o dir_svc\fP |
|
sun% \fBdir_svc &\fP |
|
moon% \fBrls sun /usr/pub\fP |
\&. |
\&.. |
ascii |
eqnchar |
greek |
kbd |
marg8 |
tabclr |
tabs |
tabs4 |
moon% |
.DE |
.LP |
.IX "debugging with rpcgen" "" "debugging with \fIrpcgen\fP" |
A final note about |
.I rpcgen : |
The client program and the server procedure can be tested together |
as a single program by simply linking them with each other rather |
than with the client and server stubs. The procedure calls will be |
executed as ordinary local procedure calls and the program can be |
debugged with a local debugger such as |
.I dbx . |
When the program is working, the client program can be linked to |
the client stub produced by |
.I rpcgen |
and the server procedures can be linked to the server stub produced |
by |
.I rpcgen . |
.SH |
.I NOTE : |
\fIIf you do this, you may want to comment out calls to RPC library |
routines, and have client-side routines call server routines |
directly.\fP |
.LP |
.NH 1 |
\&The C-Preprocessor |
.IX rpcgen "C-preprocessor" \fIrpcgen\fP |
.LP |
The C-preprocessor is run on all input files before they are |
compiled, so all the preprocessor directives are legal within a \*Q.x\*U |
file. Four symbols may be defined, depending upon which output file is |
getting generated. The symbols are: |
.TS |
box tab (&); |
lfI lfI |
lfL l . |
Symbol&Usage |
_ |
RPC_HDR&for header-file output |
RPC_XDR&for XDR routine output |
RPC_SVC&for server-skeleton output |
RPC_CLNT&for client stub output |
.TE |
.LP |
Also, |
.I rpcgen |
does a little preprocessing of its own. Any line that |
begins with a percent sign is passed directly into the output file, |
without any interpretation of the line. Here is a simple example that |
demonstrates the preprocessing features. |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* time.x: Remote time protocol |
*/ |
.ft CW |
program TIMEPROG { |
version TIMEVERS { |
unsigned int TIMEGET(void) = 1; |
} = 1; |
} = 44; |
|
#ifdef RPC_SVC |
%int * |
%timeget_1() |
%{ |
% static int thetime; |
% |
% thetime = time(0); |
% return (&thetime); |
%} |
#endif |
.DE |
The '%' feature is not generally recommended, as there is no guarantee |
that the compiler will stick the output where you intended. |
.NH 1 |
\&\fBrpcgen\fP Programming Notes |
.IX rpcgen "other operations" \fIrpcgen\fP |
.sp |
.NH 2 |
\&Timeout Changes |
.IX rpcgen "timeout changes" \fIrpcgen\fP |
.LP |
RPC sets a default timeout of 25 seconds for RPC calls when |
.I clnt_create() |
is used. This timeout may be changed using |
.I clnt_control() |
Here is a small code fragment to demonstrate use of |
.I clnt_control (): |
.ID |
struct timeval tv; |
CLIENT *cl; |
.sp .5 |
cl = clnt_create("somehost", SOMEPROG, SOMEVERS, "tcp"); |
if (cl == NULL) { |
exit(1); |
} |
tv.tv_sec = 60; /* \fIchange timeout to 1 minute\fP */ |
tv.tv_usec = 0; |
clnt_control(cl, CLSET_TIMEOUT, &tv); |
.DE |
.NH 2 |
\&Handling Broadcast on the Server Side |
.IX "broadcast RPC" |
.IX rpcgen "broadcast RPC" \fIrpcgen\fP |
.LP |
When a procedure is known to be called via broadcast RPC, |
it is usually wise for the server to not reply unless it can provide |
some useful information to the client. This prevents the network |
from getting flooded by useless replies. |
.LP |
To prevent the server from replying, a remote procedure can |
return NULL as its result, and the server code generated by |
.I rpcgen |
will detect this and not send out a reply. |
.LP |
Here is an example of a procedure that replies only if it |
thinks it is an NFS server: |
.ID |
void * |
reply_if_nfsserver() |
{ |
char notnull; /* \fIjust here so we can use its address\fP */ |
.sp .5 |
if (access("/etc/exports", F_OK) < 0) { |
return (NULL); /* \fIprevent RPC from replying\fP */ |
} |
.ft I |
/* |
* return non-null pointer so RPC will send out a reply |
*/ |
.ft L |
return ((void *)¬null); |
} |
.DE |
Note that if procedure returns type \*Qvoid *\*U, they must return a non-NULL |
pointer if they want RPC to reply for them. |
.NH 2 |
\&Other Information Passed to Server Procedures |
.LP |
Server procedures will often want to know more about an RPC call |
than just its arguments. For example, getting authentication information |
is important to procedures that want to implement some level of security. |
This extra information is actually supplied to the server procedure as a |
second argument. Here is an example to demonstrate its use. What we've |
done here is rewrite the previous |
.I printmessage_1() |
procedure to only allow root users to print a message to the console. |
.ID |
int * |
printmessage_1(msg, rq) |
char **msg; |
struct svc_req *rq; |
{ |
static in result; /* \fIMust be static\fP */ |
FILE *f; |
struct suthunix_parms *aup; |
.sp .5 |
aup = (struct authunix_parms *)rq->rq_clntcred; |
if (aup->aup_uid != 0) { |
result = 0; |
return (&result); |
} |
.sp |
.ft I |
/* |
* Same code as before. |
*/ |
.ft L |
} |
.DE |
.NH 1 |
\&RPC Language |
.IX RPCL |
.IX rpcgen "RPC Language" \fIrpcgen\fP |
.LP |
RPC language is an extension of XDR language. The sole extension is |
the addition of the |
.I program |
type. For a complete description of the XDR language syntax, see the |
.I "External Data Representation Standard: Protocol Specification" |
chapter. For a description of the RPC extensions to the XDR language, |
see the |
.I "Remote Procedure Calls: Protocol Specification" |
chapter. |
.LP |
However, XDR language is so close to C that if you know C, you know most |
of it already. We describe here the syntax of the RPC language, |
showing a few examples along the way. We also show how the various |
RPC and XDR type definitions get compiled into C type definitions in |
the output header file. |
.KS |
.NH 2 |
Definitions |
\& |
.IX rpcgen definitions \fIrpcgen\fP |
.LP |
An RPC language file consists of a series of definitions. |
.DS L |
.ft CW |
definition-list: |
definition ";" |
definition ";" definition-list |
.DE |
.KE |
It recognizes five types of definitions. |
.DS L |
.ft CW |
definition: |
enum-definition |
struct-definition |
union-definition |
typedef-definition |
const-definition |
program-definition |
.DE |
.NH 2 |
Structures |
\& |
.IX rpcgen structures \fIrpcgen\fP |
.LP |
An XDR struct is declared almost exactly like its C counterpart. It |
looks like the following: |
.DS L |
.ft CW |
struct-definition: |
"struct" struct-ident "{" |
declaration-list |
"}" |
|
declaration-list: |
declaration ";" |
declaration ";" declaration-list |
.DE |
As an example, here is an XDR structure to a two-dimensional |
coordinate, and the C structure that it gets compiled into in the |
output header file. |
.DS |
.ft CW |
struct coord { struct coord { |
int x; --> int x; |
int y; int y; |
}; }; |
typedef struct coord coord; |
.DE |
The output is identical to the input, except for the added |
.I typedef |
at the end of the output. This allows one to use \*Qcoord\*U instead of |
\*Qstruct coord\*U when declaring items. |
.NH 2 |
Unions |
\& |
.IX rpcgen unions \fIrpcgen\fP |
.LP |
XDR unions are discriminated unions, and look quite different from C |
unions. They are more analogous to Pascal variant records than they |
are to C unions. |
.DS L |
.ft CW |
union-definition: |
"union" union-ident "switch" "(" declaration ")" "{" |
case-list |
"}" |
|
case-list: |
"case" value ":" declaration ";" |
"default" ":" declaration ";" |
"case" value ":" declaration ";" case-list |
.DE |
Here is an example of a type that might be returned as the result of a |
\*Qread data\*U operation. If there is no error, return a block of data. |
Otherwise, don't return anything. |
.DS L |
.ft CW |
union read_result switch (int errno) { |
case 0: |
opaque data[1024]; |
default: |
void; |
}; |
.DE |
It gets compiled into the following: |
.DS L |
.ft CW |
struct read_result { |
int errno; |
union { |
char data[1024]; |
} read_result_u; |
}; |
typedef struct read_result read_result; |
.DE |
Notice that the union component of the output struct has the name as |
the type name, except for the trailing \*Q_u\*U. |
.NH 2 |
Enumerations |
\& |
.IX rpcgen enumerations \fIrpcgen\fP |
.LP |
XDR enumerations have the same syntax as C enumerations. |
.DS L |
.ft CW |
enum-definition: |
"enum" enum-ident "{" |
enum-value-list |
"}" |
|
enum-value-list: |
enum-value |
enum-value "," enum-value-list |
|
enum-value: |
enum-value-ident |
enum-value-ident "=" value |
.DE |
Here is a short example of an XDR enum, and the C enum that it gets |
compiled into. |
.DS L |
.ft CW |
enum colortype { enum colortype { |
RED = 0, RED = 0, |
GREEN = 1, --> GREEN = 1, |
BLUE = 2 BLUE = 2, |
}; }; |
typedef enum colortype colortype; |
.DE |
.NH 2 |
Typedef |
\& |
.IX rpcgen typedef \fIrpcgen\fP |
.LP |
XDR typedefs have the same syntax as C typedefs. |
.DS L |
.ft CW |
typedef-definition: |
"typedef" declaration |
.DE |
Here is an example that defines a |
.I fname_type |
used for declaring |
file name strings that have a maximum length of 255 characters. |
.DS L |
.ft CW |
typedef string fname_type<255>; --> typedef char *fname_type; |
.DE |
.NH 2 |
Constants |
\& |
.IX rpcgen constants \fIrpcgen\fP |
.LP |
XDR constants symbolic constants that may be used wherever a |
integer constant is used, for example, in array size specifications. |
.DS L |
.ft CW |
const-definition: |
"const" const-ident "=" integer |
.DE |
For example, the following defines a constant |
.I DOZEN |
equal to 12. |
.DS L |
.ft CW |
const DOZEN = 12; --> #define DOZEN 12 |
.DE |
.NH 2 |
Programs |
\& |
.IX rpcgen programs \fIrpcgen\fP |
.LP |
RPC programs are declared using the following syntax: |
.DS L |
.ft CW |
program-definition: |
"program" program-ident "{" |
version-list |
"}" "=" value |
|
version-list: |
version ";" |
version ";" version-list |
|
version: |
"version" version-ident "{" |
procedure-list |
"}" "=" value |
|
procedure-list: |
procedure ";" |
procedure ";" procedure-list |
|
procedure: |
type-ident procedure-ident "(" type-ident ")" "=" value |
.DE |
For example, here is the time protocol, revisited: |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* time.x: Get or set the time. Time is represented as number of seconds |
* since 0:00, January 1, 1970. |
*/ |
.ft CW |
program TIMEPROG { |
version TIMEVERS { |
unsigned int TIMEGET(void) = 1; |
void TIMESET(unsigned) = 2; |
} = 1; |
} = 44; |
.DE |
This file compiles into #defines in the output header file: |
.ie t .DS |
.el .DS L |
.ft CW |
#define TIMEPROG 44 |
#define TIMEVERS 1 |
#define TIMEGET 1 |
#define TIMESET 2 |
.DE |
.NH 2 |
Declarations |
\& |
.IX rpcgen declarations \fIrpcgen\fP |
.LP |
In XDR, there are only four kinds of declarations. |
.DS L |
.ft CW |
declaration: |
simple-declaration |
fixed-array-declaration |
variable-array-declaration |
pointer-declaration |
.DE |
\fB1) Simple declarations\fP are just like simple C declarations. |
.DS L |
.ft CW |
simple-declaration: |
type-ident variable-ident |
.DE |
Example: |
.DS L |
.ft CW |
colortype color; --> colortype color; |
.DE |
\fB2) Fixed-length Array Declarations\fP are just like C array declarations: |
.DS L |
.ft CW |
fixed-array-declaration: |
type-ident variable-ident "[" value "]" |
.DE |
Example: |
.DS L |
.ft CW |
colortype palette[8]; --> colortype palette[8]; |
.DE |
\fB3) Variable-Length Array Declarations\fP have no explicit syntax |
in C, so XDR invents its own using angle-brackets. |
.DS L |
.ft CW |
variable-array-declaration: |
type-ident variable-ident "<" value ">" |
type-ident variable-ident "<" ">" |
.DE |
The maximum size is specified between the angle brackets. The size may |
be omitted, indicating that the array may be of any size. |
.DS L |
.ft CW |
int heights<12>; /* \fIat most 12 items\fP */ |
int widths<>; /* \fIany number of items\fP */ |
.DE |
Since variable-length arrays have no explicit syntax in C, these |
declarations are actually compiled into \*Qstruct\*Us. For example, the |
\*Qheights\*U declaration gets compiled into the following struct: |
.DS L |
.ft CW |
struct { |
u_int heights_len; /* \fI# of items in array\fP */ |
int *heights_val; /* \fIpointer to array\fP */ |
} heights; |
.DE |
Note that the number of items in the array is stored in the \*Q_len\*U |
component and the pointer to the array is stored in the \*Q_val\*U |
component. The first part of each of these component's names is the |
same as the name of the declared XDR variable. |
.LP |
\fB4) Pointer Declarations\fP are made in |
XDR exactly as they are in C. You can't |
really send pointers over the network, but you can use XDR pointers |
for sending recursive data types such as lists and trees. The type is |
actually called \*Qoptional-data\*U, not \*Qpointer\*U, in XDR language. |
.DS L |
.ft CW |
pointer-declaration: |
type-ident "*" variable-ident |
.DE |
Example: |
.DS L |
.ft CW |
listitem *next; --> listitem *next; |
.DE |
.NH 2 |
\&Special Cases |
.IX rpcgen "special cases" \fIrpcgen\fP |
.LP |
There are a few exceptions to the rules described above. |
.LP |
.B Booleans: |
C has no built-in boolean type. However, the RPC library does a |
boolean type called |
.I bool_t |
that is either |
.I TRUE |
or |
.I FALSE . |
Things declared as type |
.I bool |
in XDR language are compiled into |
.I bool_t |
in the output header file. |
.LP |
Example: |
.DS L |
.ft CW |
bool married; --> bool_t married; |
.DE |
.B Strings: |
C has no built-in string type, but instead uses the null-terminated |
\*Qchar *\*U convention. In XDR language, strings are declared using the |
\*Qstring\*U keyword, and compiled into \*Qchar *\*Us in the output header |
file. The maximum size contained in the angle brackets specifies the |
maximum number of characters allowed in the strings (not counting the |
.I NULL |
character). The maximum size may be left off, indicating a string |
of arbitrary length. |
.LP |
Examples: |
.DS L |
.ft CW |
string name<32>; --> char *name; |
string longname<>; --> char *longname; |
.DE |
.B "Opaque Data:" |
Opaque data is used in RPC and XDR to describe untyped data, that is, |
just sequences of arbitrary bytes. It may be declared either as a |
fixed or variable length array. |
.DS L |
Examples: |
.ft CW |
opaque diskblock[512]; --> char diskblock[512]; |
|
opaque filedata<1024>; --> struct { |
u_int filedata_len; |
char *filedata_val; |
} filedata; |
.DE |
.B Voids: |
In a void declaration, the variable is not named. The declaration is |
just \*Qvoid\*U and nothing else. Void declarations can only occur in two |
places: union definitions and program definitions (as the argument or |
result of a remote procedure). |
/PSD.doc/nfs.rfc.ms
0,0 → 1,1372
.\" |
.\" Must use -- tbl -- with this one |
.\" |
.\" @(#)nfs.rfc.ms 2.2 88/08/05 4.0 RPCSRC |
.de BT |
.if \\n%=1 .tl ''- % -'' |
.. |
.ND |
.\" prevent excess underlining in nroff |
.if n .fp 2 R |
.OH 'Network File System: Version 2 Protocol Specification''Page %' |
.EH 'Page %''Network File System: Version 2 Protocol Specification' |
.if \\n%=1 .bp |
.SH |
\&Network File System: Version 2 Protocol Specification |
.IX NFS "" "" "" PAGE MAJOR |
.IX "Network File System" "" "" "" PAGE MAJOR |
.IX NFS "version-2 protocol specification" |
.IX "Network File System" "version-2 protocol specification" |
.LP |
.NH 0 |
\&Status of this Standard |
.LP |
Note: This document specifies a protocol that Sun Microsystems, Inc., |
and others are using. It specifies it in standard ARPA RFC form. |
.NH 1 |
\&Introduction |
.IX NFS introduction |
.LP |
The Sun Network Filesystem (NFS) protocol provides transparent remote |
access to shared filesystems over local area networks. The NFS |
protocol is designed to be machine, operating system, network architecture, |
and transport protocol independent. This independence is |
achieved through the use of Remote Procedure Call (RPC) primitives |
built on top of an External Data Representation (XDR). Implementations |
exist for a variety of machines, from personal computers to |
supercomputers. |
.LP |
The supporting mount protocol allows the server to hand out remote |
access privileges to a restricted set of clients. It performs the |
operating system-specific functions that allow, for example, to |
attach remote directory trees to some local file system. |
.NH 2 |
\&Remote Procedure Call |
.IX "Remote Procedure Call" |
.LP |
Sun's remote procedure call specification provides a procedure- |
oriented interface to remote services. Each server supplies a |
program that is a set of procedures. NFS is one such "program". |
The combination of host address, program number, and procedure |
number specifies one remote service procedure. RPC does not depend |
on services provided by specific protocols, so it can be used with |
any underlying transport protocol. See the |
.I "Remote Procedure Calls: Protocol Specification" |
chapter of this manual. |
.NH 2 |
\&External Data Representation |
.IX "External Data Representation" |
.LP |
The External Data Representation (XDR) standard provides a common |
way of representing a set of data types over a network. |
The NFS |
Protocol Specification is written using the RPC data description |
language. |
For more information, see the |
.I " External Data Representation Standard: Protocol Specification." |
Sun provides implementations of XDR and |
RPC, but NFS does not require their use. Any software that |
provides equivalent functionality can be used, and if the encoding |
is exactly the same it can interoperate with other implementations |
of NFS. |
.NH 2 |
\&Stateless Servers |
.IX "stateless servers" |
.IX servers stateless |
.LP |
The NFS protocol is stateless. That is, a server does not need to |
maintain any extra state information about any of its clients in |
order to function correctly. Stateless servers have a distinct |
advantage over stateful servers in the event of a failure. With |
stateless servers, a client need only retry a request until the |
server responds; it does not even need to know that the server has |
crashed, or the network temporarily went down. The client of a |
stateful server, on the other hand, needs to either detect a server |
crash and rebuild the server's state when it comes back up, or |
cause client operations to fail. |
.LP |
This may not sound like an important issue, but it affects the |
protocol in some unexpected ways. We feel that it is worth a bit |
of extra complexity in the protocol to be able to write very simple |
servers that do not require fancy crash recovery. |
.LP |
On the other hand, NFS deals with objects such as files and |
directories that inherently have state -- what good would a file be |
if it did not keep its contents intact? The goal is to not |
introduce any extra state in the protocol itself. Another way to |
simplify recovery is by making operations "idempotent" whenever |
possible (so that they can potentially be repeated). |
.NH 1 |
\&NFS Protocol Definition |
.IX NFS "protocol definition" |
.IX NFS protocol |
.LP |
Servers have been known to change over time, and so can the |
protocol that they use. So RPC provides a version number with each |
RPC request. This RFC describes version two of the NFS protocol. |
Even in the second version, there are various obsolete procedures |
and parameters, which will be removed in later versions. An RFC |
for version three of the NFS protocol is currently under |
preparation. |
.NH 2 |
\&File System Model |
.IX filesystem model |
.LP |
NFS assumes a file system that is hierarchical, with directories as |
all but the bottom-level files. Each entry in a directory (file, |
directory, device, etc.) has a string name. Different operating |
systems may have restrictions on the depth of the tree or the names |
used, as well as using different syntax to represent the "pathname", |
which is the concatenation of all the "components" (directory and |
file names) in the name. A "file system" is a tree on a single |
server (usually a single disk or physical partition) with a specified |
"root". Some operating systems provide a "mount" operation to make |
all file systems appear as a single tree, while others maintain a |
"forest" of file systems. Files are unstructured streams of |
uninterpreted bytes. Version 3 of NFS uses a slightly more general |
file system model. |
.LP |
NFS looks up one component of a pathname at a time. It may not be |
obvious why it does not just take the whole pathname, traipse down |
the directories, and return a file handle when it is done. There are |
several good reasons not to do this. First, pathnames need |
separators between the directory components, and different operating |
systems use different separators. We could define a Network Standard |
Pathname Representation, but then every pathname would have to be |
parsed and converted at each end. Other issues are discussed in |
\fINFS Implementation Issues\fP below. |
.LP |
Although files and directories are similar objects in many ways, |
different procedures are used to read directories and files. This |
provides a network standard format for representing directories. The |
same argument as above could have been used to justify a procedure |
that returns only one directory entry per call. The problem is |
efficiency. Directories can contain many entries, and a remote call |
to return each would be just too slow. |
.NH 2 |
\&RPC Information |
.IX NFS "RPC information" |
.IP \fIAuthentication\fP |
The NFS service uses |
.I AUTH_UNIX , |
.I AUTH_DES , |
or |
.I AUTH_SHORT |
style |
authentication, except in the NULL procedure where |
.I AUTH_NONE |
is also allowed. |
.IP "\fITransport Protocols\fP" |
NFS currently is supported on UDP/IP only. |
.IP "\fIPort Number\fP" |
The NFS protocol currently uses the UDP port number 2049. This is |
not an officially assigned port, so later versions of the protocol |
use the \*QPortmapping\*U facility of RPC. |
.NH 2 |
\&Sizes of XDR Structures |
.IX "XDR structure sizes" |
.LP |
These are the sizes, given in decimal bytes, of various XDR |
structures used in the protocol: |
.DS |
/* \fIThe maximum number of bytes of data in a READ or WRITE request\fP */ |
const MAXDATA = 8192; |
|
/* \fIThe maximum number of bytes in a pathname argument\fP */ |
const MAXPATHLEN = 1024; |
|
/* \fIThe maximum number of bytes in a file name argument\fP */ |
const MAXNAMLEN = 255; |
|
/* \fIThe size in bytes of the opaque "cookie" passed by READDIR\fP */ |
const COOKIESIZE = 4; |
|
/* \fIThe size in bytes of the opaque file handle\fP */ |
const FHSIZE = 32; |
.DE |
.NH 2 |
\&Basic Data Types |
.IX "NFS data types" |
.IX NFS "basic data types" |
.LP |
The following XDR definitions are basic structures and types used |
in other structures described further on. |
.KS |
.NH 3 |
\&stat |
.IX "NFS data types" stat "" \fIstat\fP |
.DS |
enum stat { |
NFS_OK = 0, |
NFSERR_PERM=1, |
NFSERR_NOENT=2, |
NFSERR_IO=5, |
NFSERR_NXIO=6, |
NFSERR_ACCES=13, |
NFSERR_EXIST=17, |
NFSERR_NODEV=19, |
NFSERR_NOTDIR=20, |
NFSERR_ISDIR=21, |
NFSERR_FBIG=27, |
NFSERR_NOSPC=28, |
NFSERR_ROFS=30, |
NFSERR_NAMETOOLONG=63, |
NFSERR_NOTEMPTY=66, |
NFSERR_DQUOT=69, |
NFSERR_STALE=70, |
NFSERR_WFLUSH=99 |
}; |
.DE |
.KE |
.LP |
The |
.I stat |
type is returned with every procedure's results. A |
value of |
.I NFS_OK |
indicates that the call completed successfully and |
the results are valid. The other values indicate some kind of |
error occurred on the server side during the servicing of the |
procedure. The error values are derived from UNIX error numbers. |
.IP \fBNFSERR_PERM\fP: |
Not owner. The caller does not have correct ownership |
to perform the requested operation. |
.IP \fBNFSERR_NOENT\fP: |
No such file or directory. The file or directory |
specified does not exist. |
.IP \fBNFSERR_IO\fP: |
Some sort of hard error occurred when the operation was |
in progress. This could be a disk error, for example. |
.IP \fBNFSERR_NXIO\fP: |
No such device or address. |
.IP \fBNFSERR_ACCES\fP: |
Permission denied. The caller does not have the |
correct permission to perform the requested operation. |
.IP \fBNFSERR_EXIST\fP: |
File exists. The file specified already exists. |
.IP \fBNFSERR_NODEV\fP: |
No such device. |
.IP \fBNFSERR_NOTDIR\fP: |
Not a directory. The caller specified a |
non-directory in a directory operation. |
.IP \fBNFSERR_ISDIR\fP: |
Is a directory. The caller specified a directory in |
a non- directory operation. |
.IP \fBNFSERR_FBIG\fP: |
File too large. The operation caused a file to grow |
beyond the server's limit. |
.IP \fBNFSERR_NOSPC\fP: |
No space left on device. The operation caused the |
server's filesystem to reach its limit. |
.IP \fBNFSERR_ROFS\fP: |
Read-only filesystem. Write attempted on a read-only filesystem. |
.IP \fBNFSERR_NAMETOOLONG\fP: |
File name too long. The file name in an operation was too long. |
.IP \fBNFSERR_NOTEMPTY\fP: |
Directory not empty. Attempted to remove a |
directory that was not empty. |
.IP \fBNFSERR_DQUOT\fP: |
Disk quota exceeded. The client's disk quota on the |
server has been exceeded. |
.IP \fBNFSERR_STALE\fP: |
The "fhandle" given in the arguments was invalid. |
That is, the file referred to by that file handle no longer exists, |
or access to it has been revoked. |
.IP \fBNFSERR_WFLUSH\fP: |
The server's write cache used in the |
.I WRITECACHE |
call got flushed to disk. |
.LP |
.KS |
.NH 3 |
\&ftype |
.IX "NFS data types" ftype "" \fIftype\fP |
.DS |
enum ftype { |
NFNON = 0, |
NFREG = 1, |
NFDIR = 2, |
NFBLK = 3, |
NFCHR = 4, |
NFLNK = 5 |
}; |
.DE |
.KE |
The enumeration |
.I ftype |
gives the type of a file. The type |
.I NFNON |
indicates a non-file, |
.I NFREG |
is a regular file, |
.I NFDIR |
is a directory, |
.I NFBLK |
is a block-special device, |
.I NFCHR |
is a character-special device, and |
.I NFLNK |
is a symbolic link. |
.KS |
.NH 3 |
\&fhandle |
.IX "NFS data types" fhandle "" \fIfhandle\fP |
.DS |
typedef opaque fhandle[FHSIZE]; |
.DE |
.KE |
The |
.I fhandle |
is the file handle passed between the server and the client. |
All file operations are done using file handles to refer to a file or |
directory. The file handle can contain whatever information the server |
needs to distinguish an individual file. |
.KS |
.NH 3 |
\&timeval |
.IX "NFS data types" timeval "" \fItimeval\fP |
.DS |
struct timeval { |
unsigned int seconds; |
unsigned int useconds; |
}; |
.DE |
.KE |
The |
.I timeval |
structure is the number of seconds and microseconds |
since midnight January 1, 1970, Greenwich Mean Time. It is used to |
pass time and date information. |
.KS |
.NH 3 |
\&fattr |
.IX "NFS data types" fattr "" \fIfattr\fP |
.DS |
struct fattr { |
ftype type; |
unsigned int mode; |
unsigned int nlink; |
unsigned int uid; |
unsigned int gid; |
unsigned int size; |
unsigned int blocksize; |
unsigned int rdev; |
unsigned int blocks; |
unsigned int fsid; |
unsigned int fileid; |
timeval atime; |
timeval mtime; |
timeval ctime; |
}; |
.DE |
.KE |
The |
.I fattr |
structure contains the attributes of a file; "type" is the type of |
the file; "nlink" is the number of hard links to the file (the number |
of different names for the same file); "uid" is the user |
identification number of the owner of the file; "gid" is the group |
identification number of the group of the file; "size" is the size in |
bytes of the file; "blocksize" is the size in bytes of a block of the |
file; "rdev" is the device number of the file if it is type |
.I NFCHR |
or |
.I NFBLK ; |
"blocks" is the number of blocks the file takes up on disk; "fsid" is |
the file system identifier for the filesystem containing the file; |
"fileid" is a number that uniquely identifies the file within its |
filesystem; "atime" is the time when the file was last accessed for |
either read or write; "mtime" is the time when the file data was last |
modified (written); and "ctime" is the time when the status of the |
file was last changed. Writing to the file also changes "ctime" if |
the size of the file changes. |
.LP |
"mode" is the access mode encoded as a set of bits. Notice that the |
file type is specified both in the mode bits and in the file type. |
This is really a bug in the protocol and will be fixed in future |
versions. The descriptions given below specify the bit positions |
using octal numbers. |
.TS |
box tab (&) ; |
cfI cfI |
lfL l . |
Bit&Description |
_ |
0040000&This is a directory; "type" field should be NFDIR. |
0020000&This is a character special file; "type" field should be NFCHR. |
0060000&This is a block special file; "type" field should be NFBLK. |
0100000&This is a regular file; "type" field should be NFREG. |
0120000&This is a symbolic link file; "type" field should be NFLNK. |
0140000&This is a named socket; "type" field should be NFNON. |
0004000&Set user id on execution. |
0002000&Set group id on execution. |
0001000&Save swapped text even after use. |
0000400&Read permission for owner. |
0000200&Write permission for owner. |
0000100&Execute and search permission for owner. |
0000040&Read permission for group. |
0000020&Write permission for group. |
0000010&Execute and search permission for group. |
0000004&Read permission for others. |
0000002&Write permission for others. |
0000001&Execute and search permission for others. |
.TE |
.KS |
Notes: |
.IP |
The bits are the same as the mode bits returned by the |
.I stat(2) |
system call in the UNIX system. The file type is specified both in |
the mode bits and in the file type. This is fixed in future |
versions. |
.IP |
The "rdev" field in the attributes structure is an operating system |
specific device specifier. It will be removed and generalized in |
the next revision of the protocol. |
.KE |
.LP |
.KS |
.NH 3 |
\&sattr |
.IX "NFS data types" sattr "" \fIsattr\fP |
.DS |
struct sattr { |
unsigned int mode; |
unsigned int uid; |
unsigned int gid; |
unsigned int size; |
timeval atime; |
timeval mtime; |
}; |
.DE |
.KE |
The |
.I sattr |
structure contains the file attributes which can be set |
from the client. The fields are the same as for |
.I fattr |
above. A "size" of zero means the file should be truncated. |
A value of -1 indicates a field that should be ignored. |
.LP |
.KS |
.NH 3 |
\&filename |
.IX "NFS data types" filename "" \fIfilename\fP |
.DS |
typedef string filename<MAXNAMLEN>; |
.DE |
.KE |
The type |
.I filename |
is used for passing file names or pathname components. |
.LP |
.KS |
.NH 3 |
\&path |
.IX "NFS data types" path "" \fIpath\fP |
.DS |
typedef string path<MAXPATHLEN>; |
.DE |
.KE |
The type |
.I path |
is a pathname. The server considers it as a string |
with no internal structure, but to the client it is the name of a |
node in a filesystem tree. |
.LP |
.KS |
.NH 3 |
\&attrstat |
.IX "NFS data types" attrstat "" \fIattrstat\fP |
.DS |
union attrstat switch (stat status) { |
case NFS_OK: |
fattr attributes; |
default: |
void; |
}; |
.DE |
.KE |
The |
.I attrstat |
structure is a common procedure result. It contains |
a "status" and, if the call succeeded, it also contains the |
attributes of the file on which the operation was done. |
.LP |
.KS |
.NH 3 |
\&diropargs |
.IX "NFS data types" diropargs "" \fIdiropargs\fP |
.DS |
struct diropargs { |
fhandle dir; |
filename name; |
}; |
.DE |
.KE |
The |
.I diropargs |
structure is used in directory operations. The |
"fhandle" "dir" is the directory in which to find the file "name". |
A directory operation is one in which the directory is affected. |
.LP |
.KS |
.NH 3 |
\&diropres |
.IX "NFS data types" diropres "" \fIdiropres\fP |
.DS |
union diropres switch (stat status) { |
case NFS_OK: |
struct { |
fhandle file; |
fattr attributes; |
} diropok; |
default: |
void; |
}; |
.DE |
.KE |
The results of a directory operation are returned in a |
.I diropres |
structure. If the call succeeded, a new file handle "file" and the |
"attributes" associated with that file are returned along with the |
"status". |
.NH 2 |
\&Server Procedures |
.IX "NFS server procedures" "" "" "" PAGE MAJOR |
.LP |
The protocol definition is given as a set of procedures with |
arguments and results defined using the RPC language. A brief |
description of the function of each procedure should provide enough |
information to allow implementation. |
.LP |
All of the procedures in the NFS protocol are assumed to be |
synchronous. When a procedure returns to the client, the client |
can assume that the operation has completed and any data associated |
with the request is now on stable storage. For example, a client |
.I WRITE |
request may cause the server to update data blocks, |
filesystem information blocks (such as indirect blocks), and file |
attribute information (size and modify times). When the |
.I WRITE |
returns to the client, it can assume that the write is safe, even |
in case of a server crash, and it can discard the data written. |
This is a very important part of the statelessness of the server. |
If the server waited to flush data from remote requests, the client |
would have to save those requests so that it could resend them in |
case of a server crash. |
.ie t .DS |
.el .DS L |
|
.ft I |
/* |
* Remote file service routines |
*/ |
.ft CW |
program NFS_PROGRAM { |
version NFS_VERSION { |
void NFSPROC_NULL(void) = 0; |
attrstat NFSPROC_GETATTR(fhandle) = 1; |
attrstat NFSPROC_SETATTR(sattrargs) = 2; |
void NFSPROC_ROOT(void) = 3; |
diropres NFSPROC_LOOKUP(diropargs) = 4; |
readlinkres NFSPROC_READLINK(fhandle) = 5; |
readres NFSPROC_READ(readargs) = 6; |
void NFSPROC_WRITECACHE(void) = 7; |
attrstat NFSPROC_WRITE(writeargs) = 8; |
diropres NFSPROC_CREATE(createargs) = 9; |
stat NFSPROC_REMOVE(diropargs) = 10; |
stat NFSPROC_RENAME(renameargs) = 11; |
stat NFSPROC_LINK(linkargs) = 12; |
stat NFSPROC_SYMLINK(symlinkargs) = 13; |
diropres NFSPROC_MKDIR(createargs) = 14; |
stat NFSPROC_RMDIR(diropargs) = 15; |
readdirres NFSPROC_READDIR(readdirargs) = 16; |
statfsres NFSPROC_STATFS(fhandle) = 17; |
} = 2; |
} = 100003; |
.DE |
.KS |
.NH 3 |
\&Do Nothing |
.IX "NFS server procedures" NFSPROC_NULL() "" \fINFSPROC_NULL()\fP |
.DS |
void |
NFSPROC_NULL(void) = 0; |
.DE |
.KE |
This procedure does no work. It is made available in all RPC |
services to allow server response testing and timing. |
.KS |
.NH 3 |
\&Get File Attributes |
.IX "NFS server procedures" NFSPROC_GETATTR() "" \fINFSPROC_GETATTR()\fP |
.DS |
attrstat |
NFSPROC_GETATTR (fhandle) = 1; |
.DE |
.KE |
If the reply status is |
.I NFS_OK , |
then the reply attributes contains |
the attributes for the file given by the input fhandle. |
.KS |
.NH 3 |
\&Set File Attributes |
.IX "NFS server procedures" NFSPROC_SETATTR() "" \fINFSPROC_SETATTR()\fP |
.DS |
struct sattrargs { |
fhandle file; |
sattr attributes; |
}; |
|
attrstat |
NFSPROC_SETATTR (sattrargs) = 2; |
.DE |
.KE |
The "attributes" argument contains fields which are either -1 or |
are the new value for the attributes of "file". If the reply |
status is |
.I NFS_OK , |
then the reply attributes have the attributes of |
the file after the "SETATTR" operation has completed. |
.LP |
Note: The use of -1 to indicate an unused field in "attributes" is |
changed in the next version of the protocol. |
.KS |
.NH 3 |
\&Get Filesystem Root |
.IX "NFS server procedures" NFSPROC_ROOT "" \fINFSPROC_ROOT\fP |
.DS |
void |
NFSPROC_ROOT(void) = 3; |
.DE |
.KE |
Obsolete. This procedure is no longer used because finding the |
root file handle of a filesystem requires moving pathnames between |
client and server. To do this right we would have to define a |
network standard representation of pathnames. Instead, the |
function of looking up the root file handle is done by the |
.I MNTPROC_MNT() |
procedure. (See the |
.I "Mount Protocol Definition" |
later in this chapter for details). |
.KS |
.NH 3 |
\&Look Up File Name |
.IX "NFS server procedures" NFSPROC_LOOKUP() "" \fINFSPROC_LOOKUP()\fP |
.DS |
diropres |
NFSPROC_LOOKUP(diropargs) = 4; |
.DE |
.KE |
If the reply "status" is |
.I NFS_OK , |
then the reply "file" and reply |
"attributes" are the file handle and attributes for the file "name" |
in the directory given by "dir" in the argument. |
.KS |
.NH 3 |
\&Read From Symbolic Link |
.IX "NFS server procedures" NFSPROC_READLINK() "" \fINFSPROC_READLINK()\fP |
.DS |
union readlinkres switch (stat status) { |
case NFS_OK: |
path data; |
default: |
void; |
}; |
|
readlinkres |
NFSPROC_READLINK(fhandle) = 5; |
.DE |
.KE |
If "status" has the value |
.I NFS_OK , |
then the reply "data" is the data in |
the symbolic link given by the file referred to by the fhandle argument. |
.LP |
Note: since NFS always parses pathnames on the client, the |
pathname in a symbolic link may mean something different (or be |
meaningless) on a different client or on the server if a different |
pathname syntax is used. |
.KS |
.NH 3 |
\&Read From File |
.IX "NFS server procedures" NFSPROC_READ "" \fINFSPROC_READ\fP |
.DS |
struct readargs { |
fhandle file; |
unsigned offset; |
unsigned count; |
unsigned totalcount; |
}; |
|
union readres switch (stat status) { |
case NFS_OK: |
fattr attributes; |
opaque data<NFS_MAXDATA>; |
default: |
void; |
}; |
|
readres |
NFSPROC_READ(readargs) = 6; |
.DE |
.KE |
Returns up to "count" bytes of "data" from the file given by |
"file", starting at "offset" bytes from the beginning of the file. |
The first byte of the file is at offset zero. The file attributes |
after the read takes place are returned in "attributes". |
.LP |
Note: The argument "totalcount" is unused, and is removed in the |
next protocol revision. |
.KS |
.NH 3 |
\&Write to Cache |
.IX "NFS server procedures" NFSPROC_WRITECACHE() "" \fINFSPROC_WRITECACHE()\fP |
.DS |
void |
NFSPROC_WRITECACHE(void) = 7; |
.DE |
.KE |
To be used in the next protocol revision. |
.KS |
.NH 3 |
\&Write to File |
.IX "NFS server procedures" NFSPROC_WRITE() "" \fINFSPROC_WRITE()\fP |
.DS |
struct writeargs { |
fhandle file; |
unsigned beginoffset; |
unsigned offset; |
unsigned totalcount; |
opaque data<NFS_MAXDATA>; |
}; |
|
attrstat |
NFSPROC_WRITE(writeargs) = 8; |
.DE |
.KE |
Writes "data" beginning "offset" bytes from the beginning of |
"file". The first byte of the file is at offset zero. If the |
reply "status" is NFS_OK, then the reply "attributes" contains the |
attributes of the file after the write has completed. The write |
operation is atomic. Data from this call to |
.I WRITE |
will not be mixed with data from another client's calls. |
.LP |
Note: The arguments "beginoffset" and "totalcount" are ignored and |
are removed in the next protocol revision. |
.KS |
.NH 3 |
\&Create File |
.IX "NFS server procedures" NFSPROC_CREATE() "" \fINFSPROC_CREATE()\fP |
.DS |
struct createargs { |
diropargs where; |
sattr attributes; |
}; |
|
diropres |
NFSPROC_CREATE(createargs) = 9; |
.DE |
.KE |
The file "name" is created in the directory given by "dir". The |
initial attributes of the new file are given by "attributes". A |
reply "status" of NFS_OK indicates that the file was created, and |
reply "file" and reply "attributes" are its file handle and |
attributes. Any other reply "status" means that the operation |
failed and no file was created. |
.LP |
Note: This routine should pass an exclusive create flag, meaning |
"create the file only if it is not already there". |
.KS |
.NH 3 |
\&Remove File |
.IX "NFS server procedures" NFSPROC_REMOVE() "" \fINFSPROC_REMOVE()\fP |
.DS |
stat |
NFSPROC_REMOVE(diropargs) = 10; |
.DE |
.KE |
The file "name" is removed from the directory given by "dir". A |
reply of NFS_OK means the directory entry was removed. |
.LP |
Note: possibly non-idempotent operation. |
.KS |
.NH 3 |
\&Rename File |
.IX "NFS server procedures" NFSPROC_RENAME() "" \fINFSPROC_RENAME()\fP |
.DS |
struct renameargs { |
diropargs from; |
diropargs to; |
}; |
|
stat |
NFSPROC_RENAME(renameargs) = 11; |
.DE |
.KE |
The existing file "from.name" in the directory given by "from.dir" |
is renamed to "to.name" in the directory given by "to.dir". If the |
reply is |
.I NFS_OK , |
the file was renamed. The |
RENAME |
operation is |
atomic on the server; it cannot be interrupted in the middle. |
.LP |
Note: possibly non-idempotent operation. |
.KS |
.NH 3 |
\&Create Link to File |
.IX "NFS server procedures" NFSPROC_LINK() "" \fINFSPROC_LINK()\fP |
.DS |
struct linkargs { |
fhandle from; |
diropargs to; |
}; |
|
stat |
NFSPROC_LINK(linkargs) = 12; |
.DE |
.KE |
Creates the file "to.name" in the directory given by "to.dir", |
which is a hard link to the existing file given by "from". If the |
return value is |
.I NFS_OK , |
a link was created. Any other return value |
indicates an error, and the link was not created. |
.LP |
A hard link should have the property that changes to either of the |
linked files are reflected in both files. When a hard link is made |
to a file, the attributes for the file should have a value for |
"nlink" that is one greater than the value before the link. |
.LP |
Note: possibly non-idempotent operation. |
.KS |
.NH 3 |
\&Create Symbolic Link |
.IX "NFS server procedures" NFSPROC_SYMLINK() "" \fINFSPROC_SYMLINK()\fP |
.DS |
struct symlinkargs { |
diropargs from; |
path to; |
sattr attributes; |
}; |
|
stat |
NFSPROC_SYMLINK(symlinkargs) = 13; |
.DE |
.KE |
Creates the file "from.name" with ftype |
.I NFLNK |
in the directory |
given by "from.dir". The new file contains the pathname "to" and |
has initial attributes given by "attributes". If the return value |
is |
.I NFS_OK , |
a link was created. Any other return value indicates an |
error, and the link was not created. |
.LP |
A symbolic link is a pointer to another file. The name given in |
"to" is not interpreted by the server, only stored in the newly |
created file. When the client references a file that is a symbolic |
link, the contents of the symbolic link are normally transparently |
reinterpreted as a pathname to substitute. A |
.I READLINK |
operation returns the data to the client for interpretation. |
.LP |
Note: On UNIX servers the attributes are never used, since |
symbolic links always have mode 0777. |
.KS |
.NH 3 |
\&Create Directory |
.IX "NFS server procedures" NFSPROC_MKDIR() "" \fINFSPROC_MKDIR()\fP |
.DS |
diropres |
NFSPROC_MKDIR (createargs) = 14; |
.DE |
.KE |
The new directory "where.name" is created in the directory given by |
"where.dir". The initial attributes of the new directory are given |
by "attributes". A reply "status" of NFS_OK indicates that the new |
directory was created, and reply "file" and reply "attributes" are |
its file handle and attributes. Any other reply "status" means |
that the operation failed and no directory was created. |
.LP |
Note: possibly non-idempotent operation. |
.KS |
.NH 3 |
\&Remove Directory |
.IX "NFS server procedures" NFSPROC_RMDIR() "" \fINFSPROC_RMDIR()\fP |
.DS |
stat |
NFSPROC_RMDIR(diropargs) = 15; |
.DE |
.KE |
The existing empty directory "name" in the directory given by "dir" |
is removed. If the reply is |
.I NFS_OK , |
the directory was removed. |
.LP |
Note: possibly non-idempotent operation. |
.KS |
.NH 3 |
\&Read From Directory |
.IX "NFS server procedures" NFSPROC_READDIR() "" \fINFSPROC_READDIR()\fP |
.DS |
struct readdirargs { |
fhandle dir; |
nfscookie cookie; |
unsigned count; |
}; |
|
struct entry { |
unsigned fileid; |
filename name; |
nfscookie cookie; |
entry *nextentry; |
}; |
|
union readdirres switch (stat status) { |
case NFS_OK: |
struct { |
entry *entries; |
bool eof; |
} readdirok; |
default: |
void; |
}; |
|
readdirres |
NFSPROC_READDIR (readdirargs) = 16; |
.DE |
.KE |
Returns a variable number of directory entries, with a total size |
of up to "count" bytes, from the directory given by "dir". If the |
returned value of "status" is |
.I NFS_OK , |
then it is followed by a |
variable number of "entry"s. Each "entry" contains a "fileid" |
which consists of a unique number to identify the file within a |
filesystem, the "name" of the file, and a "cookie" which is an |
opaque pointer to the next entry in the directory. The cookie is |
used in the next |
.I READDIR |
call to get more entries starting at a |
given point in the directory. The special cookie zero (all bits |
zero) can be used to get the entries starting at the beginning of |
the directory. The "fileid" field should be the same number as the |
"fileid" in the the attributes of the file. (See the |
.I "Basic Data Types" |
section.) |
The "eof" flag has a value of |
.I TRUE |
if there are no more entries in the directory. |
.KS |
.NH 3 |
\&Get Filesystem Attributes |
.IX "NFS server procedures" NFSPROC_STATFS() "" \fINFSPROC_STATFS()\fP |
.DS |
union statfsres (stat status) { |
case NFS_OK: |
struct { |
unsigned tsize; |
unsigned bsize; |
unsigned blocks; |
unsigned bfree; |
unsigned bavail; |
} info; |
default: |
void; |
}; |
|
statfsres |
NFSPROC_STATFS(fhandle) = 17; |
.DE |
.KE |
If the reply "status" is |
.I NFS_OK , |
then the reply "info" gives the |
attributes for the filesystem that contains file referred to by the |
input fhandle. The attribute fields contain the following values: |
.IP tsize: |
The optimum transfer size of the server in bytes. This is |
the number of bytes the server would like to have in the |
data part of READ and WRITE requests. |
.IP bsize: |
The block size in bytes of the filesystem. |
.IP blocks: |
The total number of "bsize" blocks on the filesystem. |
.IP bfree: |
The number of free "bsize" blocks on the filesystem. |
.IP bavail: |
The number of "bsize" blocks available to non-privileged users. |
.LP |
Note: This call does not work well if a filesystem has variable |
size blocks. |
.NH 1 |
\&NFS Implementation Issues |
.IX NFS implementation |
.LP |
The NFS protocol is designed to be operating system independent, but |
since this version was designed in a UNIX environment, many |
operations have semantics similar to the operations of the UNIX file |
system. This section discusses some of the implementation-specific |
semantic issues. |
.NH 2 |
\&Server/Client Relationship |
.IX NFS "server/client relationship" |
.LP |
The NFS protocol is designed to allow servers to be as simple and |
general as possible. Sometimes the simplicity of the server can be a |
problem, if the client wants to implement complicated filesystem |
semantics. |
.LP |
For example, some operating systems allow removal of open files. A |
process can open a file and, while it is open, remove it from the |
directory. The file can be read and written as long as the process |
keeps it open, even though the file has no name in the filesystem. |
It is impossible for a stateless server to implement these semantics. |
The client can do some tricks such as renaming the file on remove, |
and only removing it on close. We believe that the server provides |
enough functionality to implement most file system semantics on the |
client. |
.LP |
Every NFS client can also potentially be a server, and remote and |
local mounted filesystems can be freely intermixed. This leads to |
some interesting problems when a client travels down the directory |
tree of a remote filesystem and reaches the mount point on the server |
for another remote filesystem. Allowing the server to follow the |
second remote mount would require loop detection, server lookup, and |
user revalidation. Instead, we decided not to let clients cross a |
server's mount point. When a client does a LOOKUP on a directory on |
which the server has mounted a filesystem, the client sees the |
underlying directory instead of the mounted directory. A client can |
do remote mounts that match the server's mount points to maintain the |
server's view. |
.LP |
.NH 2 |
\&Pathname Interpretation |
.IX NFS "pathname interpretation" |
.LP |
There are a few complications to the rule that pathnames are always |
parsed on the client. For example, symbolic links could have |
different interpretations on different clients. Another common |
problem for non-UNIX implementations is the special interpretation of |
the pathname ".." to mean the parent of a given directory. The next |
revision of the protocol uses an explicit flag to indicate the parent |
instead. |
.NH 2 |
\&Permission Issues |
.IX NFS "permission issues" |
.LP |
The NFS protocol, strictly speaking, does not define the permission |
checking used by servers. However, it is expected that a server |
will do normal operating system permission checking using |
.I AUTH_UNIX |
style authentication as the basis of its protection mechanism. The |
server gets the client's effective "uid", effective "gid", and groups |
on each call and uses them to check permission. There are various |
problems with this method that can been resolved in interesting ways. |
.LP |
Using "uid" and "gid" implies that the client and server share the |
same "uid" list. Every server and client pair must have the same |
mapping from user to "uid" and from group to "gid". Since every |
client can also be a server, this tends to imply that the whole |
network shares the same "uid/gid" space. |
.I AUTH_DES |
(and the next |
revision of the NFS protocol) uses string names instead of numbers, |
but there are still complex problems to be solved. |
.LP |
Another problem arises due to the usually stateful open operation. |
Most operating systems check permission at open time, and then check |
that the file is open on each read and write request. With stateless |
servers, the server has no idea that the file is open and must do |
permission checking on each read and write call. On a local |
filesystem, a user can open a file and then change the permissions so |
that no one is allowed to touch it, but will still be able to write |
to the file because it is open. On a remote filesystem, by contrast, |
the write would fail. To get around this problem, the server's |
permission checking algorithm should allow the owner of a file to |
access it regardless of the permission setting. |
.LP |
A similar problem has to do with paging in from a file over the |
network. The operating system usually checks for execute permission |
before opening a file for demand paging, and then reads blocks from |
the open file. The file may not have read permission, but after it |
is opened it doesn't matter. An NFS server can not tell the |
difference between a normal file read and a demand page-in read. To |
make this work, the server allows reading of files if the "uid" given |
in the call has execute or read permission on the file. |
.LP |
In most operating systems, a particular user (on the user ID zero) |
has access to all files no matter what permission and ownership they |
have. This "super-user" permission may not be allowed on the server, |
since anyone who can become super-user on their workstation could |
gain access to all remote files. The UNIX server by default maps |
user id 0 to -2 before doing its access checking. This works except |
for NFS root filesystems, where super-user access cannot be avoided. |
.NH 2 |
\&Setting RPC Parameters |
.IX NFS "setting RPC parameters" |
.LP |
Various file system parameters and options should be set at mount |
time. The mount protocol is described in the appendix below. For |
example, "Soft" mounts as well as "Hard" mounts are usually both |
provided. Soft mounted file systems return errors when RPC |
operations fail (after a given number of optional retransmissions), |
while hard mounted file systems continue to retransmit forever. |
Clients and servers may need to keep caches of recent operations to |
help avoid problems with non-idempotent operations. |
.NH 1 |
\&Mount Protocol Definition |
.IX "mount protocol" "" "" "" PAGE MAJOR |
.sp 1 |
.NH 2 |
\&Introduction |
.IX "mount protocol" introduction |
.LP |
The mount protocol is separate from, but related to, the NFS |
protocol. It provides operating system specific services to get the |
NFS off the ground -- looking up server path names, validating user |
identity, and checking access permissions. Clients use the mount |
protocol to get the first file handle, which allows them entry into a |
remote filesystem. |
.LP |
The mount protocol is kept separate from the NFS protocol to make it |
easy to plug in new access checking and validation methods without |
changing the NFS server protocol. |
.LP |
Notice that the protocol definition implies stateful servers because |
the server maintains a list of client's mount requests. The mount |
list information is not critical for the correct functioning of |
either the client or the server. It is intended for advisory use |
only, for example, to warn possible clients when a server is going |
down. |
.LP |
Version one of the mount protocol is used with version two of the NFS |
protocol. The only connecting point is the |
.I fhandle |
structure, which is the same for both protocols. |
.NH 2 |
\&RPC Information |
.IX "mount protocol" "RPC information" |
.IP \fIAuthentication\fP |
The mount service uses |
.I AUTH_UNIX |
and |
.I AUTH_DES |
style authentication only. |
.IP "\fITransport Protocols\fP" |
The mount service is currently supported on UDP/IP only. |
.IP "\fIPort Number\fP" |
Consult the server's portmapper, described in the chapter |
.I "Remote Procedure Calls: Protocol Specification", |
to find the port number on which the mount service is registered. |
.NH 2 |
\&Sizes of XDR Structures |
.IX "mount protocol" "XDR structure sizes" |
.LP |
These are the sizes, given in decimal bytes, of various XDR |
structures used in the protocol: |
.DS |
/* \fIThe maximum number of bytes in a pathname argument\fP */ |
const MNTPATHLEN = 1024; |
|
/* \fIThe maximum number of bytes in a name argument\fP */ |
const MNTNAMLEN = 255; |
|
/* \fIThe size in bytes of the opaque file handle\fP */ |
const FHSIZE = 32; |
.DE |
.NH 2 |
\&Basic Data Types |
.IX "mount protocol" "basic data types" |
.IX "mount data types" |
.LP |
This section presents the data types used by the mount protocol. |
In many cases they are similar to the types used in NFS. |
.KS |
.NH 3 |
\&fhandle |
.IX "mount data types" fhandle "" \fIfhandle\fP |
.DS |
typedef opaque fhandle[FHSIZE]; |
.DE |
.KE |
The type |
.I fhandle |
is the file handle that the server passes to the |
client. All file operations are done using file handles to refer |
to a file or directory. The file handle can contain whatever |
information the server needs to distinguish an individual file. |
.LP |
This is the same as the "fhandle" XDR definition in version 2 of |
the NFS protocol; see |
.I "Basic Data Types" |
in the definition of the NFS protocol, above. |
.KS |
.NH 3 |
\&fhstatus |
.IX "mount data types" fhstatus "" \fIfhstatus\fP |
.DS |
union fhstatus switch (unsigned status) { |
case 0: |
fhandle directory; |
default: |
void; |
}; |
.DE |
.KE |
The type |
.I fhstatus |
is a union. If a "status" of zero is returned, |
the call completed successfully, and a file handle for the |
"directory" follows. A non-zero status indicates some sort of |
error. In this case the status is a UNIX error number. |
.KS |
.NH 3 |
\&dirpath |
.IX "mount data types" dirpath "" \fIdirpath\fP |
.DS |
typedef string dirpath<MNTPATHLEN>; |
.DE |
.KE |
The type |
.I dirpath |
is a server pathname of a directory. |
.KS |
.NH 3 |
\&name |
.IX "mount data types" name "" \fIname\fP |
.DS |
typedef string name<MNTNAMLEN>; |
.DE |
.KE |
The type |
.I name |
is an arbitrary string used for various names. |
.NH 2 |
\&Server Procedures |
.IX "mount server procedures" |
.LP |
The following sections define the RPC procedures supplied by a |
mount server. |
.ie t .DS |
.el .DS L |
.ft I |
/* |
* Protocol description for the mount program |
*/ |
.ft CW |
|
program MOUNTPROG { |
.ft I |
/* |
* Version 1 of the mount protocol used with |
* version 2 of the NFS protocol. |
*/ |
.ft CW |
version MOUNTVERS { |
void MOUNTPROC_NULL(void) = 0; |
fhstatus MOUNTPROC_MNT(dirpath) = 1; |
mountlist MOUNTPROC_DUMP(void) = 2; |
void MOUNTPROC_UMNT(dirpath) = 3; |
void MOUNTPROC_UMNTALL(void) = 4; |
exportlist MOUNTPROC_EXPORT(void) = 5; |
} = 1; |
} = 100005; |
.DE |
.KS |
.NH 3 |
\&Do Nothing |
.IX "mount server procedures" MNTPROC_NULL() "" \fIMNTPROC_NULL()\fP |
.DS |
void |
MNTPROC_NULL(void) = 0; |
.DE |
.KE |
This procedure does no work. It is made available in all RPC |
services to allow server response testing and timing. |
.KS |
.NH 3 |
\&Add Mount Entry |
.IX "mount server procedures" MNTPROC_MNT() "" \fIMNTPROC_MNT()\fP |
.DS |
fhstatus |
MNTPROC_MNT(dirpath) = 1; |
.DE |
.KE |
If the reply "status" is 0, then the reply "directory" contains the |
file handle for the directory "dirname". This file handle may be |
used in the NFS protocol. This procedure also adds a new entry to |
the mount list for this client mounting "dirname". |
.KS |
.NH 3 |
\&Return Mount Entries |
.IX "mount server procedures" MNTPROC_DUMP() "" \fIMNTPROC_DUMP()\fP |
.DS |
struct *mountlist { |
name hostname; |
dirpath directory; |
mountlist nextentry; |
}; |
|
mountlist |
MNTPROC_DUMP(void) = 2; |
.DE |
.KE |
Returns the list of remote mounted filesystems. The "mountlist" |
contains one entry for each "hostname" and "directory" pair. |
.KS |
.NH 3 |
\&Remove Mount Entry |
.IX "mount server procedures" MNTPROC_UMNT() "" \fIMNTPROC_UMNT()\fP |
.DS |
void |
MNTPROC_UMNT(dirpath) = 3; |
.DE |
.KE |
Removes the mount list entry for the input "dirpath". |
.KS |
.NH 3 |
\&Remove All Mount Entries |
.IX "mount server procedures" MNTPROC_UMNTALL() "" \fIMNTPROC_UMNTALL()\fP |
.DS |
void |
MNTPROC_UMNTALL(void) = 4; |
.DE |
.KE |
Removes all of the mount list entries for this client. |
.KS |
.NH 3 |
\&Return Export List |
.IX "mount server procedures" MNTPROC_EXPORT() "" \fIMNTPROC_EXPORT()\fP |
.DS |
struct *groups { |
name grname; |
groups grnext; |
}; |
|
struct *exportlist { |
dirpath filesys; |
groups groups; |
exportlist next; |
}; |
|
exportlist |
MNTPROC_EXPORT(void) = 5; |
.DE |
.KE |
Returns a variable number of export list entries. Each entry |
contains a filesystem name and a list of groups that are allowed to |
import it. The filesystem name is in "filesys", and the group name |
is in the list "groups". |
.LP |
Note: The exportlist should contain |
more information about the status of the filesystem, such as a |
read-only flag. |
/PSD.doc/xdr.nts.ms
0,0 → 1,1966
.\" |
.\" Must use -- eqn -- with this one |
.\" |
.\" @(#)xdr.nts.ms 2.2 88/08/05 4.0 RPCSRC |
.EQ |
delim $$ |
.EN |
.de BT |
.if \\n%=1 .tl ''- % -'' |
.. |
.ND |
.\" prevent excess underlining in nroff |
.if n .fp 2 R |
.OH 'External Data Representation: Sun Technical Notes''Page %' |
.EH 'Page %''External Data Representation: Sun Technical Notes' |
.if \\n%=1 .bp |
.SH |
\&External Data Representation: Sun Technical Notes |
.IX XDR "Sun technical notes" |
.LP |
This chapter contains technical notes on Sun's implementation of the |
External Data Representation (XDR) standard, a set of library routines |
that allow a C programmer to describe arbitrary data structures in a |
machinex-independent fashion. |
For a formal specification of the XDR |
standard, see the |
.I "External Data Representation Standard: Protocol Specification". |
XDR is the backbone of Sun's Remote Procedure Call package, in the |
sense that data for remote procedure calls is transmitted using the |
standard. XDR library routines should be used to transmit data |
that is accessed (read or written) by more than one type of machine.\** |
.FS |
.IX XDR "system routines" |
For a compete specification of the system External Data Representation |
routines, see the |
.I xdr(3N) |
manual page. |
.FE |
.LP |
This chapter contains a short tutorial overview of the XDR library |
routines, a guide to accessing currently available XDR streams, and |
information on defining new streams and data types. XDR was designed |
to work across different languages, operating systems, and machine |
architectures. Most users (particularly RPC users) will only need |
the information in the |
.I "Number Filters", |
.I "Floating Point Filters", |
and |
.I "Enumeration Filters" |
sections. |
Programmers wishing to implement RPC and XDR on new machines |
will be interested in the rest of the chapter, as well as the |
.I "External Data Representaiton Standard: Protocol Specification", |
which will be their primary reference. |
.SH |
Note: |
.I |
.I rpcgen |
can be used to write XDR routines even in cases where no RPC calls are |
being made. |
.LP |
On Sun systems, |
C programs that want to use XDR routines |
must include the file |
.I <rpc/rpc.h> , |
which contains all the necessary interfaces to the XDR system. |
Since the C library |
.I libc.a |
contains all the XDR routines, |
compile as normal. |
.DS |
example% \fBcc\0\fIprogram\fP.c\fI |
.DE |
.ne 3i |
.NH 0 |
\&Justification |
.IX XDR justification |
.LP |
Consider the following two programs, |
.I writer : |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
.sp.5 |
main() /* \fIwriter.c\fP */ |
{ |
long i; |
.sp.5 |
for (i = 0; i < 8; i++) { |
if (fwrite((char *)&i, sizeof(i), 1, stdout) != 1) { |
fprintf(stderr, "failed!\en"); |
exit(1); |
} |
} |
exit(0); |
} |
.DE |
and |
.I reader : |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
.sp.5 |
main() /* \fIreader.c\fP */ |
{ |
long i, j; |
.sp.5 |
for (j = 0; j < 8; j++) { |
if (fread((char *)&i, sizeof (i), 1, stdin) != 1) { |
fprintf(stderr, "failed!\en"); |
exit(1); |
} |
printf("%ld ", i); |
} |
printf("\en"); |
exit(0); |
} |
.DE |
The two programs appear to be portable, because (a) they pass |
.I lint |
checking, and (b) they exhibit the same behavior when executed |
on two different hardware architectures, a Sun and a VAX. |
.LP |
Piping the output of the |
.I writer |
program to the |
.I reader |
program gives identical results on a Sun or a VAX. |
.DS |
.ft CW |
sun% \fBwriter | reader\fP |
0 1 2 3 4 5 6 7 |
sun% |
|
|
vax% \fBwriter | reader\fP |
0 1 2 3 4 5 6 7 |
vax% |
.DE |
With the advent of local area networks and 4.2BSD came the concept |
of \*Qnetwork pipes\*U \(em a process produces data on one machine, |
and a second process consumes data on another machine. |
A network pipe can be constructed with |
.I writer |
and |
.I reader . |
Here are the results if the first produces data on a Sun, |
and the second consumes data on a VAX. |
.DS |
.ft CW |
sun% \fBwriter | rsh vax reader\fP |
0 16777216 33554432 50331648 67108864 83886080 100663296 |
117440512 |
sun% |
.DE |
Identical results can be obtained by executing |
.I writer |
on the VAX and |
.I reader |
on the Sun. These results occur because the byte ordering |
of long integers differs between the VAX and the Sun, |
even though word size is the same. |
Note that $16777216$ is $2 sup 24$ \(em |
when four bytes are reversed, the 1 winds up in the 24th bit. |
.LP |
Whenever data is shared by two or more machine types, there is |
a need for portable data. Programs can be made data-portable by |
replacing the |
.I read() |
and |
.I write() |
calls with calls to an XDR library routine |
.I xdr_long() , |
a filter that knows the standard representation |
of a long integer in its external form. |
Here are the revised versions of |
.I writer : |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */ |
.sp.5 |
main() /* \fIwriter.c\fP */ |
{ |
XDR xdrs; |
long i; |
.sp.5 |
xdrstdio_create(&xdrs, stdout, XDR_ENCODE); |
for (i = 0; i < 8; i++) { |
if (!xdr_long(&xdrs, &i)) { |
fprintf(stderr, "failed!\en"); |
exit(1); |
} |
} |
exit(0); |
} |
.DE |
and |
.I reader : |
.ie t .DS |
.el .DS L |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */ |
.sp.5 |
main() /* \fIreader.c\fP */ |
{ |
XDR xdrs; |
long i, j; |
.sp.5 |
xdrstdio_create(&xdrs, stdin, XDR_DECODE); |
for (j = 0; j < 8; j++) { |
if (!xdr_long(&xdrs, &i)) { |
fprintf(stderr, "failed!\en"); |
exit(1); |
} |
printf("%ld ", i); |
} |
printf("\en"); |
exit(0); |
} |
.DE |
The new programs were executed on a Sun, |
on a VAX, and from a Sun to a VAX; |
the results are shown below. |
.DS |
.ft CW |
sun% \fBwriter | reader\fP |
0 1 2 3 4 5 6 7 |
sun% |
|
vax% \fBwriter | reader\fP |
0 1 2 3 4 5 6 7 |
vax% |
|
sun% \fBwriter | rsh vax reader\fP |
0 1 2 3 4 5 6 7 |
sun% |
.DE |
.SH |
Note: |
.I |
.IX XDR "portable data" |
Integers are just the tip of the portable-data iceberg. Arbitrary |
data structures present portability problems, particularly with |
respect to alignment and pointers. Alignment on word boundaries |
may cause the size of a structure to vary from machine to machine. |
And pointers, which are very convenient to use, have no meaning |
outside the machine where they are defined. |
.LP |
.NH 1 |
\&A Canonical Standard |
.IX XDR "canonical standard" |
.LP |
XDR's approach to standardizing data representations is |
.I canonical . |
That is, XDR defines a single byte order (Big Endian), a single |
floating-point representation (IEEE), and so on. Any program running on |
any machine can use XDR to create portable data by translating its |
local representation to the XDR standard representations; similarly, any |
program running on any machine can read portable data by translating the |
XDR standard representaions to its local equivalents. The single standard |
completely decouples programs that create or send portable data from those |
that use or receive portable data. The advent of a new machine or a new |
language has no effect upon the community of existing portable data creators |
and users. A new machine joins this community by being \*Qtaught\*U how to |
convert the standard representations and its local representations; the |
local representations of other machines are irrelevant. Conversely, to |
existing programs running on other machines, the local representations of |
the new machine are also irrelevant; such programs can immediately read |
portable data produced by the new machine because such data conforms to the |
canonical standards that they already understand. |
.LP |
There are strong precedents for XDR's canonical approach. For example, |
TCP/IP, UDP/IP, XNS, Ethernet, and, indeed, all protocols below layer five |
of the ISO model, are canonical protocols. The advantage of any canonical |
approach is simplicity; in the case of XDR, a single set of conversion |
routines is written once and is never touched again. The canonical approach |
has a disadvantage, but it is unimportant in real-world data transfer |
applications. Suppose two Little-Endian machines are transferring integers |
according to the XDR standard. The sending machine converts the integers |
from Little-Endian byte order to XDR (Big-Endian) byte order; the receiving |
machine performs the reverse conversion. Because both machines observe the |
same byte order, their conversions are unnecessary. The point, however, is |
not necessity, but cost as compared to the alternative. |
.LP |
The time spent converting to and from a canonical representation is |
insignificant, especially in networking applications. Most of the time |
required to prepare a data structure for transfer is not spent in conversion |
but in traversing the elements of the data structure. To transmit a tree, |
for example, each leaf must be visited and each element in a leaf record must |
be copied to a buffer and aligned there; storage for the leaf may have to be |
deallocated as well. Similarly, to receive a tree, storage must be |
allocated for each leaf, data must be moved from the buffer to the leaf and |
properly aligned, and pointers must be constructed to link the leaves |
together. Every machine pays the cost of traversing and copying data |
structures whether or not conversion is required. In networking |
applications, communications overhead\(emthe time required to move the data |
down through the sender's protocol layers, across the network and up through |
the receiver's protocol layers\(emdwarfs conversion overhead. |
.NH 1 |
\&The XDR Library |
.IX "XDR" "library" |
.LP |
The XDR library not only solves data portability problems, it also |
allows you to write and read arbitrary C constructs in a consistent, |
specified, well-documented manner. Thus, it can make sense to use the |
library even when the data is not shared among machines on a network. |
.LP |
The XDR library has filter routines for |
strings (null-terminated arrays of bytes), |
structures, unions, and arrays, to name a few. |
Using more primitive routines, |
you can write your own specific XDR routines |
to describe arbitrary data structures, |
including elements of arrays, arms of unions, |
or objects pointed at from other structures. |
The structures themselves may contain arrays of arbitrary elements, |
or pointers to other structures. |
.LP |
Let's examine the two programs more closely. |
There is a family of XDR stream creation routines |
in which each member treats the stream of bits differently. |
In our example, data is manipulated using standard I/O routines, |
so we use |
.I xdrstdio_create (). |
.IX xdrstdio_create() "" "\fIxdrstdio_create()\fP" |
The parameters to XDR stream creation routines |
vary according to their function. |
In our example, |
.I xdrstdio_create() |
takes a pointer to an XDR structure that it initializes, |
a pointer to a |
.I FILE |
that the input or output is performed on, and the operation. |
The operation may be |
.I XDR_ENCODE |
for serializing in the |
.I writer |
program, or |
.I XDR_DECODE |
for deserializing in the |
.I reader |
program. |
.LP |
Note: RPC users never need to create XDR streams; |
the RPC system itself creates these streams, |
which are then passed to the users. |
.LP |
The |
.I xdr_long() |
.IX xdr_long() "" "\fIxdr_long()\fP" |
primitive is characteristic of most XDR library |
primitives and all client XDR routines. |
First, the routine returns |
.I FALSE |
(0) if it fails, and |
.I TRUE |
(1) if it succeeds. |
Second, for each data type, |
.I xxx , |
there is an associated XDR routine of the form: |
.DS |
.ft CW |
xdr_xxx(xdrs, xp) |
XDR *xdrs; |
xxx *xp; |
{ |
} |
.DE |
In our case, |
.I xxx |
is long, and the corresponding XDR routine is |
a primitive, |
.I xdr_long() . |
The client could also define an arbitrary structure |
.I xxx |
in which case the client would also supply the routine |
.I xdr_xxx (), |
describing each field by calling XDR routines |
of the appropriate type. |
In all cases the first parameter, |
.I xdrs |
can be treated as an opaque handle, |
and passed to the primitive routines. |
.LP |
XDR routines are direction independent; |
that is, the same routines are called to serialize or deserialize data. |
This feature is critical to software engineering of portable data. |
The idea is to call the same routine for either operation \(em |
this almost guarantees that serialized data can also be deserialized. |
One routine is used by both producer and consumer of networked data. |
This is implemented by always passing the address |
of an object rather than the object itself \(em |
only in the case of deserialization is the object modified. |
This feature is not shown in our trivial example, |
but its value becomes obvious when nontrivial data structures |
are passed among machines. |
If needed, the user can obtain the |
direction of the XDR operation. |
See the |
.I "XDR Operation Directions" |
section below for details. |
.LP |
Let's look at a slightly more complicated example. |
Assume that a person's gross assets and liabilities |
are to be exchanged among processes. |
Also assume that these values are important enough |
to warrant their own data type: |
.ie t .DS |
.el .DS L |
.ft CW |
struct gnumbers { |
long g_assets; |
long g_liabilities; |
}; |
.DE |
The corresponding XDR routine describing this structure would be: |
.ie t .DS |
.el .DS L |
.ft CW |
bool_t /* \fITRUE is success, FALSE is failure\fP */ |
xdr_gnumbers(xdrs, gp) |
XDR *xdrs; |
struct gnumbers *gp; |
{ |
if (xdr_long(xdrs, &gp->g_assets) && |
xdr_long(xdrs, &gp->g_liabilities)) |
return(TRUE); |
return(FALSE); |
} |
.DE |
Note that the parameter |
.I xdrs |
is never inspected or modified; |
it is only passed on to the subcomponent routines. |
It is imperative to inspect the return value of each XDR routine call, |
and to give up immediately and return |
.I FALSE |
if the subroutine fails. |
.LP |
This example also shows that the type |
.I bool_t |
is declared as an integer whose only values are |
.I TRUE |
(1) and |
.I FALSE |
(0). This document uses the following definitions: |
.ie t .DS |
.el .DS L |
.ft CW |
#define bool_t int |
#define TRUE 1 |
#define FALSE 0 |
.DE |
.LP |
Keeping these conventions in mind, |
.I xdr_gnumbers() |
can be rewritten as follows: |
.ie t .DS |
.el .DS L |
.ft CW |
xdr_gnumbers(xdrs, gp) |
XDR *xdrs; |
struct gnumbers *gp; |
{ |
return(xdr_long(xdrs, &gp->g_assets) && |
xdr_long(xdrs, &gp->g_liabilities)); |
} |
.DE |
This document uses both coding styles. |
.NH 1 |
\&XDR Library Primitives |
.IX "library primitives for XDR" |
.IX XDR "library primitives" |
.LP |
This section gives a synopsis of each XDR primitive. |
It starts with basic data types and moves on to constructed data types. |
Finally, XDR utilities are discussed. |
The interface to these primitives |
and utilities is defined in the include file |
.I <rpc/xdr.h> , |
automatically included by |
.I <rpc/rpc.h> . |
.NH 2 |
\&Number Filters |
.IX "XDR library" "number filters" |
.LP |
The XDR library provides primitives to translate between numbers |
and their corresponding external representations. |
Primitives cover the set of numbers in: |
.DS |
.ft CW |
[signed, unsigned] * [short, int, long] |
.DE |
.ne 2i |
Specifically, the eight primitives are: |
.DS |
.ft CW |
bool_t xdr_char(xdrs, cp) |
XDR *xdrs; |
char *cp; |
.sp.5 |
bool_t xdr_u_char(xdrs, ucp) |
XDR *xdrs; |
unsigned char *ucp; |
.sp.5 |
bool_t xdr_int(xdrs, ip) |
XDR *xdrs; |
int *ip; |
.sp.5 |
bool_t xdr_u_int(xdrs, up) |
XDR *xdrs; |
unsigned *up; |
.sp.5 |
bool_t xdr_long(xdrs, lip) |
XDR *xdrs; |
long *lip; |
.sp.5 |
bool_t xdr_u_long(xdrs, lup) |
XDR *xdrs; |
u_long *lup; |
.sp.5 |
bool_t xdr_short(xdrs, sip) |
XDR *xdrs; |
short *sip; |
.sp.5 |
bool_t xdr_u_short(xdrs, sup) |
XDR *xdrs; |
u_short *sup; |
.DE |
The first parameter, |
.I xdrs , |
is an XDR stream handle. |
The second parameter is the address of the number |
that provides data to the stream or receives data from it. |
All routines return |
.I TRUE |
if they complete successfully, and |
.I FALSE |
otherwise. |
.NH 2 |
\&Floating Point Filters |
.IX "XDR library" "floating point filters" |
.LP |
The XDR library also provides primitive routines |
for C's floating point types: |
.DS |
.ft CW |
bool_t xdr_float(xdrs, fp) |
XDR *xdrs; |
float *fp; |
.sp.5 |
bool_t xdr_double(xdrs, dp) |
XDR *xdrs; |
double *dp; |
.DE |
The first parameter, |
.I xdrs |
is an XDR stream handle. |
The second parameter is the address |
of the floating point number that provides data to the stream |
or receives data from it. |
Both routines return |
.I TRUE |
if they complete successfully, and |
.I FALSE |
otherwise. |
.LP |
Note: Since the numbers are represented in IEEE floating point, |
routines may fail when decoding a valid IEEE representation |
into a machine-specific representation, or vice-versa. |
.NH 2 |
\&Enumeration Filters |
.IX "XDR library" "enumeration filters" |
.LP |
The XDR library provides a primitive for generic enumerations. |
The primitive assumes that a C |
.I enum |
has the same representation inside the machine as a C integer. |
The boolean type is an important instance of the |
.I enum . |
The external representation of a boolean is always |
.I TRUE |
(1) or |
.I FALSE |
(0). |
.DS |
.ft CW |
#define bool_t int |
#define FALSE 0 |
#define TRUE 1 |
.sp.5 |
#define enum_t int |
.sp.5 |
bool_t xdr_enum(xdrs, ep) |
XDR *xdrs; |
enum_t *ep; |
.sp.5 |
bool_t xdr_bool(xdrs, bp) |
XDR *xdrs; |
bool_t *bp; |
.DE |
The second parameters |
.I ep |
and |
.I bp |
are addresses of the associated type that provides data to, or |
receives data from, the stream |
.I xdrs . |
.NH 2 |
\&No Data |
.IX "XDR library" "no data" |
.LP |
Occasionally, an XDR routine must be supplied to the RPC system, |
even when no data is passed or required. |
The library provides such a routine: |
.DS |
.ft CW |
bool_t xdr_void(); /* \fIalways returns TRUE\fP */ |
.DE |
.NH 2 |
\&Constructed Data Type Filters |
.IX "XDR library" "constructed data type filters" |
.LP |
Constructed or compound data type primitives |
require more parameters and perform more complicated functions |
then the primitives discussed above. |
This section includes primitives for |
strings, arrays, unions, and pointers to structures. |
.LP |
Constructed data type primitives may use memory management. |
In many cases, memory is allocated when deserializing data with |
.I XDR_DECODE |
Therefore, the XDR package must provide means to deallocate memory. |
This is done by an XDR operation, |
.I XDR_FREE |
To review, the three XDR directional operations are |
.I XDR_ENCODE , |
.I XDR_DECODE |
and |
.I XDR_FREE . |
.NH 3 |
\&Strings |
.IX "XDR library" "strings" |
.LP |
In C, a string is defined as a sequence of bytes |
terminated by a null byte, |
which is not considered when calculating string length. |
However, when a string is passed or manipulated, |
a pointer to it is employed. |
Therefore, the XDR library defines a string to be a |
.I "char *" |
and not a sequence of characters. |
The external representation of a string is drastically different |
from its internal representation. |
Externally, strings are represented as |
sequences of ASCII characters, |
while internally, they are represented with character pointers. |
Conversion between the two representations |
is accomplished with the routine |
.I xdr_string (): |
.IX xdr_string() "" \fIxdr_string()\fP |
.DS |
.ft CW |
bool_t xdr_string(xdrs, sp, maxlength) |
XDR *xdrs; |
char **sp; |
u_int maxlength; |
.DE |
The first parameter |
.I xdrs |
is the XDR stream handle. |
The second parameter |
.I sp |
is a pointer to a string (type |
.I "char **" . |
The third parameter |
.I maxlength |
specifies the maximum number of bytes allowed during encoding or decoding. |
its value is usually specified by a protocol. For example, a protocol |
specification may say that a file name may be no longer than 255 characters. |
.LP |
The routine returns |
.I FALSE |
if the number of characters exceeds |
.I maxlength , |
and |
.I TRUE |
if it doesn't. |
.SH |
Keep |
.I maxlength |
small. If it is too big you can blow the heap, since |
.I xdr_string() |
will call |
.I malloc() |
for space. |
.LP |
The behavior of |
.I xdr_string() |
.IX xdr_string() "" \fIxdr_string()\fP |
is similar to the behavior of other routines |
discussed in this section. The direction |
.I XDR_ENCODE |
is easiest to understand. The parameter |
.I sp |
points to a string of a certain length; |
if the string does not exceed |
.I maxlength , |
the bytes are serialized. |
.LP |
The effect of deserializing a string is subtle. |
First the length of the incoming string is determined; |
it must not exceed |
.I maxlength . |
Next |
.I sp |
is dereferenced; if the the value is |
.I NULL , |
then a string of the appropriate length is allocated and |
.I *sp |
is set to this string. |
If the original value of |
.I *sp |
is non-null, then the XDR package assumes |
that a target area has been allocated, |
which can hold strings no longer than |
.I maxlength . |
In either case, the string is decoded into the target area. |
The routine then appends a null character to the string. |
.LP |
In the |
.I XDR_FREE |
operation, the string is obtained by dereferencing |
.I sp . |
If the string is not |
.I NULL , |
it is freed and |
.I *sp |
is set to |
.I NULL . |
In this operation, |
.I xdr_string() |
ignores the |
.I maxlength |
parameter. |
.NH 3 |
\&Byte Arrays |
.IX "XDR library" "byte arrays" |
.LP |
Often variable-length arrays of bytes are preferable to strings. |
Byte arrays differ from strings in the following three ways: |
1) the length of the array (the byte count) is explicitly |
located in an unsigned integer, |
2) the byte sequence is not terminated by a null character, and |
3) the external representation of the bytes is the same as their |
internal representation. |
The primitive |
.I xdr_bytes() |
.IX xdr_bytes() "" \fIxdr_bytes()\fP |
converts between the internal and external |
representations of byte arrays: |
.DS |
.ft CW |
bool_t xdr_bytes(xdrs, bpp, lp, maxlength) |
XDR *xdrs; |
char **bpp; |
u_int *lp; |
u_int maxlength; |
.DE |
The usage of the first, second and fourth parameters |
are identical to the first, second and third parameters of |
.I xdr_string (), |
respectively. |
The length of the byte area is obtained by dereferencing |
.I lp |
when serializing; |
.I *lp |
is set to the byte length when deserializing. |
.NH 3 |
\&Arrays |
.IX "XDR library" "arrays" |
.LP |
The XDR library package provides a primitive |
for handling arrays of arbitrary elements. |
The |
.I xdr_bytes() |
routine treats a subset of generic arrays, |
in which the size of array elements is known to be 1, |
and the external description of each element is built-in. |
The generic array primitive, |
.I xdr_array() , |
.IX xdr_array() "" \fIxdr_array()\fP |
requires parameters identical to those of |
.I xdr_bytes() |
plus two more: |
the size of array elements, |
and an XDR routine to handle each of the elements. |
This routine is called to encode or decode |
each element of the array. |
.DS |
.ft CW |
bool_t |
xdr_array(xdrs, ap, lp, maxlength, elementsiz, xdr_element) |
XDR *xdrs; |
char **ap; |
u_int *lp; |
u_int maxlength; |
u_int elementsiz; |
bool_t (*xdr_element)(); |
.DE |
The parameter |
.I ap |
is the address of the pointer to the array. |
If |
.I *ap |
is |
.I NULL |
when the array is being deserialized, |
XDR allocates an array of the appropriate size and sets |
.I *ap |
to that array. |
The element count of the array is obtained from |
.I *lp |
when the array is serialized; |
.I *lp |
is set to the array length when the array is deserialized. |
The parameter |
.I maxlength |
is the maximum number of elements that the array is allowed to have; |
.I elementsiz |
is the byte size of each element of the array |
(the C function |
.I sizeof() |
can be used to obtain this value). |
The |
.I xdr_element() |
.IX xdr_element() "" \fIxdr_element()\fP |
routine is called to serialize, deserialize, or free |
each element of the array. |
.br |
.LP |
Before defining more constructed data types, it is appropriate to |
present three examples. |
.LP |
.I "Example A:" |
.br |
A user on a networked machine can be identified by |
(a) the machine name, such as |
.I krypton : |
see the |
.I gethostname |
man page; (b) the user's UID: see the |
.I geteuid |
man page; and (c) the group numbers to which the user belongs: |
see the |
.I getgroups |
man page. A structure with this information and its associated |
XDR routine could be coded like this: |
.ie t .DS |
.el .DS L |
.ft CW |
struct netuser { |
char *nu_machinename; |
int nu_uid; |
u_int nu_glen; |
int *nu_gids; |
}; |
#define NLEN 255 /* \fImachine names < 256 chars\fP */ |
#define NGRPS 20 /* \fIuser can't be in > 20 groups\fP */ |
.sp.5 |
bool_t |
xdr_netuser(xdrs, nup) |
XDR *xdrs; |
struct netuser *nup; |
{ |
return(xdr_string(xdrs, &nup->nu_machinename, NLEN) && |
xdr_int(xdrs, &nup->nu_uid) && |
xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen, |
NGRPS, sizeof (int), xdr_int)); |
} |
.DE |
.LP |
.I "Example B:" |
.br |
A party of network users could be implemented |
as an array of |
.I netuser |
structure. |
The declaration and its associated XDR routines |
are as follows: |
.ie t .DS |
.el .DS L |
.ft CW |
struct party { |
u_int p_len; |
struct netuser *p_nusers; |
}; |
#define PLEN 500 /* \fImax number of users in a party\fP */ |
.sp.5 |
bool_t |
xdr_party(xdrs, pp) |
XDR *xdrs; |
struct party *pp; |
{ |
return(xdr_array(xdrs, &pp->p_nusers, &pp->p_len, PLEN, |
sizeof (struct netuser), xdr_netuser)); |
} |
.DE |
.LP |
.I "Example C:" |
.br |
The well-known parameters to |
.I main , |
.I argc |
and |
.I argv |
can be combined into a structure. |
An array of these structures can make up a history of commands. |
The declarations and XDR routines might look like: |
.ie t .DS |
.el .DS L |
.ft CW |
struct cmd { |
u_int c_argc; |
char **c_argv; |
}; |
#define ALEN 1000 /* \fIargs cannot be > 1000 chars\fP */ |
#define NARGC 100 /* \fIcommands cannot have > 100 args\fP */ |
|
struct history { |
u_int h_len; |
struct cmd *h_cmds; |
}; |
#define NCMDS 75 /* \fIhistory is no more than 75 commands\fP */ |
|
bool_t |
xdr_wrap_string(xdrs, sp) |
XDR *xdrs; |
char **sp; |
{ |
return(xdr_string(xdrs, sp, ALEN)); |
} |
.DE |
.ie t .DS |
.el .DS L |
.ft CW |
bool_t |
xdr_cmd(xdrs, cp) |
XDR *xdrs; |
struct cmd *cp; |
{ |
return(xdr_array(xdrs, &cp->c_argv, &cp->c_argc, NARGC, |
sizeof (char *), xdr_wrap_string)); |
} |
.DE |
.ie t .DS |
.el .DS L |
.ft CW |
bool_t |
xdr_history(xdrs, hp) |
XDR *xdrs; |
struct history *hp; |
{ |
return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS, |
sizeof (struct cmd), xdr_cmd)); |
} |
.DE |
The most confusing part of this example is that the routine |
.I xdr_wrap_string() |
is needed to package the |
.I xdr_string() |
routine, because the implementation of |
.I xdr_array() |
only passes two parameters to the array element description routine; |
.I xdr_wrap_string() |
supplies the third parameter to |
.I xdr_string (). |
.LP |
By now the recursive nature of the XDR library should be obvious. |
Let's continue with more constructed data types. |
.NH 3 |
\&Opaque Data |
.IX "XDR library" "opaque data" |
.LP |
In some protocols, handles are passed from a server to client. |
The client passes the handle back to the server at some later time. |
Handles are never inspected by clients; |
they are obtained and submitted. |
That is to say, handles are opaque. |
The |
.I xdr_opaque() |
.IX xdr_opaque() "" \fIxdr_opaque()\fP |
primitive is used for describing fixed sized, opaque bytes. |
.DS |
.ft CW |
bool_t xdr_opaque(xdrs, p, len) |
XDR *xdrs; |
char *p; |
u_int len; |
.DE |
The parameter |
.I p |
is the location of the bytes; |
.I len |
is the number of bytes in the opaque object. |
By definition, the actual data |
contained in the opaque object are not machine portable. |
.NH 3 |
\&Fixed Sized Arrays |
.IX "XDR library" "fixed sized arrays" |
.LP |
The XDR library provides a primitive, |
.I xdr_vector (), |
for fixed-length arrays. |
.ie t .DS |
.el .DS L |
.ft CW |
#define NLEN 255 /* \fImachine names must be < 256 chars\fP */ |
#define NGRPS 20 /* \fIuser belongs to exactly 20 groups\fP */ |
.sp.5 |
struct netuser { |
char *nu_machinename; |
int nu_uid; |
int nu_gids[NGRPS]; |
}; |
.sp.5 |
bool_t |
xdr_netuser(xdrs, nup) |
XDR *xdrs; |
struct netuser *nup; |
{ |
int i; |
.sp.5 |
if (!xdr_string(xdrs, &nup->nu_machinename, NLEN)) |
return(FALSE); |
if (!xdr_int(xdrs, &nup->nu_uid)) |
return(FALSE); |
if (!xdr_vector(xdrs, nup->nu_gids, NGRPS, sizeof(int), |
xdr_int)) { |
return(FALSE); |
} |
return(TRUE); |
} |
.DE |
.NH 3 |
\&Discriminated Unions |
.IX "XDR library" "discriminated unions" |
.LP |
The XDR library supports discriminated unions. |
A discriminated union is a C union and an |
.I enum_t |
value that selects an \*Qarm\*U of the union. |
.DS |
.ft CW |
struct xdr_discrim { |
enum_t value; |
bool_t (*proc)(); |
}; |
.sp.5 |
bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm) |
XDR *xdrs; |
enum_t *dscmp; |
char *unp; |
struct xdr_discrim *arms; |
bool_t (*defaultarm)(); /* \fImay equal NULL\fP */ |
.DE |
First the routine translates the discriminant of the union located at |
.I *dscmp . |
The discriminant is always an |
.I enum_t . |
Next the union located at |
.I *unp |
is translated. |
The parameter |
.I arms |
is a pointer to an array of |
.I xdr_discrim |
structures. |
Each structure contains an ordered pair of |
.I [value,proc] . |
If the union's discriminant is equal to the associated |
.I value , |
then the |
.I proc |
is called to translate the union. |
The end of the |
.I xdr_discrim |
structure array is denoted by a routine of value |
.I NULL |
(0). If the discriminant is not found in the |
.I arms |
array, then the |
.I defaultarm |
procedure is called if it is non-null; |
otherwise the routine returns |
.I FALSE . |
.LP |
.I "Example D:" |
Suppose the type of a union may be integer, |
character pointer (a string), or a |
.I gnumbers |
structure. |
Also, assume the union and its current type |
are declared in a structure. |
The declaration is: |
.ie t .DS |
.el .DS L |
.ft CW |
enum utype { INTEGER=1, STRING=2, GNUMBERS=3 }; |
.sp.5 |
struct u_tag { |
enum utype utype; /* \fIthe union's discriminant\fP */ |
union { |
int ival; |
char *pval; |
struct gnumbers gn; |
} uval; |
}; |
.DE |
The following constructs and XDR procedure (de)serialize |
the discriminated union: |
.ie t .DS |
.el .DS L |
.ft CW |
struct xdr_discrim u_tag_arms[4] = { |
{ INTEGER, xdr_int }, |
{ GNUMBERS, xdr_gnumbers } |
{ STRING, xdr_wrap_string }, |
{ __dontcare__, NULL } |
/* \fIalways terminate arms with a NULL xdr_proc\fP */ |
} |
.sp.5 |
bool_t |
xdr_u_tag(xdrs, utp) |
XDR *xdrs; |
struct u_tag *utp; |
{ |
return(xdr_union(xdrs, &utp->utype, &utp->uval, |
u_tag_arms, NULL)); |
} |
.DE |
The routine |
.I xdr_gnumbers() |
was presented above in |
.I "The XDR Library" |
section. |
.I xdr_wrap_string() |
was presented in example C. |
The default |
.I arm |
parameter to |
.I xdr_union() |
(the last parameter) is |
.I NULL |
in this example. Therefore the value of the union's discriminant |
may legally take on only values listed in the |
.I u_tag_arms |
array. This example also demonstrates that |
the elements of the arm's array do not need to be sorted. |
.LP |
It is worth pointing out that the values of the discriminant |
may be sparse, though in this example they are not. |
It is always good |
practice to assign explicitly integer values to each element of the |
discriminant's type. |
This practice both documents the external |
representation of the discriminant and guarantees that different |
C compilers emit identical discriminant values. |
.LP |
Exercise: Implement |
.I xdr_union() |
using the other primitives in this section. |
.NH 3 |
\&Pointers |
.IX "XDR library" "pointers" |
.LP |
In C it is often convenient to put pointers |
to another structure within a structure. |
The |
.I xdr_reference() |
.IX xdr_reference() "" \fIxdr_reference()\fP |
primitive makes it easy to serialize, deserialize, and free |
these referenced structures. |
.DS |
.ft CW |
bool_t xdr_reference(xdrs, pp, size, proc) |
XDR *xdrs; |
char **pp; |
u_int ssize; |
bool_t (*proc)(); |
.DE |
.LP |
Parameter |
.I pp |
is the address of |
the pointer to the structure; |
parameter |
.I ssize |
is the size in bytes of the structure (use the C function |
.I sizeof() |
to obtain this value); and |
.I proc |
is the XDR routine that describes the structure. |
When decoding data, storage is allocated if |
.I *pp |
is |
.I NULL . |
.LP |
There is no need for a primitive |
.I xdr_struct() |
to describe structures within structures, |
because pointers are always sufficient. |
.LP |
Exercise: Implement |
.I xdr_reference() |
using |
.I xdr_array (). |
Warning: |
.I xdr_reference() |
and |
.I xdr_array() |
are NOT interchangeable external representations of data. |
.LP |
.I "Example E:" |
Suppose there is a structure containing a person's name |
and a pointer to a |
.I gnumbers |
structure containing the person's gross assets and liabilities. |
The construct is: |
.DS |
.ft CW |
struct pgn { |
char *name; |
struct gnumbers *gnp; |
}; |
.DE |
The corresponding XDR routine for this structure is: |
.DS |
.ft CW |
bool_t |
xdr_pgn(xdrs, pp) |
XDR *xdrs; |
struct pgn *pp; |
{ |
if (xdr_string(xdrs, &pp->name, NLEN) && |
xdr_reference(xdrs, &pp->gnp, |
sizeof(struct gnumbers), xdr_gnumbers)) |
return(TRUE); |
return(FALSE); |
} |
.DE |
.IX "pointer semantics and XDR" |
.I "Pointer Semantics and XDR" |
.LP |
In many applications, C programmers attach double meaning to |
the values of a pointer. Typically the value |
.I NULL |
(or zero) means data is not needed, |
yet some application-specific interpretation applies. |
In essence, the C programmer is encoding |
a discriminated union efficiently |
by overloading the interpretation of the value of a pointer. |
For instance, in example E a |
.I NULL |
pointer value for |
.I gnp |
could indicate that |
the person's assets and liabilities are unknown. |
That is, the pointer value encodes two things: |
whether or not the data is known; |
and if it is known, where it is located in memory. |
Linked lists are an extreme example of the use |
of application-specific pointer interpretation. |
.LP |
The primitive |
.I xdr_reference() |
.IX xdr_reference() "" \fIxdr_reference()\fP |
cannot and does not attach any special |
meaning to a null-value pointer during serialization. |
That is, passing an address of a pointer whose value is |
.I NULL |
to |
.I xdr_reference() |
when serialing data will most likely cause a memory fault and, on the UNIX |
system, a core dump. |
.LP |
.I xdr_pointer() |
correctly handles |
.I NULL |
pointers. For more information about its use, see |
the |
.I "Linked Lists" |
topics below. |
.LP |
.I Exercise: |
After reading the section on |
.I "Linked Lists" , |
return here and extend example E so that |
it can correctly deal with |
.I NULL |
pointer values. |
.LP |
.I Exercise: |
Using the |
.I xdr_union (), |
.I xdr_reference() |
and |
.I xdr_void() |
primitives, implement a generic pointer handling primitive |
that implicitly deals with |
.I NULL |
pointers. That is, implement |
.I xdr_pointer (). |
.NH 2 |
\&Non-filter Primitives |
.IX "XDR" "non-filter primitives" |
.LP |
XDR streams can be manipulated with |
the primitives discussed in this section. |
.DS |
.ft CW |
u_int xdr_getpos(xdrs) |
XDR *xdrs; |
.sp.5 |
bool_t xdr_setpos(xdrs, pos) |
XDR *xdrs; |
u_int pos; |
.sp.5 |
xdr_destroy(xdrs) |
XDR *xdrs; |
.DE |
The routine |
.I xdr_getpos() |
.IX xdr_getpos() "" \fIxdr_getpos()\fP |
returns an unsigned integer |
that describes the current position in the data stream. |
Warning: In some XDR streams, the returned value of |
.I xdr_getpos() |
is meaningless; |
the routine returns a \-1 in this case |
(though \-1 should be a legitimate value). |
.LP |
The routine |
.I xdr_setpos() |
.IX xdr_setpos() "" \fIxdr_setpos()\fP |
sets a stream position to |
.I pos . |
Warning: In some XDR streams, setting a position is impossible; |
in such cases, |
.I xdr_setpos() |
will return |
.I FALSE . |
This routine will also fail if the requested position is out-of-bounds. |
The definition of bounds varies from stream to stream. |
.LP |
The |
.I xdr_destroy() |
.IX xdr_destroy() "" \fIxdr_destroy()\fP |
primitive destroys the XDR stream. |
Usage of the stream |
after calling this routine is undefined. |
.NH 2 |
\&XDR Operation Directions |
.IX XDR "operation directions" |
.IX "direction of XDR operations" |
.LP |
At times you may wish to optimize XDR routines by taking |
advantage of the direction of the operation \(em |
.I XDR_ENCODE |
.I XDR_DECODE |
or |
.I XDR_FREE |
The value |
.I xdrs->x_op |
always contains the direction of the XDR operation. |
Programmers are not encouraged to take advantage of this information. |
Therefore, no example is presented here. However, an example in the |
.I "Linked Lists" |
topic below, demonstrates the usefulness of the |
.I xdrs->x_op |
field. |
.NH 2 |
\&XDR Stream Access |
.IX "XDR" "stream access" |
.LP |
An XDR stream is obtained by calling the appropriate creation routine. |
These creation routines take arguments that are tailored to the |
specific properties of the stream. |
.LP |
Streams currently exist for (de)serialization of data to or from |
standard I/O |
.I FILE |
streams, TCP/IP connections and UNIX files, and memory. |
.NH 3 |
\&Standard I/O Streams |
.IX "XDR" "standard I/O streams" |
.LP |
XDR streams can be interfaced to standard I/O using the |
.I xdrstdio_create() |
.IX xdrstdio_create() "" \fIxdrstdio_create()\fP |
routine as follows: |
.DS |
.ft CW |
#include <stdio.h> |
#include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */ |
.sp.5 |
void |
xdrstdio_create(xdrs, fp, x_op) |
XDR *xdrs; |
FILE *fp; |
enum xdr_op x_op; |
.DE |
The routine |
.I xdrstdio_create() |
initializes an XDR stream pointed to by |
.I xdrs . |
The XDR stream interfaces to the standard I/O library. |
Parameter |
.I fp |
is an open file, and |
.I x_op |
is an XDR direction. |
.NH 3 |
\&Memory Streams |
.IX "XDR" "memory streams" |
.LP |
Memory streams allow the streaming of data into or out of |
a specified area of memory: |
.DS |
.ft CW |
#include <rpc/rpc.h> |
.sp.5 |
void |
xdrmem_create(xdrs, addr, len, x_op) |
XDR *xdrs; |
char *addr; |
u_int len; |
enum xdr_op x_op; |
.DE |
The routine |
.I xdrmem_create() |
.IX xdrmem_create() "" \fIxdrmem_create()\fP |
initializes an XDR stream in local memory. |
The memory is pointed to by parameter |
.I addr ; |
parameter |
.I len |
is the length in bytes of the memory. |
The parameters |
.I xdrs |
and |
.I x_op |
are identical to the corresponding parameters of |
.I xdrstdio_create (). |
Currently, the UDP/IP implementation of RPC uses |
.I xdrmem_create (). |
Complete call or result messages are built in memory before calling the |
.I sendto() |
system routine. |
.NH 3 |
\&Record (TCP/IP) Streams |
.IX "XDR" "record (TCP/IP) streams" |
.LP |
A record stream is an XDR stream built on top of |
a record marking standard that is built on top of the |
UNIX file or 4.2 BSD connection interface. |
.DS |
.ft CW |
#include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */ |
.sp.5 |
xdrrec_create(xdrs, |
sendsize, recvsize, iohandle, readproc, writeproc) |
XDR *xdrs; |
u_int sendsize, recvsize; |
char *iohandle; |
int (*readproc)(), (*writeproc)(); |
.DE |
The routine |
.I xdrrec_create() |
provides an XDR stream interface that allows for a bidirectional, |
arbitrarily long sequence of records. |
The contents of the records are meant to be data in XDR form. |
The stream's primary use is for interfacing RPC to TCP connections. |
However, it can be used to stream data into or out of normal |
UNIX files. |
.LP |
The parameter |
.I xdrs |
is similar to the corresponding parameter described above. |
The stream does its own data buffering similar to that of standard I/O. |
The parameters |
.I sendsize |
and |
.I recvsize |
determine the size in bytes of the output and input buffers, respectively; |
if their values are zero (0), then predetermined defaults are used. |
When a buffer needs to be filled or flushed, the routine |
.I readproc() |
or |
.I writeproc() |
is called, respectively. |
The usage and behavior of these |
routines are similar to the UNIX system calls |
.I read() |
and |
.I write (). |
However, |
the first parameter to each of these routines is the opaque parameter |
.I iohandle . |
The other two parameters |
.I buf "" |
and |
.I nbytes ) |
and the results |
(byte count) are identical to the system routines. |
If |
.I xxx |
is |
.I readproc() |
or |
.I writeproc (), |
then it has the following form: |
.DS |
.ft CW |
.ft I |
/* |
* returns the actual number of bytes transferred. |
* -1 is an error |
*/ |
.ft CW |
int |
xxx(iohandle, buf, len) |
char *iohandle; |
char *buf; |
int nbytes; |
.DE |
The XDR stream provides means for delimiting records in the byte stream. |
The implementation details of delimiting records in a stream are |
discussed in the |
.I "Advanced Topics" |
topic below. |
The primitives that are specific to record streams are as follows: |
.DS |
.ft CW |
bool_t |
xdrrec_endofrecord(xdrs, flushnow) |
XDR *xdrs; |
bool_t flushnow; |
.sp.5 |
bool_t |
xdrrec_skiprecord(xdrs) |
XDR *xdrs; |
.sp.5 |
bool_t |
xdrrec_eof(xdrs) |
XDR *xdrs; |
.DE |
The routine |
.I xdrrec_endofrecord() |
.IX xdrrec_endofrecord() "" \fIxdrrec_endofrecord()\fP |
causes the current outgoing data to be marked as a record. |
If the parameter |
.I flushnow |
is |
.I TRUE , |
then the stream's |
.I writeproc |
will be called; otherwise, |
.I writeproc |
will be called when the output buffer has been filled. |
.LP |
The routine |
.I xdrrec_skiprecord() |
.IX xdrrec_skiprecord() "" \fIxdrrec_skiprecord()\fP |
causes an input stream's position to be moved past |
the current record boundary and onto the |
beginning of the next record in the stream. |
.LP |
If there is no more data in the stream's input buffer, |
then the routine |
.I xdrrec_eof() |
.IX xdrrec_eof() "" \fIxdrrec_eof()\fP |
returns |
.I TRUE . |
That is not to say that there is no more data |
in the underlying file descriptor. |
.NH 2 |
\&XDR Stream Implementation |
.IX "XDR" "stream implementation" |
.IX "stream implementation in XDR" |
.LP |
This section provides the abstract data types needed |
to implement new instances of XDR streams. |
.NH 3 |
\&The XDR Object |
.IX "XDR" "object" |
.LP |
The following structure defines the interface to an XDR stream: |
.ie t .DS |
.el .DS L |
.ft CW |
enum xdr_op { XDR_ENCODE=0, XDR_DECODE=1, XDR_FREE=2 }; |
.sp.5 |
typedef struct { |
enum xdr_op x_op; /* \fIoperation; fast added param\fP */ |
struct xdr_ops { |
bool_t (*x_getlong)(); /* \fIget long from stream\fP */ |
bool_t (*x_putlong)(); /* \fIput long to stream\fP */ |
bool_t (*x_getbytes)(); /* \fIget bytes from stream\fP */ |
bool_t (*x_putbytes)(); /* \fIput bytes to stream\fP */ |
u_int (*x_getpostn)(); /* \fIreturn stream offset\fP */ |
bool_t (*x_setpostn)(); /* \fIreposition offset\fP */ |
caddr_t (*x_inline)(); /* \fIptr to buffered data\fP */ |
VOID (*x_destroy)(); /* \fIfree private area\fP */ |
} *x_ops; |
caddr_t x_public; /* \fIusers' data\fP */ |
caddr_t x_private; /* \fIpointer to private data\fP */ |
caddr_t x_base; /* \fIprivate for position info\fP */ |
int x_handy; /* \fIextra private word\fP */ |
} XDR; |
.DE |
The |
.I x_op |
field is the current operation being performed on the stream. |
This field is important to the XDR primitives, |
but should not affect a stream's implementation. |
That is, a stream's implementation should not depend |
on this value. |
The fields |
.I x_private , |
.I x_base , |
and |
.I x_handy |
are private to the particular |
stream's implementation. |
The field |
.I x_public |
is for the XDR client and should never be used by |
the XDR stream implementations or the XDR primitives. |
.I x_getpostn() , |
.I x_setpostn() |
and |
.I x_destroy() |
are macros for accessing operations. The operation |
.I x_inline() |
takes two parameters: |
an XDR *, and an unsigned integer, which is a byte count. |
The routine returns a pointer to a piece of |
the stream's internal buffer. |
The caller can then use the buffer segment for any purpose. |
From the stream's point of view, the bytes in the |
buffer segment have been consumed or put. |
The routine may return |
.I NULL |
if it cannot return a buffer segment of the requested size. |
(The |
.I x_inline() |
routine is for cycle squeezers. |
Use of the resulting buffer is not data-portable. |
Users are encouraged not to use this feature.) |
.LP |
The operations |
.I x_getbytes() |
and |
.I x_putbytes() |
blindly get and put sequences of bytes |
from or to the underlying stream; |
they return |
.I TRUE |
if they are successful, and |
.I FALSE |
otherwise. The routines have identical parameters (replace |
.I xxx ): |
.DS |
.ft CW |
bool_t |
xxxbytes(xdrs, buf, bytecount) |
XDR *xdrs; |
char *buf; |
u_int bytecount; |
.DE |
The operations |
.I x_getlong() |
and |
.I x_putlong() |
receive and put |
long numbers from and to the data stream. |
It is the responsibility of these routines |
to translate the numbers between the machine representation |
and the (standard) external representation. |
The UNIX primitives |
.I htonl() |
and |
.I ntohl() |
can be helpful in accomplishing this. |
The higher-level XDR implementation assumes that |
signed and unsigned long integers contain the same number of bits, |
and that nonnegative integers |
have the same bit representations as unsigned integers. |
The routines return |
.I TRUE |
if they succeed, and |
.I FALSE |
otherwise. They have identical parameters: |
.DS |
.ft CW |
bool_t |
xxxlong(xdrs, lp) |
XDR *xdrs; |
long *lp; |
.DE |
Implementors of new XDR streams must make an XDR structure |
(with new operation routines) available to clients, |
using some kind of create routine. |
.NH 1 |
\&Advanced Topics |
.IX XDR "advanced topics" |
.LP |
This section describes techniques for passing data structures that |
are not covered in the preceding sections. Such structures include |
linked lists (of arbitrary lengths). Unlike the simpler examples |
covered in the earlier sections, the following examples are written |
using both the XDR C library routines and the XDR data description |
language. |
The |
.I "External Data Representation Standard: Protocol Specification" |
describes this |
language in complete detail. |
.NH 2 |
\&Linked Lists |
.IX XDR "linked lists" |
.LP |
The last example in the |
.I Pointers |
topic earlier in this chapter |
presented a C data structure and its associated XDR |
routines for a individual's gross assets and liabilities. |
The example is duplicated below: |
.ie t .DS |
.el .DS L |
.ft CW |
struct gnumbers { |
long g_assets; |
long g_liabilities; |
}; |
.sp.5 |
bool_t |
xdr_gnumbers(xdrs, gp) |
XDR *xdrs; |
struct gnumbers *gp; |
{ |
if (xdr_long(xdrs, &(gp->g_assets))) |
return(xdr_long(xdrs, &(gp->g_liabilities))); |
return(FALSE); |
} |
.DE |
.LP |
Now assume that we wish to implement a linked list of such information. |
A data structure could be constructed as follows: |
.ie t .DS |
.el .DS L |
.ft CW |
struct gnumbers_node { |
struct gnumbers gn_numbers; |
struct gnumbers_node *gn_next; |
}; |
.sp .5 |
typedef struct gnumbers_node *gnumbers_list; |
.DE |
.LP |
The head of the linked list can be thought of as the data object; |
that is, the head is not merely a convenient shorthand for a |
structure. Similarly the |
.I gn_next |
field is used to indicate whether or not the object has terminated. |
Unfortunately, if the object continues, the |
.I gn_next |
field is also the address of where it continues. The link addresses |
carry no useful information when the object is serialized. |
.LP |
The XDR data description of this linked list is described by the |
recursive declaration of |
.I gnumbers_list : |
.ie t .DS |
.el .DS L |
.ft CW |
struct gnumbers { |
int g_assets; |
int g_liabilities; |
}; |
.sp .5 |
struct gnumbers_node { |
gnumbers gn_numbers; |
gnumbers_node *gn_next; |
}; |
.DE |
.LP |
In this description, the boolean indicates whether there is more data |
following it. If the boolean is |
.I FALSE , |
then it is the last data field of the structure. If it is |
.I TRUE , |
then it is followed by a gnumbers structure and (recursively) by a |
.I gnumbers_list . |
Note that the C declaration has no boolean explicitly declared in it |
(though the |
.I gn_next |
field implicitly carries the information), while the XDR data |
description has no pointer explicitly declared in it. |
.LP |
Hints for writing the XDR routines for a |
.I gnumbers_list |
follow easily from the XDR description above. Note how the primitive |
.I xdr_pointer() |
is used to implement the XDR union above. |
.ie t .DS |
.el .DS L |
.ft CW |
bool_t |
xdr_gnumbers_node(xdrs, gn) |
XDR *xdrs; |
gnumbers_node *gn; |
{ |
return(xdr_gnumbers(xdrs, &gn->gn_numbers) && |
xdr_gnumbers_list(xdrs, &gp->gn_next)); |
} |
.sp .5 |
bool_t |
xdr_gnumbers_list(xdrs, gnp) |
XDR *xdrs; |
gnumbers_list *gnp; |
{ |
return(xdr_pointer(xdrs, gnp, |
sizeof(struct gnumbers_node), |
xdr_gnumbers_node)); |
} |
.DE |
.LP |
The unfortunate side effect of XDR'ing a list with these routines |
is that the C stack grows linearly with respect to the number of |
node in the list. This is due to the recursion. The following |
routine collapses the above two mutually recursive into a single, |
non-recursive one. |
.ie t .DS |
.el .DS L |
.ft CW |
bool_t |
xdr_gnumbers_list(xdrs, gnp) |
XDR *xdrs; |
gnumbers_list *gnp; |
{ |
bool_t more_data; |
gnumbers_list *nextp; |
.sp .5 |
for (;;) { |
more_data = (*gnp != NULL); |
if (!xdr_bool(xdrs, &more_data)) { |
return(FALSE); |
} |
if (! more_data) { |
break; |
} |
if (xdrs->x_op == XDR_FREE) { |
nextp = &(*gnp)->gn_next; |
} |
if (!xdr_reference(xdrs, gnp, |
sizeof(struct gnumbers_node), xdr_gnumbers)) { |
|
return(FALSE); |
} |
gnp = (xdrs->x_op == XDR_FREE) ? |
nextp : &(*gnp)->gn_next; |
} |
*gnp = NULL; |
return(TRUE); |
} |
.DE |
.LP |
The first task is to find out whether there is more data or not, |
so that this boolean information can be serialized. Notice that |
this statement is unnecessary in the |
.I XDR_DECODE |
case, since the value of more_data is not known until we |
deserialize it in the next statement. |
.LP |
The next statement XDR's the more_data field of the XDR union. |
Then if there is truly no more data, we set this last pointer to |
.I NULL |
to indicate the end of the list, and return |
.I TRUE |
because we are done. Note that setting the pointer to |
.I NULL |
is only important in the |
.I XDR_DECODE |
case, since it is already |
.I NULL |
in the |
.I XDR_ENCODE |
and |
XDR_FREE |
cases. |
.LP |
Next, if the direction is |
.I XDR_FREE , |
the value of |
.I nextp |
is set to indicate the location of the next pointer in the list. |
We do this now because we need to dereference gnp to find the |
location of the next item in the list, and after the next |
statement the storage pointed to by |
.I gnp |
will be freed up and no be longer valid. We can't do this for all |
directions though, because in the |
.I XDR_DECODE |
direction the value of |
.I gnp |
won't be set until the next statement. |
.LP |
Next, we XDR the data in the node using the primitive |
.I xdr_reference (). |
.I xdr_reference() |
is like |
.I xdr_pointer() |
which we used before, but it does not |
send over the boolean indicating whether there is more data. |
We use it instead of |
.I xdr_pointer() |
because we have already XDR'd this information ourselves. Notice |
that the xdr routine passed is not the same type as an element |
in the list. The routine passed is |
.I xdr_gnumbers (), |
for XDR'ing gnumbers, but each element in the list is actually of |
type |
.I gnumbers_node . |
We don't pass |
.I xdr_gnumbers_node() |
because it is recursive, and instead use |
.I xdr_gnumbers() |
which XDR's all of the non-recursive part. Note that this trick |
will work only if the |
.I gn_numbers |
field is the first item in each element, so that their addresses |
are identical when passed to |
.I xdr_reference (). |
.LP |
Finally, we update |
.I gnp |
to point to the next item in the list. If the direction is |
.I XDR_FREE , |
we set it to the previously saved value, otherwise we can |
dereference |
.I gnp |
to get the proper value. Though harder to understand than the |
recursive version, this non-recursive routine is far less likely |
to blow the C stack. It will also run more efficiently since |
a lot of procedure call overhead has been removed. Most lists |
are small though (in the hundreds of items or less) and the |
recursive version should be sufficient for them. |
.EQ |
delim off |
.EN |
PSD.doc
Property changes :
Added: svn:ignore
## -0,0 +1,2 ##
+Makefile
+Makefile.in
Index: svc_unix.c
===================================================================
--- svc_unix.c (nonexistent)
+++ svc_unix.c (revision 1765)
@@ -0,0 +1,527 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc_unix.c 1.21 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc_unix.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc_unix.c,v 1.7 2000/01/27 23:06:42 jasone Exp $";
+#endif
+
+/*
+ * svc_unix.c, Server side for TCP/IP based RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * Actually implements two flavors of transporter -
+ * a unix rendezvouser (a listner and connection establisher)
+ * and a record/unix stream.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Ops vector for AF_UNIX based rpc service handle
+ */
+static bool_t svcunix_recv();
+static enum xprt_stat svcunix_stat();
+static bool_t svcunix_getargs();
+static bool_t svcunix_reply();
+static bool_t svcunix_freeargs();
+static void svcunix_destroy();
+
+static struct xp_ops svcunix_op = {
+ svcunix_recv,
+ svcunix_stat,
+ svcunix_getargs,
+ svcunix_reply,
+ svcunix_freeargs,
+ svcunix_destroy
+};
+
+/*
+ * Ops vector for TCP/IP rendezvous handler
+ */
+static bool_t rendezvous_request();
+static enum xprt_stat rendezvous_stat();
+
+static struct xp_ops svcunix_rendezvous_op = {
+ rendezvous_request,
+ rendezvous_stat,
+ (bool_t (*)())abort,
+ (bool_t (*)())abort,
+ (bool_t (*)())abort,
+ svcunix_destroy
+};
+
+static int readunix(), writeunix();
+static SVCXPRT *makefd_xprt();
+
+struct unix_rendezvous { /* kept in xprt->xp_p1 */
+ u_int sendsize;
+ u_int recvsize;
+};
+
+struct unix_conn { /* kept in xprt->xp_p1 */
+ enum xprt_stat strm_stat;
+ u_long x_id;
+ XDR xdrs;
+ char verf_body[MAX_AUTH_BYTES];
+};
+
+
+struct cmessage {
+ struct cmsghdr cmsg;
+ struct cmsgcred cmcred;
+};
+
+static struct cmessage cm;
+
+static int __msgread(sock, buf, cnt)
+ int sock;
+ void *buf;
+ size_t cnt;
+{
+ struct iovec iov[1];
+ struct msghdr msg;
+
+ bzero((char *)&cm, sizeof(cm));
+ iov[0].iov_base = buf;
+ iov[0].iov_len = cnt;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = (caddr_t)&cm;
+ msg.msg_controllen = sizeof(struct cmessage);
+ msg.msg_flags = 0;
+
+ return(recvmsg(sock, &msg, 0));
+}
+
+static int __msgwrite(sock, buf, cnt)
+ int sock;
+ void *buf;
+ size_t cnt;
+{
+ struct iovec iov[1];
+ struct msghdr msg;
+
+ bzero((char *)&cm, sizeof(cm));
+ iov[0].iov_base = buf;
+ iov[0].iov_len = cnt;
+
+ cm.cmsg.cmsg_type = SCM_CREDS;
+ cm.cmsg.cmsg_level = SOL_SOCKET;
+ cm.cmsg.cmsg_len = sizeof(struct cmessage);
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = (caddr_t)&cm;
+ msg.msg_controllen = sizeof(struct cmessage);
+ msg.msg_flags = 0;
+
+ return(sendmsg(sock, &msg, 0));
+}
+
+/*
+ * Usage:
+ * xprt = svcunix_create(sock, send_buf_size, recv_buf_size);
+ *
+ * Creates, registers, and returns a (rpc) unix based transporter.
+ * Once *xprt is initialized, it is registered as a transporter
+ * see (svc.h, xprt_register). This routine returns
+ * a NULL if a problem occurred.
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svcunix_create
+ * binds it to an arbitrary port. The routine then starts a unix
+ * listener on the socket's associated port. In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ *
+ * Since unix streams do buffered io similar to stdio, the caller can specify
+ * how big the send and receive buffers are via the second and third parms;
+ * 0 => use the system default.
+ */
+SVCXPRT *
+svcunix_create(sock, sendsize, recvsize, path)
+ register int sock;
+ u_int sendsize;
+ u_int recvsize;
+ char *path;
+{
+ bool_t madesock = FALSE;
+ register SVCXPRT *xprt;
+ register struct unix_rendezvous *r;
+ struct sockaddr_un addr;
+ int len = sizeof(struct sockaddr_un);
+
+ if (sock == RPC_ANYSOCK) {
+ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ perror("svc_unix.c - AF_UNIX socket creation problem");
+ return ((SVCXPRT *)NULL);
+ }
+ madesock = TRUE;
+ }
+ memset(&addr, 0, sizeof (addr));
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+ len = strlen(addr.sun_path) + sizeof(addr.sun_family) +
+ sizeof(addr.sun_len) + 1;
+ addr.sun_len = len;
+
+ bind(sock, (struct sockaddr *)&addr, len);
+
+ if ((getsockname(sock, (struct sockaddr *)&addr, &len) != 0) ||
+ (listen(sock, 2) != 0)) {
+ perror("svc_unix.c - cannot getsockname or listen");
+ if (madesock)
+ (void)_RPC_close(sock);
+ return ((SVCXPRT *)NULL);
+ }
+ r = (struct unix_rendezvous *)mem_alloc(sizeof(*r));
+ if (r == NULL) {
+ (void) fprintf(stderr, "svcunix_create: out of memory\n");
+ return (NULL);
+ }
+ r->sendsize = sendsize;
+ r->recvsize = recvsize;
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == NULL) {
+ (void) fprintf(stderr, "svcunix_create: out of memory\n");
+ return (NULL);
+ }
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (caddr_t)r;
+ xprt->xp_verf = _null_auth;
+ xprt->xp_ops = &svcunix_rendezvous_op;
+ xprt->xp_port = -1 /*ntohs(addr.sin_port)*/;
+ xprt->xp_sock = sock;
+ xprt_register(xprt);
+ return (xprt);
+}
+
+/*
+ * Like svunix_create(), except the routine takes any *open* UNIX file
+ * descriptor as its first input.
+ */
+SVCXPRT *
+svcunixfd_create(fd, sendsize, recvsize)
+ int fd;
+ u_int sendsize;
+ u_int recvsize;
+{
+
+ return (makefd_xprt(fd, sendsize, recvsize));
+}
+
+static SVCXPRT *
+makefd_xprt(fd, sendsize, recvsize)
+ int fd;
+ u_int sendsize;
+ u_int recvsize;
+{
+ register SVCXPRT *xprt;
+ register struct unix_conn *cd;
+
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == (SVCXPRT *)NULL) {
+ (void) fprintf(stderr, "svc_unix: makefd_xprt: out of memory\n");
+ goto done;
+ }
+ cd = (struct unix_conn *)mem_alloc(sizeof(struct unix_conn));
+ if (cd == (struct unix_conn *)NULL) {
+ (void) fprintf(stderr, "svc_unix: makefd_xprt: out of memory\n");
+ mem_free((char *) xprt, sizeof(SVCXPRT));
+ xprt = (SVCXPRT *)NULL;
+ goto done;
+ }
+ cd->strm_stat = XPRT_IDLE;
+ xdrrec_create(&(cd->xdrs), sendsize, recvsize,
+ (caddr_t)xprt, readunix, writeunix);
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (caddr_t)cd;
+ xprt->xp_verf.oa_base = cd->verf_body;
+ xprt->xp_addrlen = 0;
+ xprt->xp_ops = &svcunix_op; /* truely deals with calls */
+ xprt->xp_port = 0; /* this is a connection, not a rendezvouser */
+ xprt->xp_sock = fd;
+ xprt_register(xprt);
+ done:
+ return (xprt);
+}
+
+static bool_t
+rendezvous_request(xprt)
+ register SVCXPRT *xprt;
+{
+ int sock;
+ struct unix_rendezvous *r;
+ struct sockaddr_un addr;
+ struct sockaddr_in in_addr;
+ int len;
+
+ r = (struct unix_rendezvous *)xprt->xp_p1;
+ again:
+ len = sizeof(struct sockaddr_in);
+ if ((sock = accept(xprt->xp_sock, (struct sockaddr *)&addr,
+ &len)) < 0) {
+ if (errno == EINTR)
+ goto again;
+ return (FALSE);
+ }
+
+ /*
+ * make a new transporter (re-uses xprt)
+ */
+ bzero((char *)&in_addr, sizeof(in_addr));
+ in_addr.sin_family = AF_UNIX;
+ xprt = makefd_xprt(sock, r->sendsize, r->recvsize);
+ xprt->xp_raddr = in_addr;
+ xprt->xp_addrlen = len;
+ return (FALSE); /* there is never an rpc msg to be processed */
+}
+
+static enum xprt_stat
+rendezvous_stat()
+{
+
+ return (XPRT_IDLE);
+}
+
+static void
+svcunix_destroy(xprt)
+ register SVCXPRT *xprt;
+{
+ register struct unix_conn *cd = (struct unix_conn *)xprt->xp_p1;
+
+ xprt_unregister(xprt);
+ (void)_RPC_close(xprt->xp_sock);
+ if (xprt->xp_port != 0) {
+ /* a rendezvouser socket */
+ xprt->xp_port = 0;
+ } else {
+ /* an actual connection socket */
+ XDR_DESTROY(&(cd->xdrs));
+ }
+ mem_free((caddr_t)cd, sizeof(struct unix_conn));
+ mem_free((caddr_t)xprt, sizeof(SVCXPRT));
+}
+
+/*
+ * All read operations timeout after 35 seconds.
+ * A timeout is fatal for the connection.
+ */
+static struct timeval wait_per_try = { 35, 0 };
+
+/*
+ * reads data from the unix conection.
+ * any error is fatal and the connection is closed.
+ * (And a read of zero bytes is a half closed stream => error.)
+ *
+ * Note: we have to be careful here not to allow ourselves to become
+ * blocked too long in this routine. While we're waiting for data from one
+ * client, another client may be trying to connect. To avoid this situation,
+ * some code from svc_run() is transplanted here: the select() loop checks
+ * all RPC descriptors including the one we want and calls svc_getreqset2()
+ * to handle new requests if any are detected.
+ */
+static int
+readunix(xprt, buf, len)
+ register SVCXPRT *xprt;
+ caddr_t buf;
+ register int len;
+{
+ register int sock = xprt->xp_sock;
+ struct timeval start, delta, tv;
+ struct timeval tmp1, tmp2;
+ fd_set *fds;
+
+ delta = wait_per_try;
+ fds = NULL;
+ gettimeofday(&start, NULL);
+ do {
+ int bytes = sizeof (fd_set);
+ if (fds != NULL)
+ free(fds);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL)
+ goto fatal_err;
+ memcpy(fds, __svc_fdset, bytes);
+
+ /* XXX we know the other bits are still clear */
+ FD_SET(sock, fds);
+ tv = delta; /* in case select() implements writeback */
+ switch (select(svc_maxfd + 1, fds, NULL, NULL, &tv)) {
+ case -1:
+ if (errno != EINTR)
+ goto fatal_err;
+ gettimeofday(&tmp1, NULL);
+ timersub(&tmp1, &start, &tmp2);
+ timersub(&wait_per_try, &tmp2, &tmp1);
+ if (tmp1.tv_sec < 0 || !timerisset(&tmp1))
+ goto fatal_err;
+ delta = tmp1;
+ continue;
+ case 0:
+ goto fatal_err;
+ default:
+ if (!FD_ISSET(sock, fds)) {
+ svc_getreqset2(fds, svc_maxfd + 1);
+ gettimeofday(&tmp1, NULL);
+ timersub(&tmp1, &start, &tmp2);
+ timersub(&wait_per_try, &tmp2, &tmp1);
+ if (tmp1.tv_sec < 0 || !timerisset(&tmp1))
+ goto fatal_err;
+ delta = tmp1;
+ continue;
+ }
+ }
+ } while (!FD_ISSET(sock, fds));
+ if ((len = __msgread(sock, buf, len)) > 0) {
+ if (fds != NULL)
+ free(fds);
+ return (len);
+ }
+fatal_err:
+ ((struct unix_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
+ if (fds != NULL)
+ free(fds);
+ return (-1);
+}
+
+/*
+ * writes data to the unix connection.
+ * Any error is fatal and the connection is closed.
+ */
+static int
+writeunix(xprt, buf, len)
+ register SVCXPRT *xprt;
+ caddr_t buf;
+ int len;
+{
+ register int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i) {
+ if ((i = __msgwrite(xprt->xp_sock, buf, cnt)) < 0) {
+ ((struct unix_conn *)(xprt->xp_p1))->strm_stat =
+ XPRT_DIED;
+ return (-1);
+ }
+ }
+ return (len);
+}
+
+static enum xprt_stat
+svcunix_stat(xprt)
+ SVCXPRT *xprt;
+{
+ register struct unix_conn *cd =
+ (struct unix_conn *)(xprt->xp_p1);
+
+ if (cd->strm_stat == XPRT_DIED)
+ return (XPRT_DIED);
+ if (! xdrrec_eof(&(cd->xdrs)))
+ return (XPRT_MOREREQS);
+ return (XPRT_IDLE);
+}
+
+static bool_t
+svcunix_recv(xprt, msg)
+ SVCXPRT *xprt;
+ register struct rpc_msg *msg;
+{
+ register struct unix_conn *cd =
+ (struct unix_conn *)(xprt->xp_p1);
+ register XDR *xdrs = &(cd->xdrs);
+
+ xdrs->x_op = XDR_DECODE;
+ (void)xdrrec_skiprecord(xdrs);
+ if (xdr_callmsg(xdrs, msg)) {
+ cd->x_id = msg->rm_xid;
+ /* set up verifiers */
+ msg->rm_call.cb_verf.oa_flavor = AUTH_UNIX;
+ msg->rm_call.cb_verf.oa_base = (caddr_t)&cm;
+ msg->rm_call.cb_verf.oa_length = sizeof(cm);
+ return (TRUE);
+ }
+ cd->strm_stat = XPRT_DIED; /* XXXX */
+ return (FALSE);
+}
+
+static bool_t
+svcunix_getargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+
+ return ((*xdr_args)(&(((struct unix_conn *)(xprt->xp_p1))->xdrs), args_ptr));
+}
+
+static bool_t
+svcunix_freeargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+ register XDR *xdrs =
+ &(((struct unix_conn *)(xprt->xp_p1))->xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args)(xdrs, args_ptr));
+}
+
+static bool_t
+svcunix_reply(xprt, msg)
+ SVCXPRT *xprt;
+ register struct rpc_msg *msg;
+{
+ register struct unix_conn *cd =
+ (struct unix_conn *)(xprt->xp_p1);
+ register XDR *xdrs = &(cd->xdrs);
+ register bool_t stat;
+
+ xdrs->x_op = XDR_ENCODE;
+ msg->rm_xid = cd->x_id;
+ stat = xdr_replymsg(xdrs, msg);
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ return (stat);
+}
Index: authdes_prot.c
===================================================================
--- authdes_prot.c (nonexistent)
+++ authdes_prot.c (revision 1765)
@@ -0,0 +1,82 @@
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)authdes_prot.c 2.1 88/07/29 4.0 RPCSRC; from 1.6 88/02/08 SMI";
+#endif
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1988 by Sun Microsystems, Inc.
+ */
+
+/*
+ * authdes_prot.c, XDR routines for DES authentication
+ */
+
+#include
+#include
+#include
+#include
+
+#define ATTEMPT(xdr_op) if (!(xdr_op)) return (FALSE)
+
+bool_t
+xdr_authdes_cred(xdrs, cred)
+ XDR *xdrs;
+ struct authdes_cred *cred;
+{
+ /*
+ * Unrolled xdr
+ */
+ ATTEMPT(xdr_enum(xdrs, (enum_t *)&cred->adc_namekind));
+ switch (cred->adc_namekind) {
+ case ADN_FULLNAME:
+ ATTEMPT(xdr_string(xdrs, &cred->adc_fullname.name, MAXNETNAMELEN));
+ ATTEMPT(xdr_opaque(xdrs, (caddr_t)&cred->adc_fullname.key, sizeof(des_block)));
+ ATTEMPT(xdr_opaque(xdrs, (caddr_t)&cred->adc_fullname.window, sizeof(cred->adc_fullname.window)));
+ return (TRUE);
+ case ADN_NICKNAME:
+ ATTEMPT(xdr_opaque(xdrs, (caddr_t)&cred->adc_nickname, sizeof(cred->adc_nickname)));
+ return (TRUE);
+ default:
+ return (FALSE);
+ }
+}
+
+
+bool_t
+xdr_authdes_verf(xdrs, verf)
+ register XDR *xdrs;
+ register struct authdes_verf *verf;
+{
+ /*
+ * Unrolled xdr
+ */
+ ATTEMPT(xdr_opaque(xdrs, (caddr_t)&verf->adv_xtimestamp, sizeof(des_block)));
+ ATTEMPT(xdr_opaque(xdrs, (caddr_t)&verf->adv_int_u, sizeof(verf->adv_int_u)));
+ return (TRUE);
+}
Index: Makefile.in
===================================================================
--- Makefile.in (nonexistent)
+++ Makefile.in (revision 1765)
@@ -0,0 +1,633 @@
+# Makefile.in generated by automake 1.6.2 from Makefile.am.
+# @configure_input@
+
+# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+# Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+mandir = @mandir@
+includedir = @includedir@
+oldincludedir = /usr/include
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = @program_transform_name@
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+host_alias = @host_alias@
+host_triplet = @host@
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+AMTAR = @AMTAR@
+AWK = @AWK@
+BARE_CPU_CFLAGS = @BARE_CPU_CFLAGS@
+BARE_CPU_MODEL = @BARE_CPU_MODEL@
+
+CC = @CC@ $(GCCSPECS)
+CPP = @CPP@ $(GCCSPECS)
+DEPDIR = @DEPDIR@
+ENDIF = @ENDIF@
+GCCSED = @GCCSED@
+GCC_SPECS = @GCC_SPECS@
+HAS_NETWORKING = @HAS_NETWORKING@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MULTIBUILDTOP = @MULTIBUILDTOP@
+MULTISUBDIR = @MULTISUBDIR@
+PACKAGE = @PACKAGE@
+PROJECT_INCLUDE = @PROJECT_INCLUDE@
+PROJECT_ROOT = @PROJECT_ROOT@
+PROJECT_TOPdir = @PROJECT_TOPdir@
+RANLIB = @RANLIB@
+RTEMS_BSP = @RTEMS_BSP@
+RTEMS_CPU = @RTEMS_CPU@
+RTEMS_HAS_NETWORKING = @RTEMS_HAS_NETWORKING@
+RTEMS_ROOT = @RTEMS_ROOT@
+RTEMS_TOPdir = @RTEMS_TOPdir@
+STRIP = @STRIP@
+VERSION = @VERSION@
+am__include = @am__include@
+am__quote = @am__quote@
+install_sh = @install_sh@
+multilib_basedir = @multilib_basedir@
+project_libdir = @project_libdir@
+
+@MULTILIB_TRUE@MULTISRCTOP =
+@MULTILIB_TRUE@MULTIDIRS =
+@MULTILIB_TRUE@MULTIDO = true
+@MULTILIB_TRUE@MULTICLEAN = true
+
+@RTEMS_USE_GCC_TRUE@CFLAGS_DEFAULT = -g -Wall
+@RTEMS_USE_GCC_TRUE@GCCSPECS = $(GCC_SPECS)
+
+DEFS = @DEFS@
+
+CPPFLAGS = @CPPFLAGS@ $(CPU_DEFINES) \
+ $(DEFINES) $(XCPPFLAGS) $(CPPFLAGS_GCC)
+
+CFLAGS = $(CFLAGS_DEFAULT) $(CPU_CFLAGS) $(XCFLAGS)
+ASFLAGS = $(CPU_ASFLAGS) $(CPU_CFLAGS) $(XASFLAGS)
+
+# when debugging, optimize flag: typically empty
+# some compilers do allow optimization with their "-g"
+CFLAGS_DEBUG_OPTIMIZE_V = -g
+
+# profile flag; use gprof(1)
+CFLAGS_PROFILE_V = -pg
+
+
+#
+# How to compile stuff into ${ARCH} subdirectory
+#
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+
+CCLD = $(CC)
+
+CCASCOMPILE = $(CCAS) $(AM_CCASFLAGS) $(CCASFLAGS)
+
+
+# Dependency files for use by gmake
+# NOTE: we don't put them into $(ARCH)
+# so that 'make clean' doesn't blow it away
+DEPEND = Depends-${ARCH}
+
+CLEAN_DEPEND = $(DEPEND).tmp
+CLOBBER_DEPEND = $(DEPEND)
+
+VARIANT = OPTIMIZE
+
+VARIANT_OPTIMIZE_V = OPTIMIZE
+VARIANT_DEBUG_V = DEBUG
+VARIANT_PROFILE_V = PROFILE
+VARIANT_optimize_V = OPTIMIZE
+VARIANT_debug_V = DEBUG
+VARIANT_profile_V = PROFILE
+
+VARIANT_V = $(VARIANT_$(VARIANT)_V)
+
+ARCH_OPTIMIZE_V = o-optimize
+ARCH_DEBUG_V = o-debug
+ARCH_PROFILE_V = o-profile
+
+ARCH__V = $(ARCH_OPTIMIZE_V)
+ARCH = $(ARCH_$(VARIANT_V)_V)
+
+LIBSUFFIX_OPTIMIZE_V =
+LIBSUFFIX_DEBUG_V = _g
+LIBSUFFIX_PROFILE_V = _p
+LIBSUFFIX__V = $(LIBSUFFIX_OPTIMIZE_V)
+
+LIB_VARIANT = $(LIBSUFFIX_$(VARIANT_V)_V)
+LIBSUFFIX_VA = $(LIB_VARIANT).a
+
+CFLAGS__V = $(CFLAGS_OPTIMIZE_V)
+
+@RTEMS_USE_GCC_TRUE@RTEMS_CFLAGS_OPTIMIZE_V =
+@RTEMS_USE_GCC_TRUE@RTEMS_CFLAGS_DEBUG_V = -Wno-unused
+@RTEMS_USE_GCC_TRUE@RTEMS_CFLAGS_PROFILE_V =
+
+RTEMS_CFLAGS__V = $(RTEMS_CFLAGS_OPTIMIZE_V)
+
+
+#
+# Add local stuff here using +=
+#
+AM_CPPFLAGS = $(RTEMS_CPPFLAGS) '-D__P(x)=x' -D_RPC_read=read -D_RPC_write=write -D_RPC_close=close -D_RTEMS_RPC_INTERNAL_
+
+AM_CFLAGS = $(RTEMS_CFLAGS_$(VARIANT_V)_V) $(CFLAGS_$(VARIANT_V)_V)
+
+# AM_CFLAGS = $(RTEMS_BSP_CFLAGS) $(RTEMS_CFLAGS)
+AM_CCASFLAGS = $(RTEMS_BSP_CFLAGS) $(RTEMS_CPPFLAGS) $(RTEMS_ASFLAGS)
+
+AR = @AR@
+
+ARFLAGS = ruv
+
+TMPINSTALL_FILES = $(project_libdir)$(MULTISUBDIR)
+
+LIB = $(ARCH)/librpc.a
+
+C_FILES = auth_none.c auth_unix.c authunix_prot.c bindresvport.c \
+ clnt_generic.c clnt_perror.c clnt_raw.c clnt_simple.c clnt_tcp.c \
+ clnt_udp.c get_myaddress.c getrpcent.c getrpcport.c netname.c netnamer.c \
+ pmap_clnt.c pmap_getmaps.c pmap_getport.c pmap_prot.c pmap_prot2.c \
+ pmap_rmt.c rpc_callmsg.c rpc_commondata.c rpc_dtablesize.c rpc_prot.c \
+ rpcdname.c rtime.c svc.c svc_auth.c svc_auth_unix.c svc_raw.c svc_run.c \
+ svc_simple.c svc_tcp.c svc_udp.c rtems_portmapper.c rtems_rpc.c
+
+UNUSED_C_FILES = auth_des.c auth_time.c authdes_prot.c clnt_unix.c \
+ crypt_client.c des_crypt.c des_soft.c getpublickey.c key_call.c \
+ key_prot_xdr.c svc_auth_des.c svc_unix.c
+
+
+C_O_FILES = $(C_FILES:%.c=$(ARCH)/%.$(OBJEXT))
+
+OBJS = $(C_O_FILES)
+
+noinst_MANS = bindresvport.3 getrpcent.3 publickey.3 rpc.3 rpc_secure.3 \
+ rstat_svc.8 des_crypt.3 getrpcport.3 publickey.5 rpc.5 rstat.1 rtime.3
+
+
+@HAS_NETWORKING_TRUE@man_MANS = $(noinst_MANS)
+
+EXTRA_DIST = $(C_FILES) DISCLAIMER $(UNUSED_C_FILES) $(noinst_MANS) PSD.doc/nfs.rfc.ms PSD.doc/rpc.prog.ms PSD.doc/rpc.rfc.ms \
+ PSD.doc/rpcgen.ms PSD.doc/xdr.nts.ms PSD.doc/xdr.rfc.ms
+
+
+PROJECT_TOOLS = $(PROJECT_RELEASE)/build-tools
+subdir = src/rpc
+mkinstalldirs = $(SHELL) $(top_srcdir)/../../mkinstalldirs
+CONFIG_CLEAN_FILES =
+DIST_SOURCES =
+
+NROFF = nroff
+MANS = $(man_MANS)
+DIST_COMMON = README Makefile.am Makefile.in
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/../automake/multilib.am $(top_srcdir)/../automake/compile.am $(top_srcdir)/../automake/lib.am $(top_srcdir)/../automake/local.am $(top_srcdir)/configure.ac $(ACLOCAL_M4)
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/rpc/Makefile
+Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)
+uninstall-info-am:
+
+man1dir = $(mandir)/man1
+install-man1: $(man1_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(man1dir)
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 1*) ;; \
+ *) ext='1' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man1dir)/$$inst; \
+ done
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man1dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man1dir)/$$inst; \
+ done
+
+man3dir = $(mandir)/man3
+install-man3: $(man3_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(man3dir)
+ @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.3*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 3*) ;; \
+ *) ext='3' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man3dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man3dir)/$$inst; \
+ done
+uninstall-man3:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man3_MANS) $(dist_man3_MANS) $(nodist_man3_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.3*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man3dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man3dir)/$$inst; \
+ done
+
+man5dir = $(mandir)/man5
+install-man5: $(man5_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(man5dir)
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 5*) ;; \
+ *) ext='5' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man5dir)/$$inst; \
+ done
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.5*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man5dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man5dir)/$$inst; \
+ done
+
+man8dir = $(mandir)/man8
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(man8dir)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \
+ done
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man8dir)/$$inst; \
+ done
+tags: TAGS
+TAGS:
+
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+
+top_distdir = ../..
+distdir = $(top_distdir)/$(PACKAGE)-$(VERSION)
+
+distdir: $(DISTFILES)
+ $(mkinstalldirs) $(distdir)/PSD.doc
+ @list='$(DISTFILES)'; for file in $$list; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkinstalldirs) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(MANS) all-local
+
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(man1dir) $(DESTDIR)$(man3dir) $(DESTDIR)$(man5dir) $(DESTDIR)$(man8dir)
+
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-local mostlyclean-am
+
+distclean: distclean-am
+
+distclean-am: clean-am distclean-generic distclean-local
+
+dvi: dvi-am
+
+dvi-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man: install-man1 install-man3 install-man5 install-man8
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+uninstall-am: uninstall-info-am uninstall-man
+
+uninstall-man: uninstall-man1 uninstall-man3 uninstall-man5 \
+ uninstall-man8
+
+.PHONY: all all-am all-local check check-am clean clean-generic \
+ clean-local distclean distclean-generic distclean-local distdir \
+ dvi dvi-am info info-am install install-am install-data \
+ install-data-am install-exec install-exec-am install-info \
+ install-info-am install-man install-man1 install-man3 \
+ install-man5 install-man8 install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ uninstall uninstall-am uninstall-info-am uninstall-man \
+ uninstall-man1 uninstall-man3 uninstall-man5 uninstall-man8
+
+
+# Multilib support rules
+.PHONY: all-multi install-multi mostlyclean-multi clean-multi distclean-multi \
+ maintainer-clean-multi
+
+@MULTILIB_TRUE@all-recursive: all-multi
+@MULTILIB_TRUE@install-recursive: install-multi
+
+@MULTILIB_TRUE@mostlyclean-recursive: mostlyclean-multi
+@MULTILIB_TRUE@clean-recursive: clean-multi
+@MULTILIB_TRUE@distclean-recursive: distclean-multi
+@MULTILIB_TRUE@maintainer-clean-recursive: maintainer-clean-multi
+
+@MULTILIB_TRUE@all-multi:
+@MULTILIB_TRUE@ $(MULTIDO) $(AM_MAKEFLAGS) DO=all multi-do
+@MULTILIB_TRUE@install-multi:
+@MULTILIB_TRUE@ $(MULTIDO) $(AM_MAKEFLAGS) DO=install multi-do
+
+@MULTILIB_TRUE@mostlyclean-multi:
+@MULTILIB_TRUE@ $(MULTICLEAN) $(AM_MAKEFLAGS) DO=mostlyclean multi-clean
+@MULTILIB_TRUE@clean-multi:
+@MULTILIB_TRUE@ $(MULTICLEAN) $(AM_MAKEFLAGS) DO=clean multi-clean
+@MULTILIB_TRUE@distclean-multi:
+@MULTILIB_TRUE@ $(MULTICLEAN) $(AM_MAKEFLAGS) DO=distclean multi-clean
+@MULTILIB_TRUE@maintainer-clean-multi:
+@MULTILIB_TRUE@ $(MULTICLEAN) $(AM_MAKEFLAGS) DO=maintainer-clean multi-clean
+@MULTILIB_FALSE@include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
+@RTEMS_USE_GCC_FALSE@include $(CONFIG.CC)
+
+${ARCH}/%.$(OBJEXT): %.c
+ ${COMPILE} -o $@ -c $<
+
+${ARCH}/%.$(OBJEXT): %.S
+ ${CCASCOMPILE} -o $@ -c $<
+
+# Make foo.rel from foo.$(OBJEXT)
+${ARCH}/%.rel: ${ARCH}/%.$(OBJEXT)
+ ${make-rel}
+
+# We deliberately don't have anything depend on the
+# $(DEPEND) file; otherwise it will get rebuilt even
+# on 'make clean'
+#
+
+depend-am: $(C_FILES) $(CC_FILES) $(S_FILES)
+ $(COMPILE) -M $^ | \
+ sed -e 's?^\(.*\)\.o[ ]*:?$$(ARCH)/\1.o:?' \
+ -e 's?$(ARCH)/?$$(ARCH)/?' >$(DEPEND).tmp
+ mv $(DEPEND).tmp $(DEPEND)
+depend: depend-am
+
+# pull in dependencies if they exist
+ifeq (${DEPEND},$(wildcard ${DEPEND}))
+include ${DEPEND}
+@ENDIF@
+
+define make-library
+$(RM) $@
+$(AR) $(ARFLAGS) $@ $^
+$(RANLIB) $@
+endef
+
+$(project_libdir)$(MULTISUBDIR):
+ @$(mkinstalldirs) $@
+
+.PRECIOUS: $(LIB)
+
+$(LIB): $(OBJS)
+ $(make-library)
+@HAS_NETWORKING_TRUE@all-local: $(ARCH) $(LIB)
+
+debug:
+ @echo
+ @echo "\"make debug\" is obsolete, instead use:"
+ @echo " make VARIANT=DEBUG"
+ @echo
+
+.PHONY: debug
+
+profile:
+ @echo
+ @echo "\"make profile\" is obsolete, instead use:"
+ @echo " make VARIANT=PROFILE"
+ @echo
+
+.PHONY: profile
+
+preinstall-am: $(PREINSTALL_FILES)
+preinstall: preinstall-am
+.PHONY: preinstall preinstall-am
+
+depend-am:
+depend: depend-am
+.PHONY: depend depend-am
+
+${ARCH}:
+ mkdir ${ARCH}
+
+clean-local:
+ $(RM) -r o-optimize o-debug o-profile $(CLEANDIRS)
+ $(RM) Depends-o-optimize.tmp Depends-o-debug.tmp Depends-o-profile.tmp
+
+distclean-local:
+ $(RM) Depends-o-optimize Depends-o-debug Depends-o-profile
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
Index: rstat.1
===================================================================
--- rstat.1 (nonexistent)
+++ rstat.1 (revision 1765)
@@ -0,0 +1,58 @@
+.\" $FreeBSD: src/lib/libc/rpc/rstat.1,v 1.5 1999/08/28 00:00:46 peter Exp $
+.\" @(#)rstat.1 2.1 88/08/03 4.0 RPCSRC
+.TH RSTAT 1 "3 August 1988"
+.SH NAME
+rstat \- remote status display
+.SH SYNOPSIS
+.B rstat
+.B host
+.SH DESCRIPTION
+.LP
+.B rstat
+displays a summary of the current system status of a particular
+.BR host .
+The output shows the current time of day, how long the system has
+been up,
+and the load averages.
+The load average numbers give the number of jobs in the run queue
+averaged over 1, 5 and 15 minutes.
+.PP
+The
+.B rstat_svc(8)
+daemon must be running on the remote host for this command to
+work.
+.B rstat
+uses an RPC protocol defined in /usr/include/rpcsvc/rstat.x.
+.SH EXAMPLE
+.RS
+.ft B
+.nf
+example% rstat otherhost
+7:36am up 6 days, 16:45, load average: 0.20, 0.23, 0.18
+example%
+.ft R
+.fi
+.RE
+.SH DIAGNOSTICS
+.LP
+rstat: RPC: Program not registered
+.IP
+The
+.B rstat_svc
+daemon has not been started on the remote host.
+.LP
+rstat: RPC: Timed out
+.IP
+A communication error occurred. Either the network is
+excessively congested, or the
+.B rstat_svc
+daemon has terminated on the remote host.
+.LP
+rstat: RPC: Port mapper failure - RPC: Timed out
+.IP
+The remote host is not running the portmapper (see
+.BR portmap(8) ),
+and cannot accommodate any RPC-based services. The host may be down.
+.SH "SEE ALSO"
+.BR portmap (8),
+.BR rstat_svc (8)
Index: svc_auth.c
===================================================================
--- svc_auth.c (nonexistent)
+++ svc_auth.c (revision 1765)
@@ -0,0 +1,216 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986-1991 by Sun Microsystems Inc.
+ */
+
+/* #ident "@(#)svc_auth.c 1.16 94/04/24 SMI" */
+
+#if !defined(lint) && defined(SCCSIDS)
+#if 0
+static char sccsid[] = "@(#)svc_auth.c 1.26 89/02/07 Copyr 1984 Sun Micro";
+#else
+static const char rcsid[] =
+ "$FreeBSD: src/lib/libc/rpc/svc_auth.c,v 1.7 1999/12/29 05:04:16 peter Exp $";
+#endif
+#endif
+
+/*
+ * svc_auth.c, Server-side rpc authenticator interface.
+ *
+ */
+
+#ifdef _KERNEL
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#else
+#include
+#include
+#endif
+#include
+
+/*
+ * svcauthsw is the bdevsw of server side authentication.
+ *
+ * Server side authenticators are called from authenticate by
+ * using the client auth struct flavor field to index into svcauthsw.
+ * The server auth flavors must implement a routine that looks
+ * like:
+ *
+ * enum auth_stat
+ * flavorx_auth(rqst, msg)
+ * register struct svc_req *rqst;
+ * register struct rpc_msg *msg;
+ *
+ */
+
+enum auth_stat _svcauth_null(); /* no authentication */
+enum auth_stat _svcauth_unix(); /* (system) unix style (uid, gids) */
+enum auth_stat _svcauth_short(); /* short hand unix style */
+enum auth_stat _svcauth_des(); /* des style */
+
+/* declarations to allow servers to specify new authentication flavors */
+struct authsvc {
+ int flavor;
+ enum auth_stat (*handler)();
+ struct authsvc *next;
+};
+#define Auths ((struct authsvc *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_auths_Auths)
+
+/*
+ * The call rpc message, msg has been obtained from the wire. The msg contains
+ * the raw form of credentials and verifiers. authenticate returns AUTH_OK
+ * if the msg is successfully authenticated. If AUTH_OK then the routine also
+ * does the following things:
+ * set rqst->rq_xprt->verf to the appropriate response verifier;
+ * sets rqst->rq_client_cred to the "cooked" form of the credentials.
+ *
+ * NB: rqst->rq_cxprt->verf must be pre-alloctaed;
+ * its length is set appropriately.
+ *
+ * The caller still owns and is responsible for msg->u.cmb.cred and
+ * msg->u.cmb.verf. The authentication system retains ownership of
+ * rqst->rq_client_cred, the cooked credentials.
+ *
+ * There is an assumption that any flavour less than AUTH_NULL is
+ * invalid.
+ */
+enum auth_stat
+_authenticate(rqst, msg)
+ register struct svc_req *rqst;
+ struct rpc_msg *msg;
+{
+ register int cred_flavor;
+ register struct authsvc *asp;
+
+ rqst->rq_cred = msg->rm_call.cb_cred;
+ rqst->rq_xprt->xp_verf.oa_flavor = _null_auth.oa_flavor;
+ rqst->rq_xprt->xp_verf.oa_length = 0;
+ cred_flavor = rqst->rq_cred.oa_flavor;
+ switch (cred_flavor) {
+ case AUTH_NULL:
+ return(_svcauth_null(rqst, msg));
+ case AUTH_UNIX:
+ return(_svcauth_unix(rqst, msg));
+ case AUTH_SHORT:
+ return(_svcauth_short(rqst, msg));
+ /*
+ * We leave AUTH_DES turned off by default because svcauth_des()
+ * needs getpublickey(), which is in librpcsvc, not libc. If we
+ * included AUTH_DES as a built-in flavor, programs that don't
+ * have -lrpcsvc in their Makefiles wouldn't link correctly, even
+ * though they don't use AUTH_DES. And I'm too lazy to go through
+ * the tree looking for all of them.
+ */
+#ifdef DES_BUILTIN
+ case AUTH_DES:
+ return(_svcauth_des(rqst, msg));
+#endif
+ }
+
+ /* flavor doesn't match any of the builtin types, so try new ones */
+ for (asp = Auths; asp; asp = asp->next) {
+ if (asp->flavor == cred_flavor) {
+ enum auth_stat as;
+
+ as = (*asp->handler)(rqst, msg);
+ return (as);
+ }
+ }
+
+ return (AUTH_REJECTEDCRED);
+}
+
+/*ARGSUSED*/
+enum auth_stat
+_svcauth_null(rqst, msg)
+ struct svc_req *rqst;
+ struct rpc_msg *msg;
+{
+ return (AUTH_OK);
+}
+
+/*
+ * Allow the rpc service to register new authentication types that it is
+ * prepared to handle. When an authentication flavor is registered,
+ * the flavor is checked against already registered values. If not
+ * registered, then a new Auths entry is added on the list.
+ *
+ * There is no provision to delete a registration once registered.
+ *
+ * This routine returns:
+ * 0 if registration successful
+ * 1 if flavor already registered
+ * -1 if can't register (errno set)
+ */
+
+int
+svc_auth_reg(cred_flavor, handler)
+ register int cred_flavor;
+ enum auth_stat (*handler)();
+{
+ register struct authsvc *asp;
+
+ switch (cred_flavor) {
+ case AUTH_NULL:
+ case AUTH_UNIX:
+ case AUTH_SHORT:
+#ifdef DES_BUILTIN
+ case AUTH_DES:
+#endif
+ /* already registered */
+ return (1);
+
+ default:
+ for (asp = Auths; asp; asp = asp->next) {
+ if (asp->flavor == cred_flavor) {
+ /* already registered */
+ return (1);
+ }
+ }
+
+ /* this is a new one, so go ahead and register it */
+ asp = (struct authsvc *)mem_alloc(sizeof (*asp));
+ if (asp == NULL) {
+ return (-1);
+ }
+ asp->flavor = cred_flavor;
+ asp->handler = handler;
+ asp->next = Auths;
+ Auths = asp;
+ break;
+ }
+ return (0);
+}
Index: pmap_clnt.c
===================================================================
--- pmap_clnt.c (nonexistent)
+++ pmap_clnt.c (revision 1765)
@@ -0,0 +1,149 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)pmap_clnt.c 1.37 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)pmap_clnt.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/pmap_clnt.c,v 1.11 2000/01/27 23:06:39 jasone Exp $";
+#endif
+
+/*
+ * pmap_clnt.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static struct timeval timeout = { 5, 0 };
+static struct timeval tottimeout = { 60, 0 };
+
+void clnt_perror();
+
+#ifndef PORTMAPSOCK
+#define PORTMAPSOCK "/var/run/portmapsock"
+#endif
+
+/*
+ * Set a mapping between program,version and port.
+ * Calls the pmap service remotely to do the mapping.
+ */
+bool_t
+pmap_set(program, version, protocol, port)
+ u_long program;
+ u_long version;
+ int protocol;
+ int port; /* was u_short but changed to match prototype */
+{
+ struct sockaddr_in myaddress;
+ int socket = -1;
+ register CLIENT *client;
+ struct pmap parms;
+ bool_t rslt;
+ struct stat st;
+
+ /*
+ * Temporary hack for backwards compatibility. Eventually
+ * this test will go away and we'll use only the "unix" transport.
+ */
+ if (stat(PORTMAPSOCK, &st) == 0 && st.st_mode & S_IFSOCK)
+ client = clnt_create(PORTMAPSOCK, PMAPPROG, PMAPVERS, "unix");
+ else {
+ if (get_myaddress(&myaddress) != 0)
+ return (FALSE);
+ myaddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
+ timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ }
+
+ if (client == (CLIENT *)NULL)
+ return (FALSE);
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = port;
+ if (CLNT_CALL(client, PMAPPROC_SET, xdr_pmap, &parms, xdr_bool, &rslt,
+ tottimeout) != RPC_SUCCESS) {
+ clnt_perror(client, "Cannot register service");
+ return (FALSE);
+ }
+ CLNT_DESTROY(client);
+ if (socket != -1)
+ (void)_RPC_close(socket);
+ return (rslt);
+}
+
+/*
+ * Remove the mapping between program,version and port.
+ * Calls the pmap service remotely to do the un-mapping.
+ */
+bool_t
+pmap_unset(program, version)
+ u_long program;
+ u_long version;
+{
+ struct sockaddr_in myaddress;
+ int socket = -1;
+ register CLIENT *client;
+ struct pmap parms;
+ bool_t rslt;
+ struct stat st;
+
+ /*
+ * Temporary hack for backwards compatibility. Eventually
+ * this test will go away and we'll use only the "unix" transport.
+ */
+ if (stat(PORTMAPSOCK, &st) == 0 && st.st_mode & S_IFSOCK)
+ client = clnt_create(PORTMAPSOCK, PMAPPROG, PMAPVERS, "unix");
+ else {
+ if (get_myaddress(&myaddress) != 0)
+ return (FALSE);
+ myaddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ client = clntudp_bufcreate(&myaddress, PMAPPROG, PMAPVERS,
+ timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ }
+ if (client == (CLIENT *)NULL)
+ return (FALSE);
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_port = parms.pm_prot = 0;
+ CLNT_CALL(client, PMAPPROC_UNSET, xdr_pmap, &parms, xdr_bool, &rslt,
+ tottimeout);
+ CLNT_DESTROY(client);
+ if (socket != -1)
+ (void)_RPC_close(socket);
+ return (rslt);
+}
Index: svc_run.c
===================================================================
--- svc_run.c (nonexistent)
+++ svc_run.c (revision 1765)
@@ -0,0 +1,83 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc_run.c 1.1 87/10/13 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc_run.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc_run.c,v 1.10 1999/08/28 00:00:49 peter Exp $";
+#endif
+
+/*
+ * This is the rpc server side idle loop
+ * Wait for input, call server program.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+void
+svc_run()
+{
+ fd_set *fds;
+
+ for (;;) {
+ if (__svc_fdset) {
+ int bytes = sizeof (fd_set);
+ fds = (fd_set *)malloc(bytes);
+ memcpy(fds, __svc_fdset, bytes);
+ } else
+ fds = NULL;
+ switch (select(svc_maxfd + 1, fds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ if (fds)
+ free(fds);
+ continue;
+ }
+ perror("svc_run: - select failed");
+ if (fds)
+ free(fds);
+ return;
+ case 0:
+ if (fds)
+ free(fds);
+ continue;
+ default:
+ /* if fds == NULL, select() can't return a result */
+ svc_getreqset2(fds, svc_maxfd + 1);
+ free(fds);
+ }
+ }
+}
Index: svc_auth_unix.c
===================================================================
--- svc_auth_unix.c (nonexistent)
+++ svc_auth_unix.c (revision 1765)
@@ -0,0 +1,148 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc_auth_unix.c 1.28 88/02/08 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc_auth_unix.c 2.3 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc_auth_unix.c,v 1.8 1999/08/28 00:00:49 peter Exp $";
+#endif
+
+/*
+ * svc_auth_unix.c
+ * Handles UNIX flavor authentication parameters on the service side of rpc.
+ * There are two svc auth implementations here: AUTH_UNIX and AUTH_SHORT.
+ * _svcauth_unix does full blown unix style uid,gid+gids auth,
+ * _svcauth_short uses a shorthand auth to index into a cache of longhand auths.
+ * Note: the shorthand has been gutted for efficiency.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+
+/*
+ * Unix longhand authenticator
+ */
+enum auth_stat
+_svcauth_unix(rqst, msg)
+ register struct svc_req *rqst;
+ register struct rpc_msg *msg;
+{
+ register enum auth_stat stat;
+ XDR xdrs;
+ register struct authunix_parms *aup;
+ register int32_t *buf;
+ struct area {
+ struct authunix_parms area_aup;
+ char area_machname[MAX_MACHINE_NAME+1];
+ int area_gids[NGRPS];
+ } *area;
+ u_int auth_len;
+ int str_len, gid_len;
+ register int i;
+
+ area = (struct area *) rqst->rq_clntcred;
+ aup = &area->area_aup;
+ aup->aup_machname = area->area_machname;
+ aup->aup_gids = area->area_gids;
+ auth_len = (u_int)msg->rm_call.cb_cred.oa_length;
+ xdrmem_create(&xdrs, msg->rm_call.cb_cred.oa_base, auth_len,XDR_DECODE);
+ buf = XDR_INLINE(&xdrs, auth_len);
+ if (buf != NULL) {
+ aup->aup_time = IXDR_GET_LONG(buf);
+ str_len = IXDR_GET_U_LONG(buf);
+ if (str_len > MAX_MACHINE_NAME) {
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+ memcpy(aup->aup_machname, (caddr_t)buf, (u_int)str_len);
+ aup->aup_machname[str_len] = 0;
+ str_len = RNDUP(str_len);
+ buf += str_len / sizeof (int32_t);
+ aup->aup_uid = IXDR_GET_LONG(buf);
+ aup->aup_gid = IXDR_GET_LONG(buf);
+ gid_len = IXDR_GET_U_LONG(buf);
+ if (gid_len > NGRPS) {
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+ aup->aup_len = gid_len;
+ for (i = 0; i < gid_len; i++) {
+ aup->aup_gids[i] = IXDR_GET_LONG(buf);
+ }
+ /*
+ * five is the smallest unix credentials structure -
+ * timestamp, hostname len (0), uid, gid, and gids len (0).
+ */
+ if ((5 + gid_len) * BYTES_PER_XDR_UNIT + str_len > auth_len) {
+ (void) printf("bad auth_len gid %d str %d auth %d\n",
+ gid_len, str_len, auth_len);
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+ } else if (! xdr_authunix_parms(&xdrs, aup)) {
+ xdrs.x_op = XDR_FREE;
+ (void)xdr_authunix_parms(&xdrs, aup);
+ stat = AUTH_BADCRED;
+ goto done;
+ }
+
+ /* get the verifier */
+ if ((u_int)msg->rm_call.cb_verf.oa_length) {
+ rqst->rq_xprt->xp_verf.oa_flavor =
+ msg->rm_call.cb_verf.oa_flavor;
+ rqst->rq_xprt->xp_verf.oa_base =
+ msg->rm_call.cb_verf.oa_base;
+ rqst->rq_xprt->xp_verf.oa_length =
+ msg->rm_call.cb_verf.oa_length;
+ } else {
+ rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL;
+ rqst->rq_xprt->xp_verf.oa_length = 0;
+ }
+ stat = AUTH_OK;
+done:
+ XDR_DESTROY(&xdrs);
+ return (stat);
+}
+
+
+/*
+ * Shorthand unix authenticator
+ * Looks up longhand in a cache.
+ */
+/*ARGSUSED*/
+enum auth_stat
+_svcauth_short(rqst, msg)
+ struct svc_req *rqst;
+ struct rpc_msg *msg;
+{
+ return (AUTH_REJECTEDCRED);
+}
Index: rtime.c
===================================================================
--- rtime.c (nonexistent)
+++ rtime.c (revision 1765)
@@ -0,0 +1,157 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * Copyright (c) 1988 by Sun Microsystems, Inc.
+
+ */
+
+/*
+ * rtime - get time from remote machine
+ *
+ * gets time, obtaining value from host
+ * on the udp/time socket. Since timeserver returns
+ * with time of day in seconds since Jan 1, 1900, must
+ * subtract seconds before Jan 1, 1970 to get
+ * what unix uses.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/* from: static char sccsid[] = "@(#)rtime.c 2.2 88/08/10 4.0 RPCSRC; from 1.8 88/02/08 SMI"; */
+static const char rcsid[] = "$FreeBSD: src/lib/libc/rpc/rtime.c,v 1.5 2000/01/27 23:06:41 jasone Exp $";
+#endif
+
+extern int _rpc_dtablesize __P(( void ));
+
+#define NYEARS (unsigned long)(1970 - 1900)
+#define TOFFSET (unsigned long)(60*60*24*(365*NYEARS + (NYEARS/4)))
+
+static void do_close __P(( int ));
+
+int
+rtime(addrp, timep, timeout)
+ struct sockaddr_in *addrp;
+ struct timeval *timep;
+ struct timeval *timeout;
+{
+ int s;
+ fd_set readfds;
+ int res;
+ unsigned long thetime;
+ struct sockaddr_in from;
+ int fromlen;
+ int type;
+ struct servent *serv;
+
+ if (timeout == NULL) {
+ type = SOCK_STREAM;
+ } else {
+ type = SOCK_DGRAM;
+ }
+ s = socket(AF_INET, type, 0);
+ if (s < 0) {
+ return(-1);
+ }
+ addrp->sin_family = AF_INET;
+
+ /* TCP and UDP port are the same in this case */
+ if ((serv = getservbyname("time", "tcp")) == NULL) {
+ return(-1);
+ }
+
+ addrp->sin_port = serv->s_port;
+
+ if (type == SOCK_DGRAM) {
+ res = sendto(s, (char *)&thetime, sizeof(thetime), 0,
+ (struct sockaddr *)addrp, sizeof(*addrp));
+ if (res < 0) {
+ do_close(s);
+ return(-1);
+ }
+ do {
+ FD_ZERO(&readfds);
+ FD_SET(s, &readfds);
+ res = select(_rpc_dtablesize(), &readfds,
+ (fd_set *)NULL, (fd_set *)NULL, timeout);
+ } while (res < 0 && errno == EINTR);
+ if (res <= 0) {
+ if (res == 0) {
+ errno = ETIMEDOUT;
+ }
+ do_close(s);
+ return(-1);
+ }
+ fromlen = sizeof(from);
+ res = recvfrom(s, (char *)&thetime, sizeof(thetime), 0,
+ (struct sockaddr *)&from, &fromlen);
+ do_close(s);
+ if (res < 0) {
+ return(-1);
+ }
+ } else {
+ if (connect(s, (struct sockaddr *)addrp, sizeof(*addrp)) < 0) {
+ do_close(s);
+ return(-1);
+ }
+ res = _RPC_read(s, (char *)&thetime, sizeof(thetime));
+ do_close(s);
+ if (res < 0) {
+ return(-1);
+ }
+ }
+ if (res != sizeof(thetime)) {
+ errno = EIO;
+ return(-1);
+ }
+ thetime = ntohl(thetime);
+ timep->tv_sec = thetime - TOFFSET;
+ timep->tv_usec = 0;
+ return(0);
+}
+
+static void
+do_close(s)
+ int s;
+{
+ int save;
+
+ save = errno;
+ (void)_RPC_close(s);
+ errno = save;
+}
Index: bindresvport.c
===================================================================
--- bindresvport.c (nonexistent)
+++ bindresvport.c (revision 1765)
@@ -0,0 +1,147 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)bindresvport.c 1.8 88/02/08 SMI";*/
+/*static char *sccsid = "from: @(#)bindresvport.c 2.2 88/07/29 4.0 RPCSRC";*/
+/*from: OpenBSD: bindresvport.c,v 1.7 1996/07/30 16:25:47 downsj Exp */
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/bindresvport.c,v 1.12 2000/01/26 09:02:42 shin Exp $";
+#endif
+
+/*
+ * Copyright (c) 1987 by Sun Microsystems, Inc.
+ *
+ * Portions Copyright(C) 1996, Jason Downs. All rights reserved.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Bind a socket to a privileged port for whatever protocol.
+ */
+int
+bindresvport_sa(sd, sa)
+ int sd;
+ struct sockaddr *sa;
+{
+ int old, error, af;
+ struct sockaddr myaddr;
+ struct sockaddr_in *sin;
+#if (defined(AF_INET6) && defined(IPPROTO_IPV6))
+ struct sockaddr_in6 *sin6;
+#endif
+ int proto, portrange, portlow;
+ u_int16_t port;
+ int salen;
+
+ if (sa == NULL) {
+ salen = sizeof(myaddr);
+ sa = (struct sockaddr *)&myaddr;
+
+ if (getsockname(sd, sa, &salen) == -1)
+ return -1; /* errno is correctly set */
+
+ af = sa->sa_family;
+ memset(&myaddr, 0, salen);
+ } else
+ af = sa->sa_family;
+
+ if (af == AF_INET) {
+ proto = IPPROTO_IP;
+ portrange = IP_PORTRANGE;
+ portlow = IP_PORTRANGE_LOW;
+ sin = (struct sockaddr_in *)sa;
+ salen = sizeof(struct sockaddr_in);
+ port = sin->sin_port;
+#if (defined(AF_INET6) && defined(IPPROTO_IPV6))
+ } else if (af == AF_INET6) {
+ proto = IPPROTO_IPV6;
+ portrange = IPV6_PORTRANGE;
+ portlow = IPV6_PORTRANGE_LOW;
+ sin6 = (struct sockaddr_in6 *)sa;
+ salen = sizeof(struct sockaddr_in6);
+ port = sin6->sin6_port;
+#endif
+ } else {
+ errno = EPFNOSUPPORT;
+ return (-1);
+ }
+ sa->sa_family = af;
+ sa->sa_len = salen;
+
+ if (port == 0) {
+ int oldlen = sizeof(old);
+
+ error = getsockopt(sd, proto, portrange, &old, &oldlen);
+ if (error < 0)
+ return (error);
+
+ error = setsockopt(sd, proto, portrange, &portlow,
+ sizeof(portlow));
+ if (error < 0)
+ return (error);
+ }
+
+ error = bind(sd, sa, salen);
+
+ if (port == 0) {
+ int saved_errno = errno;
+
+ if (error) {
+ if (setsockopt(sd, proto, portrange, &old,
+ sizeof(old)) < 0)
+ errno = saved_errno;
+ return (error);
+ }
+
+ if (sa != (struct sockaddr *)&myaddr) {
+ /* Hmm, what did the kernel assign... */
+ if (getsockname(sd, sa, &salen) < 0)
+ errno = saved_errno;
+ return (error);
+ }
+ }
+ return (error);
+}
+
+/*
+ * Bind a socket to a privileged IP port
+ */
+int
+bindresvport(sd, sin)
+ int sd;
+ struct sockaddr_in *sin;
+{
+ return bindresvport_sa(sd, (struct sockaddr *)sin);
+}
Index: pmap_prot2.c
===================================================================
--- pmap_prot2.c (nonexistent)
+++ pmap_prot2.c (revision 1765)
@@ -0,0 +1,118 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)pmap_prot2.c 1.3 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)pmap_prot2.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/pmap_prot2.c,v 1.7 1999/08/28 00:00:42 peter Exp $";
+#endif
+
+/*
+ * pmap_prot2.c
+ * Protocol for the local binder service, or pmap.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+
+
+/*
+ * What is going on with linked lists? (!)
+ * First recall the link list declaration from pmap_prot.h:
+ *
+ * struct pmaplist {
+ * struct pmap pml_map;
+ * struct pmaplist *pml_map;
+ * };
+ *
+ * Compare that declaration with a corresponding xdr declaration that
+ * is (a) pointer-less, and (b) recursive:
+ *
+ * typedef union switch (bool_t) {
+ *
+ * case TRUE: struct {
+ * struct pmap;
+ * pmaplist_t foo;
+ * };
+ *
+ * case FALSE: struct {};
+ * } pmaplist_t;
+ *
+ * Notice that the xdr declaration has no nxt pointer while
+ * the C declaration has no bool_t variable. The bool_t can be
+ * interpreted as ``more data follows me''; if FALSE then nothing
+ * follows this bool_t; if TRUE then the bool_t is followed by
+ * an actual struct pmap, and then (recursively) by the
+ * xdr union, pamplist_t.
+ *
+ * This could be implemented via the xdr_union primitive, though this
+ * would cause a one recursive call per element in the list. Rather than do
+ * that we can ``unwind'' the recursion
+ * into a while loop and do the union arms in-place.
+ *
+ * The head of the list is what the C programmer wishes to past around
+ * the net, yet is the data that the pointer points to which is interesting;
+ * this sounds like a job for xdr_reference!
+ */
+bool_t
+xdr_pmaplist(xdrs, rp)
+ register XDR *xdrs;
+ register struct pmaplist **rp;
+{
+ /*
+ * more_elements is pre-computed in case the direction is
+ * XDR_ENCODE or XDR_FREE. more_elements is overwritten by
+ * xdr_bool when the direction is XDR_DECODE.
+ */
+ bool_t more_elements;
+ register int freeing = (xdrs->x_op == XDR_FREE);
+ register struct pmaplist **next = NULL;
+
+ while (TRUE) {
+ more_elements = (bool_t)(*rp != NULL);
+ if (! xdr_bool(xdrs, &more_elements))
+ return (FALSE);
+ if (! more_elements)
+ return (TRUE); /* we are done */
+ /*
+ * the unfortunate side effect of non-recursion is that in
+ * the case of freeing we must remember the next object
+ * before we free the current object ...
+ */
+ if (freeing)
+ next = &((*rp)->pml_next);
+ if (! xdr_reference(xdrs, (caddr_t *)rp,
+ (u_int)sizeof(struct pmaplist), xdr_pmap))
+ return (FALSE);
+ rp = (freeing) ? next : &((*rp)->pml_next);
+ }
+}
Index: rpc_commondata.c
===================================================================
--- rpc_commondata.c (nonexistent)
+++ rpc_commondata.c (revision 1765)
@@ -0,0 +1,41 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)rpc_commondata.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/rpc_commondata.c,v 1.7 1999/08/28 00:00:45 peter Exp $";
+#endif
+
+#include
+/*
+ * This file should only contain common data (global data) that is exported
+ * by public interfaces
+ */
+struct opaque_auth _null_auth;
+struct rpc_createerr rpc_createerr;
Index: getpublickey.c
===================================================================
--- getpublickey.c (nonexistent)
+++ getpublickey.c (revision 1765)
@@ -0,0 +1,172 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)publickey.c 1.10 91/03/11 Copyr 1986 Sun Micro";
+#endif
+
+/*
+ * publickey.c
+ * Copyright (C) 1986, Sun Microsystems, Inc.
+ */
+
+/*
+ * Public key lookup routines
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define PKFILE "/etc/publickey"
+
+/*
+ * Hack to let ypserv/rpc.nisd use AUTH_DES.
+ */
+int (*__getpublickey_LOCAL)() = 0;
+
+/*
+ * Get somebody's public key
+ */
+int
+__getpublickey_real(netname, publickey)
+ char *netname;
+ char *publickey;
+{
+ char lookup[3 * HEXKEYBYTES];
+ char *p;
+
+ if (publickey == NULL)
+ return (0);
+ if (!getpublicandprivatekey(netname, lookup))
+ return (0);
+ p = strchr(lookup, ':');
+ if (p == NULL) {
+ return (0);
+ }
+ *p = '\0';
+ (void) strncpy(publickey, lookup, HEXKEYBYTES);
+ publickey[HEXKEYBYTES] = '\0';
+ return (1);
+}
+
+/*
+ * reads the file /etc/publickey looking for a + to optionally go to the
+ * yellow pages
+ */
+
+int
+getpublicandprivatekey(key, ret)
+ char *key;
+ char *ret;
+{
+ char buf[1024]; /* big enough */
+ char *res;
+ FILE *fd;
+ char *mkey;
+ char *mval;
+
+ fd = fopen(PKFILE, "r");
+ if (fd == (FILE *) 0)
+ return (0);
+ for (;;) {
+ res = fgets(buf, 1024, fd);
+ if (res == 0) {
+ fclose(fd);
+ return (0);
+ }
+ if (res[0] == '#')
+ continue;
+ else if (res[0] == '+') {
+#ifdef YP
+ char *PKMAP = "publickey.byname";
+ char *lookup;
+ char *domain;
+ int err;
+ int len;
+
+ err = yp_get_default_domain(&domain);
+ if (err) {
+ continue;
+ }
+ lookup = NULL;
+ err = yp_match(domain, PKMAP, key, strlen(key), &lookup, &len);
+ if (err) {
+#ifdef DEBUG
+ fprintf(stderr, "match failed error %d\n", err);
+#endif
+ continue;
+ }
+ lookup[len] = 0;
+ strcpy(ret, lookup);
+ fclose(fd);
+ free(lookup);
+ return (2);
+#else /* YP */
+#ifdef DEBUG
+ fprintf(stderr,
+"Bad record in %s '+' -- NIS not supported in this library copy\n", PKFILE);
+#endif /* DEBUG */
+ continue;
+#endif /* YP */
+ } else {
+ mkey = strtok(buf, "\t ");
+ if (mkey == NULL) {
+ fprintf(stderr,
+ "Bad record in %s -- %s", PKFILE, buf);
+ continue;
+ }
+ mval = strtok((char *)NULL, " \t#\n");
+ if (mval == NULL) {
+ fprintf(stderr,
+ "Bad record in %s val problem - %s", PKFILE, buf);
+ continue;
+ }
+ if (strcmp(mkey, key) == 0) {
+ strcpy(ret, mval);
+ fclose(fd);
+ return (1);
+ }
+ }
+ }
+}
+
+int getpublickey(netname, publickey)
+ char *netname;
+ char *publickey;
+{
+ if (__getpublickey_LOCAL != NULL)
+ return(__getpublickey_LOCAL(netname, publickey));
+ else
+ return(__getpublickey_real(netname, publickey));
+}
Index: clnt_perror.c
===================================================================
--- clnt_perror.c (nonexistent)
+++ clnt_perror.c (revision 1765)
@@ -0,0 +1,254 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)clnt_perror.c 1.15 87/10/07 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)clnt_perror.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_perror.c,v 1.11 1999/08/28 00:00:35 peter Exp $";
+#endif
+
+/*
+ * clnt_perror.c
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static char *auth_errmsg();
+#define CLNT_PERROR_BUFLEN 256
+
+#define buf ((char *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->clnt_perror_buf)
+
+static char *
+_buf()
+{
+
+ if (buf == 0)
+ buf = (char *)malloc(CLNT_PERROR_BUFLEN);
+ return (buf);
+}
+
+/*
+ * Print reply error info
+ */
+char *
+clnt_sperror(rpch, s)
+ CLIENT *rpch;
+ char *s;
+{
+ struct rpc_err e;
+ char *err;
+ char *str = _buf();
+ char *strstart = str;
+
+ if (str == 0)
+ return (0);
+ CLNT_GETERR(rpch, &e);
+
+ (void) sprintf(str, "%s: %s", s, clnt_sperrno(e.re_status));
+ str += strlen(str);
+
+ switch (e.re_status) {
+ case RPC_SUCCESS:
+ case RPC_CANTENCODEARGS:
+ case RPC_CANTDECODERES:
+ case RPC_TIMEDOUT:
+ case RPC_PROGUNAVAIL:
+ case RPC_PROCUNAVAIL:
+ case RPC_CANTDECODEARGS:
+ case RPC_SYSTEMERROR:
+ case RPC_UNKNOWNHOST:
+ case RPC_UNKNOWNPROTO:
+ case RPC_PMAPFAILURE:
+ case RPC_PROGNOTREGISTERED:
+ case RPC_FAILED:
+ break;
+
+ case RPC_CANTSEND:
+ case RPC_CANTRECV:
+ (void) snprintf(str, CLNT_PERROR_BUFLEN - (str - strstart),
+ "; errno = %s\n", strerror(e.re_errno));
+ break;
+
+ case RPC_VERSMISMATCH:
+ (void) sprintf(str,
+ "; low version = %lu, high version = %lu\n",
+ (u_long)e.re_vers.low, (u_long)e.re_vers.high);
+ break;
+
+ case RPC_AUTHERROR:
+ err = auth_errmsg(e.re_why);
+ (void) sprintf(str,"; why = ");
+ str += strlen(str);
+ if (err != NULL) {
+ (void) sprintf(str, "%s\n",err);
+ } else {
+ (void) sprintf(str,
+ "(unknown authentication error - %d)\n",
+ (int) e.re_why);
+ }
+ break;
+
+ case RPC_PROGVERSMISMATCH:
+ (void) sprintf(str,
+ "; low version = %lu, high version = %lu\n",
+ (u_long)e.re_vers.low, (u_long)e.re_vers.high);
+ break;
+
+ default: /* unknown */
+ (void) sprintf(str,
+ "; s1 = %lu, s2 = %lu\n",
+ (long)e.re_lb.s1, (long)e.re_lb.s2);
+ break;
+ }
+ strstart[CLNT_PERROR_BUFLEN-2] = '\n';
+ strstart[CLNT_PERROR_BUFLEN-1] = '\0';
+ return(strstart) ;
+}
+
+void
+clnt_perror(rpch, s)
+ CLIENT *rpch;
+ char *s;
+{
+ (void) fprintf(stderr,"%s\n",clnt_sperror(rpch,s));
+}
+
+
+static const char *const rpc_errlist[] = {
+ "RPC: Success", /* 0 - RPC_SUCCESS */
+ "RPC: Can't encode arguments", /* 1 - RPC_CANTENCODEARGS */
+ "RPC: Can't decode result", /* 2 - RPC_CANTDECODERES */
+ "RPC: Unable to send", /* 3 - RPC_CANTSEND */
+ "RPC: Unable to receive", /* 4 - RPC_CANTRECV */
+ "RPC: Timed out", /* 5 - RPC_TIMEDOUT */
+ "RPC: Incompatible versions of RPC", /* 6 - RPC_VERSMISMATCH */
+ "RPC: Authentication error", /* 7 - RPC_AUTHERROR */
+ "RPC: Program unavailable", /* 8 - RPC_PROGUNAVAIL */
+ "RPC: Program/version mismatch", /* 9 - RPC_PROGVERSMISMATCH */
+ "RPC: Procedure unavailable", /* 10 - RPC_PROCUNAVAIL */
+ "RPC: Server can't decode arguments", /* 11 - RPC_CANTDECODEARGS */
+ "RPC: Remote system error", /* 12 - RPC_SYSTEMERROR */
+ "RPC: Unknown host", /* 13 - RPC_UNKNOWNHOST */
+ "RPC: Port mapper failure", /* 14 - RPC_PMAPFAILURE */
+ "RPC: Program not registered", /* 15 - RPC_PROGNOTREGISTERED */
+ "RPC: Failed (unspecified error)", /* 16 - RPC_FAILED */
+ "RPC: Unknown protocol" /* 17 - RPC_UNKNOWNPROTO */
+};
+
+
+/*
+ * This interface for use by clntrpc
+ */
+char *
+clnt_sperrno(stat)
+ enum clnt_stat stat;
+{
+ unsigned int errnum = stat;
+
+ if (errnum < (sizeof(rpc_errlist)/sizeof(rpc_errlist[0])))
+ return (char *)rpc_errlist[errnum];
+
+ return ("RPC: (unknown error code)");
+}
+
+void
+clnt_perrno(num)
+ enum clnt_stat num;
+{
+ (void) fprintf(stderr,"%s\n",clnt_sperrno(num));
+}
+
+
+char *
+clnt_spcreateerror(s)
+ char *s;
+{
+ char *str = _buf();
+
+ if (str == 0)
+ return(0);
+ switch (rpc_createerr.cf_stat) {
+ case RPC_PMAPFAILURE:
+ (void) snprintf(str, CLNT_PERROR_BUFLEN, "%s: %s - %s\n", s,
+ clnt_sperrno(rpc_createerr.cf_stat),
+ clnt_sperrno(rpc_createerr.cf_error.re_status));
+ break;
+
+ case RPC_SYSTEMERROR:
+ (void) snprintf(str, CLNT_PERROR_BUFLEN, "%s: %s - %s\n", s,
+ clnt_sperrno(rpc_createerr.cf_stat),
+ strerror(rpc_createerr.cf_error.re_errno));
+ break;
+ default:
+ (void) snprintf(str, CLNT_PERROR_BUFLEN, "%s: %s\n", s,
+ clnt_sperrno(rpc_createerr.cf_stat));
+ break;
+ }
+ str[CLNT_PERROR_BUFLEN-2] = '\n';
+ str[CLNT_PERROR_BUFLEN-1] = '\0';
+ return (str);
+}
+
+void
+clnt_pcreateerror(s)
+ char *s;
+{
+ (void) fprintf(stderr,"%s\n",clnt_spcreateerror(s));
+}
+
+static const char *const auth_errlist[] = {
+ "Authentication OK", /* 0 - AUTH_OK */
+ "Invalid client credential", /* 1 - AUTH_BADCRED */
+ "Server rejected credential", /* 2 - AUTH_REJECTEDCRED */
+ "Invalid client verifier", /* 3 - AUTH_BADVERF */
+ "Server rejected verifier", /* 4 - AUTH_REJECTEDVERF */
+ "Client credential too weak", /* 5 - AUTH_TOOWEAK */
+ "Invalid server verifier", /* 6 - AUTH_INVALIDRESP */
+ "Failed (unspecified error)" /* 7 - AUTH_FAILED */
+};
+
+static char *
+auth_errmsg(stat)
+ enum auth_stat stat;
+{
+ unsigned int errnum = stat;
+
+ if (errnum < (sizeof(auth_errlist)/sizeof(auth_errlist[0])))
+ return (char *)auth_errlist[errnum];
+
+ return(NULL);
+}
Index: svc.c
===================================================================
--- svc.c (nonexistent)
+++ svc.c (revision 1765)
@@ -0,0 +1,491 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc.c 1.44 88/02/08 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc.c 2.4 88/08/11 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc.c,v 1.14 1999/08/28 00:00:48 peter Exp $";
+#endif
+
+/*
+ * svc.c, Server-side remote procedure call interface.
+ *
+ * There are two sets of procedures here. The xprt routines are
+ * for handling transport handles. The svc routines handle the
+ * list of service routines.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#define xports ((SVCXPRT **)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_xports)
+#define xportssize (((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_xportssize)
+
+#define NULL_SVC ((struct svc_callout *)0)
+#define RQCRED_SIZE 400 /* this size is excessive */
+
+#define max(a, b) (a > b ? a : b)
+
+/*
+ * The services list
+ * Each entry represents a set of procedures (an rpc program).
+ * The dispatch routine takes request structs and runs the
+ * apropriate procedure.
+ */
+struct svc_callout {
+ struct svc_callout *sc_next;
+ u_long sc_prog;
+ u_long sc_vers;
+ void (*sc_dispatch)();
+};
+#define svc_head (struct svc_callout *)(((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_svc_head)
+
+static struct svc_callout *svc_find();
+
+/* *************** SVCXPRT related stuff **************** */
+
+/*
+ * Activate a transport handle.
+ */
+void
+xprt_register(xprt)
+ SVCXPRT *xprt;
+{
+ register int sock = xprt->xp_sock;
+
+ if (sock + 1 > __svc_fdsetsize) {
+ int bytes = sizeof (fd_set);
+ fd_set *fds;
+
+ fds = (fd_set *)malloc(bytes);
+ memset(fds, 0, bytes);
+ if (__svc_fdset) {
+ memcpy(fds, __svc_fdset, bytes);
+ free(__svc_fdset);
+ }
+ __svc_fdset = fds;
+ __svc_fdsetsize = bytes * NBBY;
+ }
+
+ if (sock < FD_SETSIZE)
+ FD_SET(sock, &svc_fdset);
+ FD_SET(sock, __svc_fdset);
+
+ if (xports == NULL || sock + 1 > xportssize) {
+ SVCXPRT **xp;
+ int size = FD_SETSIZE;
+
+ if (sock + 1 > size)
+ size = sock + 1;
+ xp = (SVCXPRT **)mem_alloc(size * sizeof(SVCXPRT *));
+ memset(xp, 0, size * sizeof(SVCXPRT *));
+ if (xports) {
+ memcpy(xp, xports, xportssize * sizeof(SVCXPRT *));
+ free(xports);
+ }
+ xportssize = size;
+ xports = xp;
+ }
+ xports[sock] = xprt;
+ svc_maxfd = max(svc_maxfd, sock);
+}
+
+/*
+ * De-activate a transport handle.
+ */
+void
+xprt_unregister(xprt)
+ SVCXPRT *xprt;
+{
+ register int sock = xprt->xp_sock;
+
+ if (xports[sock] == xprt) {
+ xports[sock] = (SVCXPRT *)0;
+ if (sock < FD_SETSIZE)
+ FD_CLR(sock, &svc_fdset);
+ FD_CLR(sock, __svc_fdset);
+ if (sock == svc_maxfd) {
+ for (svc_maxfd--; svc_maxfd >= 0; svc_maxfd--)
+ if (xports[svc_maxfd])
+ break;
+ }
+ /*
+ * XXX could use svc_maxfd as a hint to
+ * decrease the size of __svc_fdset
+ */
+ }
+}
+
+
+/* ********************** CALLOUT list related stuff ************* */
+
+/*
+ * Add a service program to the callout list.
+ * The dispatch routine will be called when a rpc request for this
+ * program number comes in.
+ */
+bool_t
+svc_register(xprt, prog, vers, dispatch, protocol)
+ SVCXPRT *xprt;
+ u_long prog;
+ u_long vers;
+ void (*dispatch)();
+ int protocol;
+{
+ struct svc_callout *prev;
+ register struct svc_callout *s;
+
+ if ((s = svc_find(prog, vers, &prev)) != NULL_SVC) {
+ if (s->sc_dispatch == dispatch)
+ goto pmap_it; /* he is registering another xptr */
+ return (FALSE);
+ }
+ s = (struct svc_callout *)mem_alloc(sizeof(struct svc_callout));
+ if (s == (struct svc_callout *)0) {
+ return (FALSE);
+ }
+ s->sc_prog = prog;
+ s->sc_vers = vers;
+ s->sc_dispatch = dispatch;
+ s->sc_next = svc_head;
+ svc_head = s;
+pmap_it:
+ /* now register the information with the local binder service */
+ if (protocol) {
+ return (pmap_set(prog, vers, protocol, xprt->xp_port));
+ }
+ return (TRUE);
+}
+
+/*
+ * Remove a service program from the callout list.
+ */
+void
+svc_unregister(prog, vers)
+ u_long prog;
+ u_long vers;
+{
+ struct svc_callout *prev;
+ register struct svc_callout *s;
+
+ if ((s = svc_find(prog, vers, &prev)) == NULL_SVC)
+ return;
+ if (prev == NULL_SVC) {
+ svc_head = s->sc_next;
+ } else {
+ prev->sc_next = s->sc_next;
+ }
+ s->sc_next = NULL_SVC;
+ mem_free((char *) s, (u_int) sizeof(struct svc_callout));
+ /* now unregister the information with the local binder service */
+ (void)pmap_unset(prog, vers);
+}
+
+/*
+ * Search the callout list for a program number, return the callout
+ * struct.
+ */
+static struct svc_callout *
+svc_find(prog, vers, prev)
+ u_long prog;
+ u_long vers;
+ struct svc_callout **prev;
+{
+ register struct svc_callout *s, *p;
+
+ p = NULL_SVC;
+ for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
+ if ((s->sc_prog == prog) && (s->sc_vers == vers))
+ goto done;
+ p = s;
+ }
+done:
+ *prev = p;
+ return (s);
+}
+
+/* ******************* REPLY GENERATION ROUTINES ************ */
+
+/*
+ * Send a reply to an rpc request
+ */
+bool_t
+svc_sendreply(xprt, xdr_results, xdr_location)
+ register SVCXPRT *xprt;
+ xdrproc_t xdr_results;
+ caddr_t xdr_location;
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = SUCCESS;
+ rply.acpted_rply.ar_results.where = xdr_location;
+ rply.acpted_rply.ar_results.proc = xdr_results;
+ return (SVC_REPLY(xprt, &rply));
+}
+
+/*
+ * No procedure error reply
+ */
+void
+svcerr_noproc(xprt)
+ register SVCXPRT *xprt;
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = PROC_UNAVAIL;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Can't decode args error reply
+ */
+void
+svcerr_decode(xprt)
+ register SVCXPRT *xprt;
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = GARBAGE_ARGS;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Some system error
+ */
+void
+svcerr_systemerr(xprt)
+ register SVCXPRT *xprt;
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = SYSTEM_ERR;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Authentication error reply
+ */
+void
+svcerr_auth(xprt, why)
+ SVCXPRT *xprt;
+ enum auth_stat why;
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_DENIED;
+ rply.rjcted_rply.rj_stat = AUTH_ERROR;
+ rply.rjcted_rply.rj_why = why;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Auth too weak error reply
+ */
+void
+svcerr_weakauth(xprt)
+ SVCXPRT *xprt;
+{
+
+ svcerr_auth(xprt, AUTH_TOOWEAK);
+}
+
+/*
+ * Program unavailable error reply
+ */
+void
+svcerr_noprog(xprt)
+ register SVCXPRT *xprt;
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = PROG_UNAVAIL;
+ SVC_REPLY(xprt, &rply);
+}
+
+/*
+ * Program version mismatch error reply
+ */
+void
+svcerr_progvers(xprt, low_vers, high_vers)
+ register SVCXPRT *xprt;
+ u_long low_vers;
+ u_long high_vers;
+{
+ struct rpc_msg rply;
+
+ rply.rm_direction = REPLY;
+ rply.rm_reply.rp_stat = MSG_ACCEPTED;
+ rply.acpted_rply.ar_verf = xprt->xp_verf;
+ rply.acpted_rply.ar_stat = PROG_MISMATCH;
+ rply.acpted_rply.ar_vers.low = low_vers;
+ rply.acpted_rply.ar_vers.high = high_vers;
+ SVC_REPLY(xprt, &rply);
+}
+
+/* ******************* SERVER INPUT STUFF ******************* */
+
+/*
+ * Get server side input from some transport.
+ *
+ * Statement of authentication parameters management:
+ * This function owns and manages all authentication parameters, specifically
+ * the "raw" parameters (msg.rm_call.cb_cred and msg.rm_call.cb_verf) and
+ * the "cooked" credentials (rqst->rq_clntcred).
+ * However, this function does not know the structure of the cooked
+ * credentials, so it make the following assumptions:
+ * a) the structure is contiguous (no pointers), and
+ * b) the cred structure size does not exceed RQCRED_SIZE bytes.
+ * In all events, all three parameters are freed upon exit from this routine.
+ * The storage is trivially management on the call stack in user land, but
+ * is mallocated in kernel land.
+ */
+
+void
+svc_getreq(rdfds)
+ int rdfds;
+{
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ readfds.fds_bits[0] = rdfds;
+ svc_getreqset(&readfds);
+}
+
+void
+svc_getreqset(readfds)
+ fd_set *readfds;
+{
+ svc_getreqset2(readfds, FD_SETSIZE);
+}
+
+void
+svc_getreqset2(readfds, width)
+ fd_set *readfds;
+ int width;
+{
+ enum xprt_stat stat;
+ struct rpc_msg msg;
+ int prog_found;
+ u_long low_vers;
+ u_long high_vers;
+ struct svc_req r;
+ register SVCXPRT *xprt;
+ register int bit;
+ register int sock;
+ register fd_mask mask, *maskp;
+ char cred_area[2*MAX_AUTH_BYTES + RQCRED_SIZE];
+ msg.rm_call.cb_cred.oa_base = cred_area;
+ msg.rm_call.cb_verf.oa_base = &(cred_area[MAX_AUTH_BYTES]);
+ r.rq_clntcred = &(cred_area[2*MAX_AUTH_BYTES]);
+
+
+ maskp = readfds->fds_bits;
+ for (sock = 0; sock < width; sock += NFDBITS) {
+ for (mask = *maskp++; (bit = ffs(mask)); mask ^= (1 << (bit - 1))) {
+ /* sock has input waiting */
+ xprt = xports[sock + bit - 1];
+ if (xprt == NULL)
+ /* But do we control sock? */
+ continue;
+ /* now receive msgs from xprtprt (support batch calls) */
+ do {
+ if (SVC_RECV(xprt, &msg)) {
+
+ /* now find the exported program and call it */
+ register struct svc_callout *s;
+ enum auth_stat why;
+
+ r.rq_xprt = xprt;
+ r.rq_prog = msg.rm_call.cb_prog;
+ r.rq_vers = msg.rm_call.cb_vers;
+ r.rq_proc = msg.rm_call.cb_proc;
+ r.rq_cred = msg.rm_call.cb_cred;
+ /* first authenticate the message */
+ if ((why= _authenticate(&r, &msg)) != AUTH_OK) {
+ svcerr_auth(xprt, why);
+ goto call_done;
+ }
+ /* now match message with a registered service*/
+ prog_found = FALSE;
+ low_vers = (u_long) - 1;
+ high_vers = 0;
+ for (s = svc_head; s != NULL_SVC; s = s->sc_next) {
+ if (s->sc_prog == r.rq_prog) {
+ if (s->sc_vers == r.rq_vers) {
+ (*s->sc_dispatch)(&r, xprt);
+ goto call_done;
+ } /* found correct version */
+ prog_found = TRUE;
+ if (s->sc_vers < low_vers)
+ low_vers = s->sc_vers;
+ if (s->sc_vers > high_vers)
+ high_vers = s->sc_vers;
+ } /* found correct program */
+ }
+ /*
+ * if we got here, the program or version
+ * is not served ...
+ */
+ if (prog_found)
+ svcerr_progvers(xprt,
+ low_vers, high_vers);
+ else
+ svcerr_noprog(xprt);
+ /* Fall through to ... */
+ }
+ call_done:
+ if ((stat = SVC_STAT(xprt)) == XPRT_DIED){
+ SVC_DESTROY(xprt);
+ break;
+ }
+ } while (stat == XPRT_MOREREQS);
+ }
+ }
+}
Index: rstat_svc.8
===================================================================
--- rstat_svc.8 (nonexistent)
+++ rstat_svc.8 (revision 1765)
@@ -0,0 +1,22 @@
+.\" $FreeBSD: src/lib/libc/rpc/rstat_svc.8,v 1.5 1999/08/28 00:00:47 peter Exp $
+.\" @(#)rstat_svc.8c 2.2 88/08/03 4.0 RPCSRC; from 1.10 87/09/09 SMI
+.TH RSTAT_SVC 8 "24 November 1987"
+.SH NAME
+rstat_svc \- kernel statistics server
+.SH SYNOPSIS
+.B /etc/rstat_svc
+.SH DESCRIPTION
+.LP
+.B rstat_svc
+is a server which returns performance statistics
+obtained from the kernel.
+These statistics are graphically displayed by the Sun Microsystems program,
+.BR perfmeter (1).
+The
+.B rstat_svc
+daemon is normally invoked at boot time through /etc/rc.local.
+.PP
+.B rstat_svc
+uses an RPC protocol defined in /usr/include/rpcsvc/rstat.x.
+.\" .SH "SEE ALSO"
+.\" .BR rstat (1),
Index: crypt_client.c
===================================================================
--- crypt_client.c (nonexistent)
+++ crypt_client.c (revision 1765)
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul . All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/lib/libc/rpc/crypt_client.c,v 1.4 1999/08/28 00:00:37 peter Exp $
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef lint
+static const char rcsid[] = "$FreeBSD: src/lib/libc/rpc/crypt_client.c,v 1.4 1999/08/28 00:00:37 peter Exp $";
+#endif
+
+#ifndef KEYSERVSOCK
+#define KEYSERVSOCK "/var/run/keyservsock"
+#endif
+
+int
+_des_crypt_call(buf, len, dparms)
+ char *buf;
+ int len;
+ struct desparams *dparms;
+{
+ CLIENT *clnt;
+ desresp *result_1;
+ desargs des_crypt_1_arg;
+ int stat;
+
+ clnt = clnt_create(KEYSERVSOCK, CRYPT_PROG, CRYPT_VERS, "unix");
+ if (clnt == (CLIENT *) NULL) {
+ return(DESERR_HWERROR);
+ }
+
+ des_crypt_1_arg.desbuf.desbuf_len = len;
+ des_crypt_1_arg.desbuf.desbuf_val = buf;
+ des_crypt_1_arg.des_dir = dparms->des_dir;
+ des_crypt_1_arg.des_mode = dparms->des_mode;
+ bcopy(dparms->des_ivec, des_crypt_1_arg.des_ivec, 8);
+ bcopy(dparms->des_key, des_crypt_1_arg.des_key, 8);
+
+ result_1 = des_crypt_1(&des_crypt_1_arg, clnt);
+ if (result_1 == (desresp *) NULL) {
+ clnt_destroy(clnt);
+ return(DESERR_HWERROR);
+ }
+
+ stat = result_1->stat;
+
+ if (result_1->stat == DESERR_NONE ||
+ result_1->stat == DESERR_NOHWDEVICE) {
+ bcopy(result_1->desbuf.desbuf_val, buf, len);
+ bcopy(result_1->des_ivec, dparms->des_ivec, 8);
+ }
+
+ clnt_freeres(clnt, xdr_desresp, (char *)result_1);
+ clnt_destroy(clnt);
+
+ return(stat);
+}
Index: pmap_prot.c
===================================================================
--- pmap_prot.c (nonexistent)
+++ pmap_prot.c (revision 1765)
@@ -0,0 +1,59 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)pmap_prot.c 1.17 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)pmap_prot.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/pmap_prot.c,v 1.6 1999/08/28 00:00:42 peter Exp $";
+#endif
+
+/*
+ * pmap_prot.c
+ * Protocol for the local binder service, or pmap.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+
+
+bool_t
+xdr_pmap(xdrs, regs)
+ XDR *xdrs;
+ struct pmap *regs;
+{
+
+ if (xdr_u_long(xdrs, ®s->pm_prog) &&
+ xdr_u_long(xdrs, ®s->pm_vers) &&
+ xdr_u_long(xdrs, ®s->pm_prot))
+ return (xdr_u_long(xdrs, ®s->pm_port));
+ return (FALSE);
+}
Index: svc_udp.c
===================================================================
--- svc_udp.c (nonexistent)
+++ svc_udp.c (revision 1765)
@@ -0,0 +1,480 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc_udp.c,v 1.13 2000/01/27 23:06:41 jasone Exp $";
+#endif
+
+/*
+ * svc_udp.c,
+ * Server side for UDP/IP based RPC. (Does some caching in the hopes of
+ * achieving execute-at-most-once semantics.)
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define rpc_buffer(xprt) ((xprt)->xp_p1)
+#define MAX(a, b) ((a > b) ? a : b)
+
+static bool_t svcudp_recv();
+static bool_t svcudp_reply();
+static enum xprt_stat svcudp_stat();
+static bool_t svcudp_getargs();
+static bool_t svcudp_freeargs();
+static void svcudp_destroy();
+static void cache_set __P((SVCXPRT *, u_long));
+static int cache_get __P((SVCXPRT *, struct rpc_msg *, char **, u_long *));
+
+static struct xp_ops svcudp_op = {
+ svcudp_recv,
+ svcudp_stat,
+ svcudp_getargs,
+ svcudp_reply,
+ svcudp_freeargs,
+ svcudp_destroy
+};
+
+/*
+ * kept in xprt->xp_p2
+ */
+struct svcudp_data {
+ u_int su_iosz; /* byte size of send.recv buffer */
+ u_long su_xid; /* transaction id */
+ XDR su_xdrs; /* XDR handle */
+ char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
+ char * su_cache; /* cached data, NULL if no cache */
+};
+#define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
+
+/*
+ * Usage:
+ * xprt = svcudp_create(sock);
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svcudp_create
+ * binds it to an arbitrary port. In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ * Once *xprt is initialized, it is registered as a transporter;
+ * see (svc.h, xprt_register).
+ * The routines returns NULL if a problem occurred.
+ */
+SVCXPRT *
+svcudp_bufcreate(sock, sendsz, recvsz)
+ register int sock;
+ u_int sendsz, recvsz;
+{
+ bool_t madesock = FALSE;
+ register SVCXPRT *xprt;
+ register struct svcudp_data *su;
+ struct sockaddr_in addr;
+ int len = sizeof(struct sockaddr_in);
+
+ if (sock == RPC_ANYSOCK) {
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ perror("svcudp_create: socket creation problem");
+ return ((SVCXPRT *)NULL);
+ }
+ madesock = TRUE;
+ }
+ memset((char *)&addr, 0, sizeof (addr));
+ addr.sin_len = sizeof(struct sockaddr_in);
+ addr.sin_family = AF_INET;
+ if (bindresvport(sock, &addr)) {
+ addr.sin_port = 0;
+ (void)bind(sock, (struct sockaddr *)&addr, len);
+ }
+ if (getsockname(sock, (struct sockaddr *)&addr, &len) != 0) {
+ perror("svcudp_create - cannot getsockname");
+ if (madesock)
+ (void)_RPC_close(sock);
+ return ((SVCXPRT *)NULL);
+ }
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == NULL) {
+ (void)fprintf(stderr, "svcudp_create: out of memory\n");
+ return (NULL);
+ }
+ su = (struct svcudp_data *)mem_alloc(sizeof(*su));
+ if (su == NULL) {
+ (void)fprintf(stderr, "svcudp_create: out of memory\n");
+ return (NULL);
+ }
+ su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4;
+ if ((rpc_buffer(xprt) = mem_alloc(su->su_iosz)) == NULL) {
+ (void)fprintf(stderr, "svcudp_create: out of memory\n");
+ return (NULL);
+ }
+ xdrmem_create(
+ &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE);
+ su->su_cache = NULL;
+ xprt->xp_p2 = (caddr_t)su;
+ xprt->xp_verf.oa_base = su->su_verfbody;
+ xprt->xp_ops = &svcudp_op;
+ xprt->xp_port = ntohs(addr.sin_port);
+ xprt->xp_sock = sock;
+ xprt_register(xprt);
+ return (xprt);
+}
+
+SVCXPRT *
+svcudp_create(sock)
+ int sock;
+{
+
+ return(svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));
+}
+
+static enum xprt_stat
+svcudp_stat(xprt)
+ SVCXPRT *xprt;
+{
+
+ return (XPRT_IDLE);
+}
+
+static bool_t
+svcudp_recv(xprt, msg)
+ register SVCXPRT *xprt;
+ struct rpc_msg *msg;
+{
+ register struct svcudp_data *su = su_data(xprt);
+ register XDR *xdrs = &(su->su_xdrs);
+ register int rlen;
+ char *reply;
+ u_long replylen;
+
+ again:
+ xprt->xp_addrlen = sizeof(struct sockaddr_in);
+ rlen = recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz,
+ 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen));
+ if (rlen == -1 && errno == EINTR)
+ goto again;
+ if (rlen == -1 || rlen < 4*sizeof(u_int32_t))
+ return (FALSE);
+ xdrs->x_op = XDR_DECODE;
+ XDR_SETPOS(xdrs, 0);
+ if (! xdr_callmsg(xdrs, msg))
+ return (FALSE);
+ su->su_xid = msg->rm_xid;
+ if (su->su_cache != NULL) {
+ if (cache_get(xprt, msg, &reply, &replylen)) {
+ (void) sendto(xprt->xp_sock, reply, (int) replylen, 0,
+ (struct sockaddr *) &xprt->xp_raddr, xprt->xp_addrlen);
+ return (TRUE);
+ }
+ }
+ return (TRUE);
+}
+
+static bool_t
+svcudp_reply(xprt, msg)
+ register SVCXPRT *xprt;
+ struct rpc_msg *msg;
+{
+ register struct svcudp_data *su = su_data(xprt);
+ register XDR *xdrs = &(su->su_xdrs);
+ register int slen;
+ register bool_t stat = FALSE;
+
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, 0);
+ msg->rm_xid = su->su_xid;
+ if (xdr_replymsg(xdrs, msg)) {
+ slen = (int)XDR_GETPOS(xdrs);
+ if (sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0,
+ (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen)
+ == slen) {
+ stat = TRUE;
+ if (su->su_cache && slen >= 0) {
+ cache_set(xprt, (u_long) slen);
+ }
+ }
+ }
+ return (stat);
+}
+
+static bool_t
+svcudp_getargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+
+ return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr));
+}
+
+static bool_t
+svcudp_freeargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+ register XDR *xdrs = &(su_data(xprt)->su_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args)(xdrs, args_ptr));
+}
+
+static void
+svcudp_destroy(xprt)
+ register SVCXPRT *xprt;
+{
+ register struct svcudp_data *su = su_data(xprt);
+
+ xprt_unregister(xprt);
+ (void)_RPC_close(xprt->xp_sock);
+ XDR_DESTROY(&(su->su_xdrs));
+ mem_free(rpc_buffer(xprt), su->su_iosz);
+ mem_free((caddr_t)su, sizeof(struct svcudp_data));
+ mem_free((caddr_t)xprt, sizeof(SVCXPRT));
+}
+
+
+/***********this could be a separate file*********************/
+
+/*
+ * Fifo cache for udp server
+ * Copies pointers to reply buffers into fifo cache
+ * Buffers are sent again if retransmissions are detected.
+ */
+
+#define SPARSENESS 4 /* 75% sparse */
+
+#define CACHE_PERROR(msg) \
+ (void) fprintf(stderr,"%s\n", msg)
+
+#define ALLOC(type, size) \
+ (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
+
+#define BZERO(addr, type, size) \
+ memset((char *) addr, 0, sizeof(type) * (int) (size))
+
+/*
+ * An entry in the cache
+ */
+typedef struct cache_node *cache_ptr;
+struct cache_node {
+ /*
+ * Index into cache is xid, proc, vers, prog and address
+ */
+ u_long cache_xid;
+ u_long cache_proc;
+ u_long cache_vers;
+ u_long cache_prog;
+ struct sockaddr_in cache_addr;
+ /*
+ * The cached reply and length
+ */
+ char * cache_reply;
+ u_long cache_replylen;
+ /*
+ * Next node on the list, if there is a collision
+ */
+ cache_ptr cache_next;
+};
+
+
+
+/*
+ * The entire cache
+ */
+struct udp_cache {
+ u_long uc_size; /* size of cache */
+ cache_ptr *uc_entries; /* hash table of entries in cache */
+ cache_ptr *uc_fifo; /* fifo list of entries in cache */
+ u_long uc_nextvictim; /* points to next victim in fifo list */
+ u_long uc_prog; /* saved program number */
+ u_long uc_vers; /* saved version number */
+ u_long uc_proc; /* saved procedure number */
+ struct sockaddr_in uc_addr; /* saved caller's address */
+};
+
+
+/*
+ * the hashing function
+ */
+#define CACHE_LOC(transp, xid) \
+ (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
+
+
+/*
+ * Enable use of the cache.
+ * Note: there is no disable.
+ */
+int svcudp_enablecache(transp, size)
+ SVCXPRT *transp;
+ u_long size;
+{
+ struct svcudp_data *su = su_data(transp);
+ struct udp_cache *uc;
+
+ if (su->su_cache != NULL) {
+ CACHE_PERROR("enablecache: cache already enabled");
+ return(0);
+ }
+ uc = ALLOC(struct udp_cache, 1);
+ if (uc == NULL) {
+ CACHE_PERROR("enablecache: could not allocate cache");
+ return(0);
+ }
+ uc->uc_size = size;
+ uc->uc_nextvictim = 0;
+ uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS);
+ if (uc->uc_entries == NULL) {
+ CACHE_PERROR("enablecache: could not allocate cache data");
+ return(0);
+ }
+ BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS);
+ uc->uc_fifo = ALLOC(cache_ptr, size);
+ if (uc->uc_fifo == NULL) {
+ CACHE_PERROR("enablecache: could not allocate cache fifo");
+ return(0);
+ }
+ BZERO(uc->uc_fifo, cache_ptr, size);
+ su->su_cache = (char *) uc;
+ return(1);
+}
+
+
+/*
+ * Set an entry in the cache
+ */
+static void
+cache_set(xprt, replylen)
+ SVCXPRT *xprt;
+ u_long replylen;
+{
+ register cache_ptr victim;
+ register cache_ptr *vicp;
+ register struct svcudp_data *su = su_data(xprt);
+ struct udp_cache *uc = (struct udp_cache *) su->su_cache;
+ u_int loc;
+ char *newbuf;
+
+ /*
+ * Find space for the new entry, either by
+ * reusing an old entry, or by mallocing a new one
+ */
+ victim = uc->uc_fifo[uc->uc_nextvictim];
+ if (victim != NULL) {
+ loc = CACHE_LOC(xprt, victim->cache_xid);
+ for (vicp = &uc->uc_entries[loc];
+ *vicp != NULL && *vicp != victim;
+ vicp = &(*vicp)->cache_next)
+ ;
+ if (*vicp == NULL) {
+ CACHE_PERROR("cache_set: victim not found");
+ return;
+ }
+ *vicp = victim->cache_next; /* remote from cache */
+ newbuf = victim->cache_reply;
+ } else {
+ victim = ALLOC(struct cache_node, 1);
+ if (victim == NULL) {
+ CACHE_PERROR("cache_set: victim alloc failed");
+ return;
+ }
+ newbuf = mem_alloc(su->su_iosz);
+ if (newbuf == NULL) {
+ CACHE_PERROR("cache_set: could not allocate new rpc_buffer");
+ return;
+ }
+ }
+
+ /*
+ * Store it away
+ */
+ victim->cache_replylen = replylen;
+ victim->cache_reply = rpc_buffer(xprt);
+ rpc_buffer(xprt) = newbuf;
+ xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_ENCODE);
+ victim->cache_xid = su->su_xid;
+ victim->cache_proc = uc->uc_proc;
+ victim->cache_vers = uc->uc_vers;
+ victim->cache_prog = uc->uc_prog;
+ victim->cache_addr = uc->uc_addr;
+ loc = CACHE_LOC(xprt, victim->cache_xid);
+ victim->cache_next = uc->uc_entries[loc];
+ uc->uc_entries[loc] = victim;
+ uc->uc_fifo[uc->uc_nextvictim++] = victim;
+ uc->uc_nextvictim %= uc->uc_size;
+}
+
+/*
+ * Try to get an entry from the cache
+ * return 1 if found, 0 if not found
+ */
+static int
+cache_get(xprt, msg, replyp, replylenp)
+ SVCXPRT *xprt;
+ struct rpc_msg *msg;
+ char **replyp;
+ u_long *replylenp;
+{
+ u_int loc;
+ register cache_ptr ent;
+ register struct svcudp_data *su = su_data(xprt);
+ register struct udp_cache *uc = (struct udp_cache *) su->su_cache;
+
+# define EQADDR(a1, a2) (memcmp(&a1, &a2, sizeof(a1)) == 0)
+
+ loc = CACHE_LOC(xprt, su->su_xid);
+ for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) {
+ if (ent->cache_xid == su->su_xid &&
+ ent->cache_proc == uc->uc_proc &&
+ ent->cache_vers == uc->uc_vers &&
+ ent->cache_prog == uc->uc_prog &&
+ EQADDR(ent->cache_addr, uc->uc_addr)) {
+ *replyp = ent->cache_reply;
+ *replylenp = ent->cache_replylen;
+ return(1);
+ }
+ }
+ /*
+ * Failed to find entry
+ * Remember a few things so we can do a set later
+ */
+ uc->uc_proc = msg->rm_call.cb_proc;
+ uc->uc_vers = msg->rm_call.cb_vers;
+ uc->uc_prog = msg->rm_call.cb_prog;
+ uc->uc_addr = xprt->xp_raddr;
+ return(0);
+}
+
Index: clnt_unix.c
===================================================================
--- clnt_unix.c (nonexistent)
+++ clnt_unix.c (revision 1765)
@@ -0,0 +1,635 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)clnt_unix.c 1.37 87/10/05 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)clnt_unix.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_unix.c,v 1.5 2000/01/27 23:06:37 jasone Exp $";
+#endif
+
+/*
+ * clnt_unix.c, Implements a AF_UNIX based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * AF_UNIX based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MCALL_MSG_SIZE 24
+
+static int readunix();
+static int writeunix();
+
+static enum clnt_stat clntunix_call();
+static void clntunix_abort();
+static void clntunix_geterr();
+static bool_t clntunix_freeres();
+static bool_t clntunix_control();
+static void clntunix_destroy();
+
+static struct clnt_ops unix_ops = {
+ clntunix_call,
+ clntunix_abort,
+ clntunix_geterr,
+ clntunix_freeres,
+ clntunix_destroy,
+ clntunix_control
+};
+
+struct ct_data {
+ int ct_sock;
+ bool_t ct_closeit;
+ struct timeval ct_wait;
+ bool_t ct_waitset; /* wait set by clnt_control? */
+ struct sockaddr_un ct_addr;
+ struct rpc_err ct_error;
+ char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
+ u_int ct_mpos; /* pos after marshal */
+ XDR ct_xdrs;
+};
+
+/*
+ * Create a client handle for a unix/ip connection.
+ * If *sockp<0, *sockp is set to a newly created TCP socket and it is
+ * connected to raddr. If *sockp non-negative then
+ * raddr is ignored. The rpc/unix package does buffering
+ * similar to stdio, so the client must pick send and receive buffer sizes,];
+ * 0 => use the default.
+ * If raddr->sin_port is 0, then a binder on the remote machine is
+ * consulted for the right port number.
+ * NB: *sockp is copied into a private area.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
+ * something more useful.
+ */
+CLIENT *
+clntunix_create(raddr, prog, vers, sockp, sendsz, recvsz)
+ struct sockaddr_un *raddr;
+ u_long prog;
+ u_long vers;
+ register int *sockp;
+ u_int sendsz;
+ u_int recvsz;
+{
+ CLIENT *h;
+ register struct ct_data *ct = NULL;
+ struct timeval now;
+ struct rpc_msg call_msg;
+ static u_int32_t disrupt;
+ int len;
+
+ if (disrupt == 0)
+ disrupt = (u_int32_t)(long)raddr;
+
+ h = (CLIENT *)mem_alloc(sizeof(*h));
+ if (h == NULL) {
+ (void)fprintf(stderr, "clntunix_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ ct = (struct ct_data *)mem_alloc(sizeof(*ct));
+ if (ct == NULL) {
+ (void)fprintf(stderr, "clntunix_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+
+ /*
+ * If no socket given, open one
+ */
+ if (*sockp < 0) {
+ *sockp = socket(AF_UNIX, SOCK_STREAM, 0);
+ len = strlen(raddr->sun_path) + sizeof(raddr->sun_family) +
+ sizeof(raddr->sun_len) + 1;
+ raddr->sun_len = len;
+ if ((*sockp < 0)
+ || (connect(*sockp, (struct sockaddr *)raddr, len) < 0)) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (*sockp != -1)
+ (void)_RPC_close(*sockp);
+ goto fooy;
+ }
+ ct->ct_closeit = TRUE;
+ } else {
+ ct->ct_closeit = FALSE;
+ }
+
+ /*
+ * Set up private data struct
+ */
+ ct->ct_sock = *sockp;
+ ct->ct_wait.tv_usec = 0;
+ ct->ct_waitset = FALSE;
+ ct->ct_addr = *raddr;
+
+ /*
+ * Initialize call message
+ */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ call_msg.rm_xid = (++disrupt) ^ getpid() ^ now.tv_sec ^ now.tv_usec;
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+
+ /*
+ * pre-serialize the static part of the call msg and stash it away
+ */
+ xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
+ if (ct->ct_closeit) {
+ (void)_RPC_close(*sockp);
+ }
+ goto fooy;
+ }
+ ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
+ XDR_DESTROY(&(ct->ct_xdrs));
+
+ /*
+ * Create a client handle which uses xdrrec for serialization
+ * and authnone for authentication.
+ */
+ xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
+ (caddr_t)ct, readunix, writeunix);
+ h->cl_ops = &unix_ops;
+ h->cl_private = (caddr_t) ct;
+ h->cl_auth = authnone_create();
+ return (h);
+
+fooy:
+ /*
+ * Something goofed, free stuff and barf
+ */
+ if (ct)
+ mem_free((caddr_t)ct, sizeof(struct ct_data));
+ if (h)
+ mem_free((caddr_t)h, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+}
+
+static enum clnt_stat
+clntunix_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
+ register CLIENT *h;
+ u_long proc;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+ xdrproc_t xdr_results;
+ caddr_t results_ptr;
+ struct timeval timeout;
+{
+ register struct ct_data *ct = (struct ct_data *) h->cl_private;
+ register XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ u_long x_id;
+ u_int32_t *msg_x_id = (u_int32_t *)(ct->ct_mcall); /* yuk */
+ register bool_t shipnow;
+ int refreshes = 2;
+
+ if (!ct->ct_waitset) {
+ ct->ct_wait = timeout;
+ }
+
+ shipnow =
+ (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
+ && timeout.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+ xdrs->x_op = XDR_ENCODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ x_id = ntohl(--(*msg_x_id));
+ if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+ (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
+ (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
+ (! (*xdr_args)(xdrs, args_ptr))) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ return (ct->ct_error.re_status);
+ }
+ if (! xdrrec_endofrecord(xdrs, shipnow))
+ return (ct->ct_error.re_status = RPC_CANTSEND);
+ if (! shipnow)
+ return (RPC_SUCCESS);
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
+ return(ct->ct_error.re_status = RPC_TIMEDOUT);
+ }
+
+
+ /*
+ * Keep receiving until we get a valid transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ while (TRUE) {
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = xdr_void;
+ if (! xdrrec_skiprecord(xdrs))
+ return (ct->ct_error.re_status);
+ /* now decode and validate the response header */
+ if (! xdr_replymsg(xdrs, &reply_msg)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ continue;
+ return (ct->ct_error.re_status);
+ }
+ if (reply_msg.rm_xid == x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ _seterr_reply(&reply_msg, &(ct->ct_error));
+ if (ct->ct_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
+ ct->ct_error.re_status = RPC_AUTHERROR;
+ ct->ct_error.re_why = AUTH_INVALIDRESP;
+ } else if (! (*xdr_results)(xdrs, results_ptr)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTDECODERES;
+ }
+ /* free verifier ... */
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else {
+ /* maybe our credentials need to be refreshed ... */
+ if (refreshes-- && AUTH_REFRESH(h->cl_auth))
+ goto call_again;
+ } /* end of unsuccessful completion */
+ return (ct->ct_error.re_status);
+}
+
+static void
+clntunix_geterr(h, errp)
+ CLIENT *h;
+ struct rpc_err *errp;
+{
+ register struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ *errp = ct->ct_error;
+}
+
+static bool_t
+clntunix_freeres(cl, xdr_res, res_ptr)
+ CLIENT *cl;
+ xdrproc_t xdr_res;
+ caddr_t res_ptr;
+{
+ register struct ct_data *ct = (struct ct_data *)cl->cl_private;
+ register XDR *xdrs = &(ct->ct_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+static void
+clntunix_abort()
+{
+}
+
+
+static bool_t
+clntunix_control(cl, request, info)
+ CLIENT *cl;
+ int request;
+ char *info;
+{
+ register struct ct_data *ct = (struct ct_data *)cl->cl_private;
+ register struct timeval *tv;
+ int len;
+
+ switch (request) {
+ case CLSET_FD_CLOSE:
+ ct->ct_closeit = TRUE;
+ break;
+ case CLSET_FD_NCLOSE:
+ ct->ct_closeit = FALSE;
+ break;
+ case CLSET_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ tv = (struct timeval *)info;
+ ct->ct_wait.tv_sec = tv->tv_sec;
+ ct->ct_wait.tv_usec = tv->tv_usec;
+ ct->ct_waitset = TRUE;
+ break;
+ case CLGET_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ *(struct timeval *)info = ct->ct_wait;
+ break;
+ case CLGET_SERVER_ADDR:
+ if (info == NULL)
+ return(FALSE);
+ *(struct sockaddr_un *)info = ct->ct_addr;
+ break;
+ case CLGET_FD:
+ if (info == NULL)
+ return(FALSE);
+ *(int *)info = ct->ct_sock;
+ break;
+ case CLGET_XID:
+ /*
+ * use the knowledge that xid is the
+ * first element in the call structure *.
+ * This will get the xid of the PREVIOUS call
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)ct->ct_mcall);
+ break;
+ case CLSET_XID:
+ /* This will set the xid of the NEXT call */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)ct->ct_mcall = htonl(*(u_long *)info - 1);
+ /* decrement by 1 as clntunix_call() increments once */
+ case CLGET_VERS:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the version number field is the fifth field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
+ 4 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_VERS:
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ case CLGET_PROG:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the program number field is the field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
+ 3 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_PROG:
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ case CLGET_LOCAL_ADDR:
+ len = sizeof(struct sockaddr);
+ if (getsockname(ct->ct_sock, (struct sockaddr *)info, &len) <0)
+ return(FALSE);
+ break;
+ case CLGET_RETRY_TIMEOUT:
+ case CLSET_RETRY_TIMEOUT:
+ case CLGET_SVC_ADDR:
+ case CLSET_SVC_ADDR:
+ case CLSET_PUSH_TIMOD:
+ case CLSET_POP_TIMOD:
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+static void
+clntunix_destroy(h)
+ CLIENT *h;
+{
+ register struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ if (ct->ct_closeit) {
+ (void)_RPC_close(ct->ct_sock);
+ }
+ XDR_DESTROY(&(ct->ct_xdrs));
+ mem_free((caddr_t)ct, sizeof(struct ct_data));
+ mem_free((caddr_t)h, sizeof(CLIENT));
+}
+
+/*
+ * read() and write() are replaced with recvmsg()/sendmsg() so that
+ * we can pass ancillary control data. In this case, the data constists
+ * of credential information which the kernel will fill in for us.
+ * XXX: This code is specific to FreeBSD and will not work on other
+ * platforms without the requisite kernel modifications.
+ */
+struct cmessage {
+ struct cmsghdr cmsg;
+ struct cmsgcred cmcred;
+};
+
+static int __msgread(sock, buf, cnt)
+ int sock;
+ void *buf;
+ size_t cnt;
+{
+ struct iovec iov[1];
+ struct msghdr msg;
+ struct cmessage cm;
+
+ bzero((char *)&cm, sizeof(cm));
+ iov[0].iov_base = buf;
+ iov[0].iov_len = cnt;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = (caddr_t)&cm;
+ msg.msg_controllen = sizeof(struct cmessage);
+ msg.msg_flags = 0;
+
+ return(recvmsg(sock, &msg, 0));
+}
+
+static int __msgwrite(sock, buf, cnt)
+ int sock;
+ void *buf;
+ size_t cnt;
+{
+ struct iovec iov[1];
+ struct msghdr msg;
+ struct cmessage cm;
+
+ bzero((char *)&cm, sizeof(cm));
+ iov[0].iov_base = buf;
+ iov[0].iov_len = cnt;
+
+ cm.cmsg.cmsg_type = SCM_CREDS;
+ cm.cmsg.cmsg_level = SOL_SOCKET;
+ cm.cmsg.cmsg_len = sizeof(struct cmessage);
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = (caddr_t)&cm;
+ msg.msg_controllen = sizeof(struct cmessage);
+ msg.msg_flags = 0;
+
+ return(sendmsg(sock, &msg, 0));
+}
+
+/*
+ * Interface between xdr serializer and unix connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readunix(ct, buf, len)
+ register struct ct_data *ct;
+ caddr_t buf;
+ register int len;
+{
+ fd_set *fds, readfds;
+ struct timeval start, after, duration, delta, tmp, tv;
+ int r, save_errno;
+
+ if (len == 0)
+ return (0);
+
+ if (ct->ct_sock + 1 > FD_SETSIZE) {
+ int bytes = howmany(ct->ct_sock + 1, NFDBITS) * sizeof(fd_mask);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL)
+ return (-1);
+ memset(fds, 0, bytes);
+ } else {
+ fds = &readfds;
+ FD_ZERO(fds);
+ }
+
+ gettimeofday(&start, NULL);
+ delta = ct->ct_wait;
+ while (TRUE) {
+ /* XXX we know the other bits are still clear */
+ FD_SET(ct->ct_sock, fds);
+ tv = delta; /* in case select writes back */
+ r = select(ct->ct_sock+1, fds, NULL, NULL, &tv);
+ save_errno = errno;
+
+ gettimeofday(&after, NULL);
+ timersub(&start, &after, &duration);
+ timersub(&delta, &duration, &tmp);
+ delta = tmp;
+ if (delta.tv_sec < 0 || !timerisset(&delta))
+ r = 0;
+
+ switch (r) {
+ case 0:
+ if (fds != &readfds)
+ free(fds);
+ ct->ct_error.re_status = RPC_TIMEDOUT;
+ return (-1);
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ if (fds != &readfds)
+ free(fds);
+ ct->ct_error.re_status = RPC_CANTRECV;
+ ct->ct_error.re_errno = save_errno;
+ return (-1);
+ }
+ break;
+ }
+ switch (len = __msgread(ct->ct_sock, buf, len)) {
+
+ case 0:
+ /* premature eof */
+ ct->ct_error.re_errno = ECONNRESET;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ len = -1; /* it's really an error */
+ break;
+
+ case -1:
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ break;
+ }
+ return (len);
+}
+
+static int
+writeunix(ct, buf, len)
+ struct ct_data *ct;
+ caddr_t buf;
+ int len;
+{
+ register int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i) {
+ if ((i = __msgwrite(ct->ct_sock, buf, cnt)) == -1) {
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTSEND;
+ return (-1);
+ }
+ }
+ return (len);
+}
Index: rpc.3
===================================================================
--- rpc.3 (nonexistent)
+++ rpc.3 (revision 1765)
@@ -0,0 +1,1767 @@
+.\" @(#)rpc.3n 2.4 88/08/08 4.0 RPCSRC; from 1.19 88/06/24 SMI
+.\" $FreeBSD: src/lib/libc/rpc/rpc.3,v 1.11 2000/03/02 09:13:47 sheldonh Exp $
+.\"
+.TH RPC 3 "16 February 1988"
+.SH NAME
+rpc \- library routines for remote procedure calls
+.SH SYNOPSIS AND DESCRIPTION
+These routines allow C programs to make procedure
+calls on other machines across the network.
+First, the client calls a procedure to send a
+data packet to the server.
+Upon receipt of the packet, the server calls a dispatch routine
+to perform the requested service, and then sends back a
+reply.
+Finally, the procedure call returns to the client.
+.LP
+Routines that are used for Secure RPC (DES authentication) are described in
+.BR rpc_secure (3).
+Secure RPC can be used only if DES encryption is available.
+.LP
+.ft B
+.nf
+.sp .5
+#include
+.fi
+.ft R
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+void
+auth_destroy(auth)
+\s-1AUTH\s0 *auth;
+.fi
+.ft R
+.IP
+A macro that destroys the authentication information associated with
+.IR auth .
+Destruction usually involves deallocation of private data
+structures.
+The use of
+.I auth
+is undefined after calling
+.BR auth_destroy(\|) .
+.br
+.if t .ne 6
+.LP
+.ft B
+.nf
+.sp .5
+\s-1AUTH\s0 *
+authnone_create(\|)
+.fi
+.ft R
+.IP
+Create and returns an
+.SM RPC
+authentication handle that passes nonusable authentication
+information with each remote procedure call.
+This is the
+default authentication used by
+.SM RPC.
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+\s-1AUTH\s0 *
+authunix_create(host, uid, gid, len, aup_gids)
+char *host;
+int uid, gid, len, *aup.gids;
+.fi
+.ft R
+.IP
+Create and return an
+.SM RPC
+authentication handle that contains
+.UX
+authentication information.
+The parameter
+.I host
+is the name of the machine on which the information was
+created;
+.I uid
+is the user's user
+.SM ID ;
+.I gid
+is the user's current group
+.SM ID ;
+.I len
+and
+.I aup_gids
+refer to a counted array of groups to which the user belongs.
+It is easy to impersonate a user.
+.br
+.if t .ne 5
+.LP
+.ft B
+.nf
+.sp .5
+\s-1AUTH\s0 *
+authunix_create_default(\|)
+.fi
+.ft R
+.IP
+Calls
+.B authunix_create(\|)
+with the appropriate parameters.
+.br
+.if t .ne 13
+.LP
+.ft B
+.nf
+.sp .5
+callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
+char *host;
+u_long prognum, versnum, procnum;
+char *in, *out;
+xdrproc_t inproc, outproc;
+.fi
+.ft R
+.IP
+Call the remote procedure associated with
+.IR prognum ,
+.IR versnum ,
+and
+.I procnum
+on the machine,
+.IR host .
+The parameter
+.I in
+is the address of the procedure's argument(s), and
+.I out
+is the address of where to place the result(s);
+.I inproc
+is used to encode the procedure's parameters, and
+.I outproc
+is used to decode the procedure's results.
+This routine returns zero if it succeeds, or the value of
+.B "enum clnt_stat"
+cast to an integer if it fails.
+The routine
+.B clnt_perrno(\|)
+is handy for translating failure statuses into messages.
+.IP
+Warning: calling remote procedures with this routine
+uses
+.SM UDP/IP
+as a transport; see
+.B clntudp_create(\|)
+for restrictions.
+You do not have control of timeouts or authentication using
+this routine.
+.br
+.if t .ne 16
+.LP
+.ft B
+.nf
+.sp .5
+enum clnt_stat
+clnt_broadcast(prognum, versnum, procnum, inproc, in, outproc, out, eachresult)
+u_long prognum, versnum, procnum;
+char *in, *out;
+xdrproc_t inproc, outproc;
+resultproc_t eachresult;
+.fi
+.ft R
+.IP
+Like
+.BR callrpc(\|) ,
+except the call message is broadcast to all locally
+connected broadcast nets.
+Each time it receives a
+response, this routine calls
+.BR eachresult(\|) ,
+whose form is:
+.IP
+.RS 1i
+.ft B
+.nf
+eachresult(out, addr)
+char *out;
+struct sockaddr_in *addr;
+.ft R
+.fi
+.RE
+.IP
+where
+.I out
+is the same as
+.I out
+passed to
+.BR clnt_broadcast(\|) ,
+except that the remote procedure's output is decoded there;
+.I addr
+points to the address of the machine that sent the results.
+If
+.B eachresult(\|)
+returns zero,
+.B clnt_broadcast(\|)
+waits for more replies; otherwise it returns with appropriate
+status.
+.IP
+Warning: broadcast sockets are limited in size to the
+maximum transfer unit of the data link.
+For ethernet,
+this value is 1500 bytes.
+.br
+.if t .ne 13
+.LP
+.ft B
+.nf
+.sp .5
+enum clnt_stat
+clnt_call(clnt, procnum, inproc, in, outproc, out, tout)
+\s-1CLIENT\s0 *clnt;
+u_long
+procnum;
+xdrproc_t inproc, outproc;
+char *in, *out;
+struct timeval tout;
+.fi
+.ft R
+.IP
+A macro that calls the remote procedure
+.I procnum
+associated with the client handle,
+.IR clnt ,
+which is obtained with an
+.SM RPC
+client creation routine such as
+.BR clnt_create(\|) .
+The parameter
+.I in
+is the address of the procedure's argument(s), and
+.I out
+is the address of where to place the result(s);
+.I inproc
+is used to encode the procedure's parameters, and
+.I outproc
+is used to decode the procedure's results;
+.I tout
+is the time allowed for results to come back.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+clnt_destroy(clnt)
+\s-1CLIENT\s0 *clnt;
+.fi
+.ft R
+.IP
+A macro that destroys the client's
+.SM RPC
+handle.
+Destruction usually involves deallocation
+of private data structures, including
+.I clnt
+itself. Use of
+.I clnt
+is undefined after calling
+.BR clnt_destroy(\|) .
+If the
+.SM RPC
+library opened the associated socket, it will close it also.
+Otherwise, the socket remains open.
+.br
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+\s-1CLIENT\s0 *
+clnt_create(host, prog, vers, proto)
+char *host;
+u_long prog, vers;
+char *proto;
+.fi
+.ft R
+.IP
+Generic client creation routine.
+.I host
+identifies the name of the remote host where the server
+is located.
+.I proto
+indicates which kind of transport protocol to use.
+The
+currently supported values for this field are \(lqudp\(rq
+and \(lqtcp\(rq.
+Default timeouts are set, but can be modified using
+.BR clnt_control(\|) .
+.IP
+Warning: Using
+.SM UDP
+has its shortcomings. Since
+.SM UDP\s0-based
+.SM RPC
+messages can only hold up to 8 Kbytes of encoded data,
+this transport cannot be used for procedures that take
+large arguments or return huge results.
+.br
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+bool_t
+clnt_control(cl, req, info)
+\s-1CLIENT\s0 *cl;
+u_int req;
+char *info;
+.fi
+.ft R
+.IP
+A macro used to change or retrieve various information
+about a client object.
+.I req
+indicates the type of operation, and
+.I info
+is a pointer to the information.
+For both
+.SM UDP
+and
+.SM TCP\s0,
+the supported values of
+.I req
+and their argument types and what they do are:
+.IP
+.nf
+.ta +2.0i +2.0i +2.0i
+.SM CLSET_TIMEOUT\s0 struct timeval set total timeout
+.SM CLGET_TIMEOUT\s0 struct timeval get total timeout
+.fi
+.IP
+Note: if you set the timeout using
+.BR clnt_control(\|) ,
+the timeout parameter passed to
+.B clnt_call(\|)
+will be ignored in all future calls.
+.IP
+.nf
+.SM CLGET_SERVER_ADDR\s0 struct sockaddr_in get server's address
+.fi
+.br
+.IP
+The following operations are valid for
+.SM UDP
+only:
+.IP
+.nf
+.ta +2.0i ; +2.0i ; +2.0i
+.SM CLSET_RETRY_TIMEOUT\s0 struct timeval set the retry timeout
+.SM CLGET_RETRY_TIMEOUT\s0 struct timeval get the retry timeout
+.fi
+.br
+.IP
+The retry timeout is the time that
+.SM "UDP RPC"
+waits for the server to reply before
+retransmitting the request.
+.br
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+clnt_freeres(clnt, outproc, out)
+\s-1CLIENT\s0 *clnt;
+xdrproc_t outproc;
+char *out;
+.fi
+.ft R
+.IP
+A macro that frees any data allocated by the
+.SM RPC/XDR
+system when it decoded the results of an
+.SM RPC
+call. The
+parameter
+.I out
+is the address of the results, and
+.I outproc
+is the
+.SM XDR
+routine describing the results.
+This routine returns one if the results were successfully
+freed,
+and zero otherwise.
+.br
+.if t .ne 6
+.LP
+.ft B
+.nf
+.sp .5
+void
+clnt_geterr(clnt, errp)
+\s-1CLIENT\s0 *clnt;
+struct rpc_err *errp;
+.fi
+.ft R
+.IP
+A macro that copies the error structure out of the client
+handle
+to the structure at address
+.IR errp .
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+void
+clnt_pcreateerror(s)
+char *s;
+.fi
+.ft R
+.IP
+Print a message to standard error indicating
+why a client
+.SM RPC
+handle could not be created.
+The message is prepended with string
+.I s
+and a colon.
+Used when a
+.BR clnt_create(\|) ,
+.BR clntraw_create(\|) ,
+.BR clnttcp_create(\|) ,
+or
+.B clntudp_create(\|)
+call fails.
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+void
+clnt_perrno(stat)
+enum clnt_stat stat;
+.fi
+.ft R
+.IP
+Print a message to standard error corresponding
+to the condition indicated by
+.IR stat .
+Used after
+.BR callrpc(\|) .
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+clnt_perror(clnt, s)
+\s-1CLIENT\s0 *clnt;
+char *s;
+.fi
+.ft R
+.IP
+Print a message to standard error indicating why an
+.SM RPC
+call failed;
+.I clnt
+is the handle used to do the call.
+The message is prepended with string
+.I s
+and a colon.
+Used after
+.BR clnt_call(\|) .
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+char *
+clnt_spcreateerror
+char *s;
+.fi
+.ft R
+.IP
+Like
+.BR clnt_pcreateerror(\|) ,
+except that it returns a string
+instead of printing to the standard error.
+.IP
+Bugs: returns pointer to static data that is overwritten
+on each call.
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+char *
+clnt_sperrno(stat)
+enum clnt_stat stat;
+.fi
+.ft R
+.IP
+Take the same arguments as
+.BR clnt_perrno(\|) ,
+but instead of sending a message to the standard error
+indicating why an
+.SM RPC
+call failed, return a pointer to a string which contains
+the message. The string ends with a
+.SM NEWLINE\s0.
+.IP
+.B clnt_sperrno(\|)
+is used instead of
+.B clnt_perrno(\|)
+if the program does not have a standard error (as a program
+running as a server quite likely does not), or if the
+programmer
+does not want the message to be output with
+.BR printf ,
+or if a message format different from that supported by
+.B clnt_perrno(\|)
+is to be used.
+Note: unlike
+.B clnt_sperror(\|)
+and
+.BR clnt_spcreaterror(\|) ,
+.B clnt_sperrno(\|)
+returns pointer to static data, but the
+result will not get overwritten on each call.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+char *
+clnt_sperror(rpch, s)
+\s-1CLIENT\s0 *rpch;
+char *s;
+.fi
+.ft R
+.IP
+Like
+.BR clnt_perror(\|) ,
+except that (like
+.BR clnt_sperrno(\|) )
+it returns a string instead of printing to standard error.
+.IP
+Bugs: returns pointer to static data that is overwritten
+on each call.
+.br
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+\s-1CLIENT\s0 *
+clntraw_create(prognum, versnum)
+u_long prognum, versnum;
+.fi
+.ft R
+.IP
+This routine creates a toy
+.SM RPC
+client for the remote program
+.IR prognum ,
+version
+.IR versnum .
+The transport used to pass messages to the service is
+actually a buffer within the process's address space, so the
+corresponding
+.SM RPC
+server should live in the same address space; see
+.BR svcraw_create(\|) .
+This allows simulation of
+.SM RPC
+and acquisition of
+.SM RPC
+overheads, such as round trip times, without any
+kernel interference.
+This routine returns
+.SM NULL
+if it fails.
+.br
+.if t .ne 15
+.LP
+.ft B
+.nf
+.sp .5
+\s-1CLIENT\s0 *
+clnttcp_create(addr, prognum, versnum, sockp, sendsz, recvsz)
+struct sockaddr_in *addr;
+u_long prognum, versnum;
+int *sockp;
+u_int sendsz, recvsz;
+.fi
+.ft R
+.IP
+This routine creates an
+.SM RPC
+client for the remote program
+.IR prognum ,
+version
+.IR versnum ;
+the client uses
+.SM TCP/IP
+as a transport.
+The remote program is located at Internet
+address
+.IR *addr .
+If
+.\"The following in-line font conversion is necessary for the hyphen indicator
+\fB\%addr\->sin_port\fR
+is zero, then it is set to the actual port that the remote
+program is listening on (the remote
+.B portmap
+service is consulted for this information). The parameter
+.I sockp
+is a socket; if it is
+.BR \s-1RPC_ANYSOCK\s0 ,
+then this routine opens a new one and sets
+.IR sockp .
+Since
+.SM TCP\s0-based
+.SM RPC
+uses buffered
+.SM I/O ,
+the user may specify the size of the send and receive buffers
+with the parameters
+.I sendsz
+and
+.IR recvsz ;
+values of zero choose suitable defaults.
+This routine returns
+.SM NULL
+if it fails.
+.br
+.if t .ne 15
+.LP
+.ft B
+.nf
+.sp .5
+\s-1CLIENT\s0 *
+clntudp_create(addr, prognum, versnum, wait, sockp)
+struct sockaddr_in *addr;
+u_long prognum, versnum;
+struct timeval wait;
+int *sockp;
+.fi
+.ft R
+.IP
+This routine creates an
+.SM RPC
+client for the remote program
+.IR prognum ,
+version
+.IR versnum ;
+the client uses
+.SM UDP/IP
+as a transport.
+The remote program is located at Internet
+address
+.IR addr .
+If
+\fB\%addr\->sin_port\fR
+is zero, then it is set to actual port that the remote
+program is listening on (the remote
+.B portmap
+service is consulted for this information). The parameter
+.I sockp
+is a socket; if it is
+.BR \s-1RPC_ANYSOCK\s0 ,
+then this routine opens a new one and sets
+.IR sockp .
+The
+.SM UDP
+transport resends the call message in intervals of
+.B wait
+time until a response is received or until the call times
+out.
+The total time for the call to time out is specified by
+.BR clnt_call(\|) .
+.IP
+Warning: since
+.SM UDP\s0-based
+.SM RPC
+messages can only hold up to 8 Kbytes
+of encoded data, this transport cannot be used for procedures
+that take large arguments or return huge results.
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+\s-1CLIENT\s0 *
+clntudp_bufcreate(addr, prognum, versnum, wait, sockp, sendsize, recosize)
+struct sockaddr_in *addr;
+u_long prognum, versnum;
+struct timeval wait;
+int *sockp;
+unsigned int sendsize;
+unsigned int recosize;
+.fi
+.ft R
+.IP
+This routine creates an
+.SM RPC
+client for the remote program
+.IR prognum ,
+on
+.IR versnum ;
+the client uses
+.SM UDP/IP
+as a transport.
+The remote program is located at Internet
+address
+.IR addr .
+If
+\fB\%addr\->sin_port\fR
+is zero, then it is set to actual port that the remote
+program is listening on (the remote
+.B portmap
+service is consulted for this information). The parameter
+.I sockp
+is a socket; if it is
+.BR \s-1RPC_ANYSOCK\s0 ,
+then this routine opens a new one and sets
+.BR sockp .
+The
+.SM UDP
+transport resends the call message in intervals of
+.B wait
+time until a response is received or until the call times
+out.
+The total time for the call to time out is specified by
+.BR clnt_call(\|) .
+.IP
+This allows the user to specify the maximum packet size for sending and receiving
+.SM UDP\s0-based
+.SM RPC
+messages.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+int
+get_myaddress(addr)
+struct sockaddr_in *addr;
+.fi
+.ft R
+.IP
+Stuff the machine's
+.SM IP
+address into
+.IR *addr ,
+without consulting the library routines that deal with
+.BR /etc/hosts .
+The port number is always set to
+.BR htons(\s-1PMAPPORT\s0) .
+Returns zero on success, non-zero on failure.
+.br
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+struct pmaplist *
+pmap_getmaps(addr)
+struct sockaddr_in *addr;
+.fi
+.ft R
+.IP
+A user interface to the
+.B portmap
+service, which returns a list of the current
+.SM RPC
+program-to-port mappings
+on the host located at
+.SM IP
+address
+.IR *addr .
+This routine can return
+.SM NULL .
+The command
+.RB ` "rpcinfo \-p" '
+uses this routine.
+.br
+.if t .ne 12
+.LP
+.ft B
+.nf
+.sp .5
+u_short
+pmap_getport(addr, prognum, versnum, protocol)
+struct sockaddr_in *addr;
+u_long prognum, versnum, protocol;
+.fi
+.ft R
+.IP
+A user interface to the
+.B portmap
+service, which returns the port number
+on which waits a service that supports program number
+.IR prognum ,
+version
+.IR versnum ,
+and speaks the transport protocol associated with
+.IR protocol .
+The value of
+.I protocol
+is most likely
+.B
+.SM IPPROTO_UDP
+or
+.BR \s-1IPPROTO_TCP\s0 .
+A return value of zero means that the mapping does not exist
+or that
+the
+.SM RPC
+system failed to contact the remote
+.B portmap
+service. In the latter case, the global variable
+.B rpc_createerr(\|)
+contains the
+.SM RPC
+status.
+.br
+.if t .ne 15
+.LP
+.ft B
+.nf
+.sp .5
+enum clnt_stat
+pmap_rmtcall(addr, prognum, versnum, procnum, inproc, in, outproc, out, tout, portp)
+struct sockaddr_in *addr;
+u_long prognum, versnum, procnum;
+char *in, *out;
+xdrproc_t inproc, outproc;
+struct timeval tout;
+u_long *portp;
+.fi
+.ft R
+.IP
+A user interface to the
+.B portmap
+service, which instructs
+.B portmap
+on the host at
+.SM IP
+address
+.I *addr
+to make an
+.SM RPC
+call on your behalf to a procedure on that host.
+The parameter
+.I *portp
+will be modified to the program's port number if the
+procedure
+succeeds.
+The definitions of other parameters are discussed
+in
+.B callrpc(\|)
+and
+.BR clnt_call(\|) .
+This procedure should be used for a \(lqping\(rq and nothing
+else.
+See also
+.BR clnt_broadcast(\|) .
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+pmap_set(prognum, versnum, protocol, port)
+u_long prognum, versnum, protocol;
+u_short port;
+.fi
+.ft R
+.IP
+A user interface to the
+.B portmap
+service, which establishes a mapping between the triple
+.RI [ prognum , versnum , protocol\fR]
+and
+.I port
+on the machine's
+.B portmap
+service.
+The value of
+.I protocol
+is most likely
+.B
+.SM IPPROTO_UDP
+or
+.BR \s-1IPPROTO_TCP\s0 .
+This routine returns one if it succeeds, zero otherwise.
+Automatically done by
+.BR svc_register(\|) .
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+pmap_unset(prognum, versnum)
+u_long prognum, versnum;
+.fi
+.ft R
+.IP
+A user interface to the
+.B portmap
+service, which destroys all mapping between the triple
+.RI [ prognum , versnum , *\fR]
+and
+.B ports
+on the machine's
+.B portmap
+service.
+This routine returns one if it succeeds, zero
+otherwise.
+.br
+.if t .ne 15
+.LP
+.ft B
+.nf
+.sp .5
+registerrpc(prognum, versnum, procnum, procname, inproc, outproc)
+u_long prognum, versnum, procnum;
+char *(*procname) (\|) ;
+xdrproc_t inproc, outproc;
+.fi
+.ft R
+.IP
+Register procedure
+.I procname
+with the
+.SM RPC
+service package. If a request arrives for program
+.IR prognum ,
+version
+.IR versnum ,
+and procedure
+.IR procnum ,
+.I procname
+is called with a pointer to its parameter(s);
+.I progname
+should return a pointer to its static result(s);
+.I inproc
+is used to decode the parameters while
+.I outproc
+is used to encode the results.
+This routine returns zero if the registration succeeded, \-1
+otherwise.
+.IP
+Warning: remote procedures registered in this form
+are accessed using the
+.SM UDP/IP
+transport; see
+.B svcudp_create(\|)
+for restrictions.
+.br
+.if t .ne 5
+.LP
+.ft B
+.nf
+.sp .5
+struct rpc_createerr rpc_createerr;
+.fi
+.ft R
+.IP
+A global variable whose value is set by any
+.SM RPC
+client creation routine
+that does not succeed. Use the routine
+.B clnt_pcreateerror(\|)
+to print the reason why.
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+svc_destroy(xprt)
+\s-1SVCXPRT\s0 *
+xprt;
+.fi
+.ft R
+.IP
+A macro that destroys the
+.SM RPC
+service transport handle,
+.IR xprt .
+Destruction usually involves deallocation
+of private data structures, including
+.I xprt
+itself. Use of
+.I xprt
+is undefined after calling this routine.
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+fd_set svc_fdset;
+.fi
+.ft R
+.IP
+A global variable reflecting the
+.SM RPC
+service side's
+read file descriptor bit mask; it is suitable as a template parameter
+to the
+.B select
+system call.
+This is only of interest
+if a service implementor does not call
+.BR svc_run(\|) ,
+but rather does his own asynchronous event processing.
+This variable is read-only (do not pass its address to
+.BR select !),
+yet it may change after calls to
+.B svc_getreqset(\|)
+or any creation routines.
+.br
+As well, note that if the process has descriptor limits
+which are extended beyond
+.BR FD_SETSIZE ,
+this variable will only be usable for the first
+.BR FD_SETSIZE
+descriptors.
+.br
+.if t .ne 6
+.LP
+.ft B
+.nf
+.sp .5
+int svc_fds;
+.fi
+.ft R
+.IP
+Similar to
+.BR svc_fedset(\|) ,
+but limited to 32 descriptors.
+This
+interface is obsoleted by
+.BR svc_fdset(\|) .
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+svc_freeargs(xprt, inproc, in)
+\s-1SVCXPRT\s0 *xprt;
+xdrproc_t inproc;
+char *in;
+.fi
+.ft R
+.IP
+A macro that frees any data allocated by the
+.SM RPC/XDR
+system when it decoded the arguments to a service procedure
+using
+.BR svc_getargs(\|) .
+This routine returns 1 if the results were successfully
+freed,
+and zero otherwise.
+.br
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+svc_getargs(xprt, inproc, in)
+\s-1SVCXPRT\s0 *xprt;
+xdrproc_t inproc;
+char *in;
+.fi
+.ft R
+.IP
+A macro that decodes the arguments of an
+.SM RPC
+request
+associated with the
+.SM RPC
+service transport handle,
+.IR xprt .
+The parameter
+.I in
+is the address where the arguments will be placed;
+.I inproc
+is the
+.SM XDR
+routine used to decode the arguments.
+This routine returns one if decoding succeeds, and zero
+otherwise.
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+struct sockaddr_in *
+svc_getcaller(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+The approved way of getting the network address of the caller
+of a procedure associated with the
+.SM RPC
+service transport handle,
+.IR xprt .
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+svc_getreqset(rdfds)
+fd_set *rdfds;
+.fi
+.ft R
+.IP
+This routine is only of interest if a service implementor
+does not call
+.BR svc_run(\|) ,
+but instead implements custom asynchronous event processing.
+It is called when the
+.B select
+system call has determined that an
+.SM RPC
+request has arrived on some
+.SM RPC
+.B socket(s) ;
+.I rdfds
+is the resultant read file descriptor bit mask.
+The routine returns when all sockets associated with the
+value of
+.I rdfds
+have been serviced.
+.br
+.if t .ne 6
+.LP
+.ft B
+.nf
+.sp .5
+svc_getreq(rdfds)
+int rdfds;
+.fi
+.ft R
+.IP
+Similar to
+.BR svc_getreqset(\|) ,
+but limited to 32 descriptors.
+This interface is obsoleted by
+.BR svc_getreqset(\|) .
+.br
+.if t .ne 17
+.LP
+.ft B
+.nf
+.sp .5
+svc_register(xprt, prognum, versnum, dispatch, protocol)
+\s-1SVCXPRT\s0 *xprt;
+u_long prognum, versnum;
+void (*dispatch) (\|);
+u_long protocol;
+.fi
+.ft R
+.IP
+Associates
+.I prognum
+and
+.I versnum
+with the service dispatch procedure,
+.IR dispatch .
+If
+.I protocol
+is zero, the service is not registered with the
+.B portmap
+service. If
+.I protocol
+is non-zero, then a mapping of the triple
+.RI [ prognum , versnum , protocol\fR]
+to
+\fB\%xprt\->xp_port\fR
+is established with the local
+.B portmap
+service (generally
+.I protocol
+is zero,
+.B
+.SM IPPROTO_UDP
+or
+.B
+.SM IPPROTO_TCP
+).
+The procedure
+.I dispatch
+has the following form:
+.RS 1i
+.ft B
+.nf
+dispatch(request, xprt)
+struct svc_req *request;
+\s-1SVCXPRT\s0 *xprt;
+.ft R
+.fi
+.RE
+.IP
+The
+.B svc_register(\|)
+routine returns one if it succeeds, and zero otherwise.
+.br
+.if t .ne 6
+.LP
+.ft B
+.nf
+.sp .5
+svc_run(\|)
+.fi
+.ft R
+.IP
+This routine never returns.
+It waits for
+.SM RPC
+requests to arrive, and calls the appropriate service
+procedure using
+.B svc_getreq(\|)
+when one arrives.
+This procedure is usually waiting for a
+.B select(\|)
+system call to return.
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+svc_sendreply(xprt, outproc, out)
+\s-1SVCXPRT\s0 *xprt;
+xdrproc_t outproc;
+char *out;
+.fi
+.ft R
+.IP
+Called by an
+.SM RPC
+service's dispatch routine to send the results of a
+remote procedure call. The parameter
+.I xprt
+is the request's associated transport handle;
+.I outproc
+is the
+.SM XDR
+routine which is used to encode the results; and
+.I out
+is the address of the results.
+This routine returns one if it succeeds, zero otherwise.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+void
+svc_unregister(prognum, versnum)
+u_long prognum, versnum;
+.fi
+.ft R
+.IP
+Remove all mapping of the double
+.RI [ prognum , versnum ]
+to dispatch routines, and of the triple
+.RI [ prognum , versnum , *\fR]
+to port number.
+.br
+.if t .ne 9
+.LP
+.ft B
+.nf
+.sp .5
+void
+svcerr_auth(xprt, why)
+\s-1SVCXPRT\s0 *xprt;
+enum auth_stat why;
+.fi
+.ft R
+.IP
+Called by a service dispatch routine that refuses to perform
+a remote procedure call due to an authentication error.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+void
+svcerr_decode(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+Called by a service dispatch routine that cannot successfully
+decode its parameters.
+See also
+.BR svc_getargs(\|) .
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+void
+svcerr_noproc(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+Called by a service dispatch routine that does not implement
+the procedure number that the caller requests.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+void
+svcerr_noprog(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+Called when the desired program is not registered with the
+.SM RPC
+package.
+Service implementors usually do not need this routine.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+void
+svcerr_progvers(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+Called when the desired version of a program is not registered
+with the
+.SM RPC
+package.
+Service implementors usually do not need this routine.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+void
+svcerr_systemerr(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+Called by a service dispatch routine when it detects a system
+error
+not covered by any particular protocol.
+For example, if a service can no longer allocate storage,
+it may call this routine.
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+void
+svcerr_weakauth(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+Called by a service dispatch routine that refuses to perform
+a remote procedure call due to insufficient
+authentication parameters. The routine calls
+.BR "svcerr_auth(xprt, \s-1AUTH_TOOWEAK\s0)" .
+.br
+.if t .ne 11
+.LP
+.ft B
+.nf
+.sp .5
+\s-1SVCXPRT\s0 *
+svcraw_create(\|)
+.fi
+.ft R
+.IP
+This routine creates a toy
+.SM RPC
+service transport, to which it returns a pointer. The
+transport
+is really a buffer within the process's address space,
+so the corresponding
+.SM RPC
+client should live in the same
+address space;
+see
+.BR clntraw_create(\|) .
+This routine allows simulation of
+.SM RPC
+and acquisition of
+.SM RPC
+overheads (such as round trip times), without any kernel
+interference.
+This routine returns
+.SM NULL
+if it fails.
+.br
+.if t .ne 11
+.LP
+.ft B
+.nf
+.sp .5
+\s-1SVCXPRT\s0 *
+svctcp_create(sock, send_buf_size, recv_buf_size)
+int sock;
+u_int send_buf_size, recv_buf_size;
+.fi
+.ft R
+.IP
+This routine creates a
+.SM TCP/IP\s0-based
+.SM RPC
+service transport, to which it returns a pointer.
+The transport is associated with the socket
+.IR sock ,
+which may be
+.BR \s-1RPC_ANYSOCK\s0 ,
+in which case a new socket is created.
+If the socket is not bound to a local
+.SM TCP
+port, then this routine binds it to an arbitrary port. Upon
+completion,
+\fB\%xprt\->xp_sock\fR
+is the transport's socket descriptor, and
+\fB\%xprt\->xp_port\fR
+is the transport's port number.
+This routine returns
+.SM NULL
+if it fails.
+Since
+.SM TCP\s0-based
+.SM RPC
+uses buffered
+.SM I/O ,
+users may specify the size of buffers; values of zero
+choose suitable defaults.
+.br
+.if t .ne 11
+.LP
+.ft B
+.nf
+.sp .5
+\s-1SVCXPRT\s0 *
+svcfd_create(fd, sendsize, recvsize)
+int fd;
+u_int sendsize;
+u_int recvsize;
+.fi
+.ft R
+.IP
+Create a service on top of any open descriptor.
+Typically,
+this
+descriptor is a connected socket for a stream protocol such
+as
+.SM TCP\s0.
+.I sendsize
+and
+.I recvsize
+indicate sizes for the send and receive buffers. If they are
+zero, a reasonable default is chosen.
+.br
+.if t .ne 10
+.LP
+.ft B
+.nf
+.sp .5
+\s-1SVCXPRT\s0 *
+svcudp_bufcreate(sock, sendsize, recosize)
+int sock;
+.fi
+.ft R
+.IP
+This routine creates a
+.SM UDP/IP\s0-based
+.SM RPC
+service transport, to which it returns a pointer.
+The transport is associated with the socket
+.IR sock ,
+which may be
+.B \s-1RPC_ANYSOCK\s0 ,
+in which case a new socket is created.
+If the socket is not bound to a local
+.SM UDP
+port, then this routine binds it to an arbitrary port.
+Upon
+completion,
+\fB\%xprt\->xp_sock\fR
+is the transport's socket descriptor, and
+\fB\%xprt\->xp_port\fR
+is the transport's port number.
+This routine returns
+.SM NULL
+if it fails.
+.IP
+This allows the user to specify the maximum packet size for sending and
+receiving
+.SM UDP\s0-based
+.SM RPC messages.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+xdr_accepted_reply(xdrs, ar)
+\s-1XDR\s0 *xdrs;
+struct accepted_reply *ar;
+.fi
+.ft R
+.IP
+Used for encoding
+.SM RPC
+reply messages.
+This routine is useful for users who
+wish to generate
+\s-1RPC\s0-style
+messages without using the
+.SM RPC
+package.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+xdr_authunix_parms(xdrs, aupp)
+\s-1XDR\s0 *xdrs;
+struct authunix_parms *aupp;
+.fi
+.ft R
+.IP
+Used for describing
+.SM UNIX
+credentials.
+This routine is useful for users
+who wish to generate these credentials without using the
+.SM RPC
+authentication package.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+void
+xdr_callhdr(xdrs, chdr)
+\s-1XDR\s0 *xdrs;
+struct rpc_msg *chdr;
+.fi
+.ft R
+.IP
+Used for describing
+.SM RPC
+call header messages.
+This routine is useful for users who wish to generate
+.SM RPC\s0-style
+messages without using the
+.SM RPC
+package.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+xdr_callmsg(xdrs, cmsg)
+\s-1XDR\s0 *xdrs;
+struct rpc_msg *cmsg;
+.fi
+.ft R
+.IP
+Used for describing
+.SM RPC
+call messages.
+This routine is useful for users who wish to generate
+.SM RPC\s0-style
+messages without using the
+.SM RPC
+package.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+xdr_opaque_auth(xdrs, ap)
+\s-1XDR\s0 *xdrs;
+struct opaque_auth *ap;
+.fi
+.ft R
+.IP
+Used for describing
+.SM RPC
+authentication information messages.
+This routine is useful for users who wish to generate
+.SM RPC\s0-style
+messages without using the
+.SM RPC
+package.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+xdr_pmap(xdrs, regs)
+\s-1XDR\s0 *xdrs;
+struct pmap *regs;
+.fi
+.ft R
+.IP
+Used for describing parameters to various
+.B portmap
+procedures, externally.
+This routine is useful for users who wish to generate
+these parameters without using the
+.B pmap
+interface.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+xdr_pmaplist(xdrs, rp)
+\s-1XDR\s0 *xdrs;
+struct pmaplist **rp;
+.fi
+.ft R
+.IP
+Used for describing a list of port mappings, externally.
+This routine is useful for users who wish to generate
+these parameters without using the
+.B pmap
+interface.
+.br
+.if t .ne 7
+.LP
+.ft B
+.nf
+.sp .5
+xdr_rejected_reply(xdrs, rr)
+\s-1XDR\s0 *xdrs;
+struct rejected_reply *rr;
+.fi
+.ft R
+.IP
+Used for describing
+.SM RPC
+reply messages.
+This routine is useful for users who wish to generate
+.SM RPC\s0-style
+messages without using the
+.SM RPC
+package.
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+xdr_replymsg(xdrs, rmsg)
+\s-1XDR\s0 *xdrs;
+struct rpc_msg *rmsg;
+.fi
+.ft R
+.IP
+Used for describing
+.SM RPC
+reply messages.
+This routine is useful for users who wish to generate
+.SM RPC
+style messages without using the
+.SM RPC
+package.
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+void
+xprt_register(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+After
+.SM RPC
+service transport handles are created,
+they should register themselves with the
+.SM RPC
+service package.
+This routine modifies the global variable
+.BR svc_fds(\|) .
+Service implementors usually do not need this routine.
+.br
+.if t .ne 8
+.LP
+.ft B
+.nf
+.sp .5
+void
+xprt_unregister(xprt)
+\s-1SVCXPRT\s0 *xprt;
+.fi
+.ft R
+.IP
+Before an
+.SM RPC
+service transport handle is destroyed,
+it should unregister itself with the
+.SM RPC
+service package.
+This routine modifies the global variable
+.BR svc_fds(\|) .
+Service implementors usually do not need this routine.
+.SH SEE ALSO
+.BR rpc_secure (3),
+.BR xdr (3)
+.br
+The following manuals:
+.RS
+.ft I
+Remote Procedure Calls: Protocol Specification
+.br
+Remote Procedure Call Programming Guide
+.br
+rpcgen Programming Guide
+.br
+.ft R
+.RE
+.IR "\s-1RPC\s0: Remote Procedure Call Protocol Specification" ,
+.SM RFC1050, Sun Microsystems, Inc.,
+.SM USC-ISI\s0.
+
Index: rpc.5
===================================================================
--- rpc.5 (nonexistent)
+++ rpc.5 (revision 1765)
@@ -0,0 +1,35 @@
+.\" $FreeBSD: src/lib/libc/rpc/rpc.5,v 1.6 1999/08/28 00:00:44 peter Exp $
+.\" @(#)rpc.5 2.2 88/08/03 4.0 RPCSRC; from 1.4 87/11/27 SMI;
+.Dd September 26, 1985
+.Dt RPC 5
+.Sh NAME
+.Nm rpc
+.Nd rpc program number data base
+.Sh SYNOPSIS
+/etc/rpc
+.Sh DESCRIPTION
+The
+.Pa /etc/rpc
+file contains user readable names that
+can be used in place of rpc program numbers.
+Each line has the following information:
+.Pp
+.Bl -bullet -compact
+.It
+name of server for the rpc program
+.It
+rpc program number
+.It
+aliases
+.El
+.Pp
+Items are separated by any number of blanks and/or
+tab characters.
+A ``#'' indicates the beginning of a comment; characters up to the end of
+the line are not interpreted by routines which search the file.
+.Sh FILES
+.Bl -tag -compact -width /etc/rpc
+.Pa /etc/rpc
+.El
+.Sh "SEE ALSO"
+.Xr getrpcent 3
Index: des_soft.c
===================================================================
--- des_soft.c (nonexistent)
+++ des_soft.c (revision 1765)
@@ -0,0 +1,67 @@
+#if !defined(lint) && defined(SCCSIDS)
+static char sccsid[] = "@(#)des_soft.c 2.2 88/08/10 4.0 RPCSRC; from 1.13 88/02/08 SMI";
+#endif
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Table giving odd parity in the low bit for ASCII characters
+ */
+static char partab[128] = {
+ 0x01, 0x01, 0x02, 0x02, 0x04, 0x04, 0x07, 0x07,
+ 0x08, 0x08, 0x0b, 0x0b, 0x0d, 0x0d, 0x0e, 0x0e,
+ 0x10, 0x10, 0x13, 0x13, 0x15, 0x15, 0x16, 0x16,
+ 0x19, 0x19, 0x1a, 0x1a, 0x1c, 0x1c, 0x1f, 0x1f,
+ 0x20, 0x20, 0x23, 0x23, 0x25, 0x25, 0x26, 0x26,
+ 0x29, 0x29, 0x2a, 0x2a, 0x2c, 0x2c, 0x2f, 0x2f,
+ 0x31, 0x31, 0x32, 0x32, 0x34, 0x34, 0x37, 0x37,
+ 0x38, 0x38, 0x3b, 0x3b, 0x3d, 0x3d, 0x3e, 0x3e,
+ 0x40, 0x40, 0x43, 0x43, 0x45, 0x45, 0x46, 0x46,
+ 0x49, 0x49, 0x4a, 0x4a, 0x4c, 0x4c, 0x4f, 0x4f,
+ 0x51, 0x51, 0x52, 0x52, 0x54, 0x54, 0x57, 0x57,
+ 0x58, 0x58, 0x5b, 0x5b, 0x5d, 0x5d, 0x5e, 0x5e,
+ 0x61, 0x61, 0x62, 0x62, 0x64, 0x64, 0x67, 0x67,
+ 0x68, 0x68, 0x6b, 0x6b, 0x6d, 0x6d, 0x6e, 0x6e,
+ 0x70, 0x70, 0x73, 0x73, 0x75, 0x75, 0x76, 0x76,
+ 0x79, 0x79, 0x7a, 0x7a, 0x7c, 0x7c, 0x7f, 0x7f,
+};
+
+/*
+ * Add odd parity to low bit of 8 byte key
+ */
+void
+des_setparity(p)
+ char *p;
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ *p = partab[*p & 0x7f];
+ p++;
+ }
+}
Index: svc_auth_des.c
===================================================================
--- svc_auth_des.c (nonexistent)
+++ svc_auth_des.c (revision 1765)
@@ -0,0 +1,531 @@
+
+/*
+ * Copyright (c) 1988 by Sun Microsystems, Inc.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * svcauth_des.c, server-side des authentication
+ *
+ * We insure for the service the following:
+ * (1) The timestamp microseconds do not exceed 1 million.
+ * (2) The timestamp plus the window is less than the current time.
+ * (3) The timestamp is not less than the one previously
+ * seen in the current session.
+ *
+ * It is up to the server to determine if the window size is
+ * too small .
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/* from: static char sccsid[] = "@(#)svcauth_des.c 2.3 89/07/11 4.0 RPCSRC; from 1.15 88/02/08 SMI"; */
+static const char rcsid[] = "$FreeBSD: src/lib/libc/rpc/svc_auth_des.c,v 1.3 1999/08/28 00:00:48 peter Exp $";
+#endif
+
+#define debug(msg) printf("svcauth_des: %s\n", msg)
+
+#define USEC_PER_SEC ((u_long) 1000000L)
+#define BEFORE(t1, t2) timercmp(t1, t2, <)
+
+/*
+ * LRU cache of conversation keys and some other useful items.
+ */
+#define AUTHDES_CACHESZ 64
+struct cache_entry {
+ des_block key; /* conversation key */
+ char *rname; /* client's name */
+ u_int window; /* credential lifetime window */
+ struct timeval laststamp; /* detect replays of creds */
+ char *localcred; /* generic local credential */
+};
+static struct cache_entry *authdes_cache/* [AUTHDES_CACHESZ] */;
+static short *authdes_lru/* [AUTHDES_CACHESZ] */;
+
+static void cache_init(); /* initialize the cache */
+static short cache_spot(); /* find an entry in the cache */
+static void cache_ref(/*short sid*/); /* note that sid was ref'd */
+
+static void invalidate(); /* invalidate entry in cache */
+
+/*
+ * cache statistics
+ */
+static struct {
+ u_long ncachehits; /* times cache hit, and is not replay */
+ u_long ncachereplays; /* times cache hit, and is replay */
+ u_long ncachemisses; /* times cache missed */
+} svcauthdes_stats;
+
+/*
+ * Service side authenticator for AUTH_DES
+ */
+enum auth_stat
+_svcauth_des(rqst, msg)
+ register struct svc_req *rqst;
+ register struct rpc_msg *msg;
+{
+
+ register long *ixdr;
+ des_block cryptbuf[2];
+ register struct authdes_cred *cred;
+ struct authdes_verf verf;
+ int status;
+ register struct cache_entry *entry;
+ short sid = 0;
+ des_block *sessionkey;
+ des_block ivec;
+ u_int window;
+ struct timeval timestamp;
+ u_long namelen;
+ struct area {
+ struct authdes_cred area_cred;
+ char area_netname[MAXNETNAMELEN+1];
+ } *area;
+
+ if (authdes_cache == NULL) {
+ cache_init();
+ }
+
+ area = (struct area *)rqst->rq_clntcred;
+ cred = (struct authdes_cred *)&area->area_cred;
+
+ /*
+ * Get the credential
+ */
+ ixdr = (long *)msg->rm_call.cb_cred.oa_base;
+ cred->adc_namekind = IXDR_GET_ENUM(ixdr, enum authdes_namekind);
+ switch (cred->adc_namekind) {
+ case ADN_FULLNAME:
+ namelen = IXDR_GET_U_LONG(ixdr);
+ if (namelen > MAXNETNAMELEN) {
+ return (AUTH_BADCRED);
+ }
+ cred->adc_fullname.name = area->area_netname;
+ bcopy((char *)ixdr, cred->adc_fullname.name,
+ (u_int)namelen);
+ cred->adc_fullname.name[namelen] = 0;
+ ixdr += (RNDUP(namelen) / BYTES_PER_XDR_UNIT);
+ cred->adc_fullname.key.key.high = (u_long)*ixdr++;
+ cred->adc_fullname.key.key.low = (u_long)*ixdr++;
+ cred->adc_fullname.window = (u_long)*ixdr++;
+ break;
+ case ADN_NICKNAME:
+ cred->adc_nickname = (u_long)*ixdr++;
+ break;
+ default:
+ return (AUTH_BADCRED);
+ }
+
+ /*
+ * Get the verifier
+ */
+ ixdr = (long *)msg->rm_call.cb_verf.oa_base;
+ verf.adv_xtimestamp.key.high = (u_long)*ixdr++;
+ verf.adv_xtimestamp.key.low = (u_long)*ixdr++;
+ verf.adv_int_u = (u_long)*ixdr++;
+
+
+ /*
+ * Get the conversation key
+ */
+ if (cred->adc_namekind == ADN_FULLNAME) {
+ netobj pkey;
+ char pkey_data[1024];
+
+ sessionkey = &cred->adc_fullname.key;
+ if (! getpublickey(cred->adc_fullname.name, pkey_data)) {
+ debug("getpublickey");
+ return(AUTH_BADCRED);
+ }
+ pkey.n_bytes = pkey_data;
+ pkey.n_len = strlen(pkey_data) + 1;
+ if (key_decryptsession_pk(cred->adc_fullname.name, &pkey,
+ sessionkey) < 0) {
+ debug("decryptsessionkey");
+ return (AUTH_BADCRED); /* key not found */
+ }
+ } else { /* ADN_NICKNAME */
+ sid = (short)cred->adc_nickname;
+ if (sid >= AUTHDES_CACHESZ) {
+ debug("bad nickname");
+ return (AUTH_BADCRED); /* garbled credential */
+ }
+ sessionkey = &authdes_cache[sid].key;
+ }
+
+
+ /*
+ * Decrypt the timestamp
+ */
+ cryptbuf[0] = verf.adv_xtimestamp;
+ if (cred->adc_namekind == ADN_FULLNAME) {
+ cryptbuf[1].key.high = cred->adc_fullname.window;
+ cryptbuf[1].key.low = verf.adv_winverf;
+ ivec.key.high = ivec.key.low = 0;
+ status = cbc_crypt((char *)sessionkey, (char *)cryptbuf,
+ 2*sizeof(des_block), DES_DECRYPT | DES_HW,
+ (char *)&ivec);
+ } else {
+ status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
+ sizeof(des_block), DES_DECRYPT | DES_HW);
+ }
+ if (DES_FAILED(status)) {
+ debug("decryption failure");
+ return (AUTH_FAILED); /* system error */
+ }
+
+ /*
+ * XDR the decrypted timestamp
+ */
+ ixdr = (long *)cryptbuf;
+ timestamp.tv_sec = IXDR_GET_LONG(ixdr);
+ timestamp.tv_usec = IXDR_GET_LONG(ixdr);
+
+ /*
+ * Check for valid credentials and verifiers.
+ * They could be invalid because the key was flushed
+ * out of the cache, and so a new session should begin.
+ * Be sure and send AUTH_REJECTED{CRED, VERF} if this is the case.
+ */
+ {
+ struct timeval current;
+ int nick;
+ int winverf;
+
+ if (cred->adc_namekind == ADN_FULLNAME) {
+ window = IXDR_GET_U_LONG(ixdr);
+ winverf = IXDR_GET_U_LONG(ixdr);
+ if (winverf != window - 1) {
+ debug("window verifier mismatch");
+ return (AUTH_BADCRED); /* garbled credential */
+ }
+ sid = cache_spot(sessionkey, cred->adc_fullname.name,
+ ×tamp);
+ if (sid < 0) {
+ debug("replayed credential");
+ return (AUTH_REJECTEDCRED); /* replay */
+ }
+ nick = 0;
+ } else { /* ADN_NICKNAME */
+ window = authdes_cache[sid].window;
+ nick = 1;
+ }
+
+ if ((u_long)timestamp.tv_usec >= USEC_PER_SEC) {
+ debug("invalid usecs");
+ /* cached out (bad key), or garbled verifier */
+ return (nick ? AUTH_REJECTEDVERF : AUTH_BADVERF);
+ }
+ if (nick && BEFORE(×tamp,
+ &authdes_cache[sid].laststamp)) {
+ debug("timestamp before last seen");
+ return (AUTH_REJECTEDVERF); /* replay */
+ }
+ (void) gettimeofday(¤t, (struct timezone *)NULL);
+ current.tv_sec -= window; /* allow for expiration */
+ if (!BEFORE(¤t, ×tamp)) {
+ debug("timestamp expired");
+ /* replay, or garbled credential */
+ return (nick ? AUTH_REJECTEDVERF : AUTH_BADCRED);
+ }
+ }
+
+ /*
+ * Set up the reply verifier
+ */
+ verf.adv_nickname = (u_long)sid;
+
+ /*
+ * xdr the timestamp before encrypting
+ */
+ ixdr = (long *)cryptbuf;
+ IXDR_PUT_LONG(ixdr, timestamp.tv_sec - 1);
+ IXDR_PUT_LONG(ixdr, timestamp.tv_usec);
+
+ /*
+ * encrypt the timestamp
+ */
+ status = ecb_crypt((char *)sessionkey, (char *)cryptbuf,
+ sizeof(des_block), DES_ENCRYPT | DES_HW);
+ if (DES_FAILED(status)) {
+ debug("encryption failure");
+ return (AUTH_FAILED); /* system error */
+ }
+ verf.adv_xtimestamp = cryptbuf[0];
+
+ /*
+ * Serialize the reply verifier, and update rqst
+ */
+ ixdr = (long *)msg->rm_call.cb_verf.oa_base;
+ *ixdr++ = (long)verf.adv_xtimestamp.key.high;
+ *ixdr++ = (long)verf.adv_xtimestamp.key.low;
+ *ixdr++ = (long)verf.adv_int_u;
+
+ rqst->rq_xprt->xp_verf.oa_flavor = AUTH_DES;
+ rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base;
+ rqst->rq_xprt->xp_verf.oa_length =
+ (char *)ixdr - msg->rm_call.cb_verf.oa_base;
+
+ /*
+ * We succeeded, commit the data to the cache now and
+ * finish cooking the credential.
+ */
+ entry = &authdes_cache[sid];
+ entry->laststamp = timestamp;
+ cache_ref(sid);
+ if (cred->adc_namekind == ADN_FULLNAME) {
+ cred->adc_fullname.window = window;
+ cred->adc_nickname = (u_long)sid; /* save nickname */
+ if (entry->rname != NULL) {
+ mem_free(entry->rname, strlen(entry->rname) + 1);
+ }
+ entry->rname = (char *)mem_alloc((u_int)strlen(cred->adc_fullname.name)
+ + 1);
+ if (entry->rname != NULL) {
+ (void) strcpy(entry->rname, cred->adc_fullname.name);
+ } else {
+ debug("out of memory");
+ }
+ entry->key = *sessionkey;
+ entry->window = window;
+ invalidate(entry->localcred); /* mark any cached cred invalid */
+ } else { /* ADN_NICKNAME */
+ /*
+ * nicknames are cooked into fullnames
+ */
+ cred->adc_namekind = ADN_FULLNAME;
+ cred->adc_fullname.name = entry->rname;
+ cred->adc_fullname.key = entry->key;
+ cred->adc_fullname.window = entry->window;
+ }
+ return (AUTH_OK); /* we made it!*/
+}
+
+
+/*
+ * Initialize the cache
+ */
+static void
+cache_init()
+{
+ register int i;
+
+ authdes_cache = (struct cache_entry *)
+ mem_alloc(sizeof(struct cache_entry) * AUTHDES_CACHESZ);
+ bzero((char *)authdes_cache,
+ sizeof(struct cache_entry) * AUTHDES_CACHESZ);
+
+ authdes_lru = (short *)mem_alloc(sizeof(short) * AUTHDES_CACHESZ);
+ /*
+ * Initialize the lru list
+ */
+ for (i = 0; i < AUTHDES_CACHESZ; i++) {
+ authdes_lru[i] = i;
+ }
+}
+
+
+/*
+ * Find the lru victim
+ */
+static short
+cache_victim()
+{
+ return (authdes_lru[AUTHDES_CACHESZ-1]);
+}
+
+/*
+ * Note that sid was referenced
+ */
+static void
+cache_ref(sid)
+ register short sid;
+{
+ register int i;
+ register short curr;
+ register short prev;
+
+ prev = authdes_lru[0];
+ authdes_lru[0] = sid;
+ for (i = 1; prev != sid; i++) {
+ curr = authdes_lru[i];
+ authdes_lru[i] = prev;
+ prev = curr;
+ }
+}
+
+
+/*
+ * Find a spot in the cache for a credential containing
+ * the items given. Return -1 if a replay is detected, otherwise
+ * return the spot in the cache.
+ */
+static short
+cache_spot(key, name, timestamp)
+ register des_block *key;
+ char *name;
+ struct timeval *timestamp;
+{
+ register struct cache_entry *cp;
+ register int i;
+ register u_long hi;
+
+ hi = key->key.high;
+ for (cp = authdes_cache, i = 0; i < AUTHDES_CACHESZ; i++, cp++) {
+ if (cp->key.key.high == hi &&
+ cp->key.key.low == key->key.low &&
+ cp->rname != NULL &&
+ bcmp(cp->rname, name, strlen(name) + 1) == 0) {
+ if (BEFORE(timestamp, &cp->laststamp)) {
+ svcauthdes_stats.ncachereplays++;
+ return (-1); /* replay */
+ }
+ svcauthdes_stats.ncachehits++;
+ return (i); /* refresh */
+ }
+ }
+ svcauthdes_stats.ncachemisses++;
+ return (cache_victim()); /* new credential */
+}
+
+
+#if (defined(sun) || defined(vax) || defined(__FreeBSD__))
+/*
+ * Local credential handling stuff.
+ * NOTE: bsd unix dependent.
+ * Other operating systems should put something else here.
+ */
+#define UNKNOWN -2 /* grouplen, if cached cred is unknown user */
+#define INVALID -1 /* grouplen, if cache entry is invalid */
+
+struct bsdcred {
+ short uid; /* cached uid */
+ short gid; /* cached gid */
+ short grouplen; /* length of cached groups */
+ short groups[NGROUPS]; /* cached groups */
+};
+
+/*
+ * Map a des credential into a unix cred.
+ * We cache the credential here so the application does
+ * not have to make an rpc call every time to interpret
+ * the credential.
+ */
+int
+authdes_getucred(adc, uid, gid, grouplen, groups)
+ struct authdes_cred *adc;
+ uid_t *uid;
+ gid_t *gid;
+ int *grouplen;
+ register gid_t *groups;
+{
+ unsigned sid;
+ register int i;
+ uid_t i_uid;
+ gid_t i_gid;
+ int i_grouplen;
+ struct bsdcred *cred;
+
+ sid = adc->adc_nickname;
+ if (sid >= AUTHDES_CACHESZ) {
+ debug("invalid nickname");
+ return (0);
+ }
+ cred = (struct bsdcred *)authdes_cache[sid].localcred;
+ if (cred == NULL) {
+ cred = (struct bsdcred *)mem_alloc(sizeof(struct bsdcred));
+ authdes_cache[sid].localcred = (char *)cred;
+ cred->grouplen = INVALID;
+ }
+ if (cred->grouplen == INVALID) {
+ /*
+ * not in cache: lookup
+ */
+ if (!netname2user(adc->adc_fullname.name, &i_uid, &i_gid,
+ &i_grouplen, groups))
+ {
+ debug("unknown netname");
+ cred->grouplen = UNKNOWN; /* mark as lookup up, but not found */
+ return (0);
+ }
+ debug("missed ucred cache");
+ *uid = cred->uid = i_uid;
+ *gid = cred->gid = i_gid;
+ *grouplen = cred->grouplen = i_grouplen;
+ for (i = i_grouplen - 1; i >= 0; i--) {
+ cred->groups[i] = groups[i]; /* int to short */
+ }
+ return (1);
+ } else if (cred->grouplen == UNKNOWN) {
+ /*
+ * Already lookup up, but no match found
+ */
+ return (0);
+ }
+
+ /*
+ * cached credentials
+ */
+ *uid = cred->uid;
+ *gid = cred->gid;
+ *grouplen = cred->grouplen;
+ for (i = cred->grouplen - 1; i >= 0; i--) {
+ groups[i] = cred->groups[i]; /* short to int */
+ }
+ return (1);
+}
+
+static void
+invalidate(cred)
+ char *cred;
+{
+ if (cred == NULL) {
+ return;
+ }
+ ((struct bsdcred *)cred)->grouplen = INVALID;
+}
+#endif
+
Index: clnt_udp.c
===================================================================
--- clnt_udp.c (nonexistent)
+++ clnt_udp.c (revision 1765)
@@ -0,0 +1,567 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_udp.c,v 1.15 2000/01/27 23:06:36 jasone Exp $";
+#endif
+
+/*
+ * clnt_udp.c, Implements a UDP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * UDP bases client side rpc operations
+ */
+static enum clnt_stat clntudp_call();
+static void clntudp_abort();
+static void clntudp_geterr();
+static bool_t clntudp_freeres();
+static bool_t clntudp_control();
+static void clntudp_destroy();
+
+static struct clnt_ops udp_ops = {
+ clntudp_call,
+ clntudp_abort,
+ clntudp_geterr,
+ clntudp_freeres,
+ clntudp_destroy,
+ clntudp_control
+};
+
+/*
+ * Private data kept per client handle
+ */
+struct cu_data {
+ int cu_sock;
+ bool_t cu_closeit;
+ struct sockaddr_in cu_raddr;
+ int cu_rlen;
+ struct timeval cu_wait;
+ struct timeval cu_total;
+ struct rpc_err cu_error;
+ XDR cu_outxdrs;
+ u_int cu_xdrpos;
+ u_int cu_sendsz;
+ char *cu_outbuf;
+ u_int cu_recvsz;
+ char cu_inbuf[1];
+};
+
+/*
+ * Create a UDP based client handle.
+ * If *sockp<0, *sockp is set to a newly created UPD socket.
+ * If raddr->sin_port is 0 a binder on the remote machine
+ * is consulted for the correct port number.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is initialized to null authentication.
+ * Caller may wish to set this something more useful.
+ *
+ * wait is the amount of time used between retransmitting a call if
+ * no response has been heard; retransmition occurs until the actual
+ * rpc call times out.
+ *
+ * sendsz and recvsz are the maximum allowable packet sizes that can be
+ * sent and received.
+ */
+CLIENT *
+clntudp_bufcreate(raddr, program, version, wait, sockp, sendsz, recvsz)
+ struct sockaddr_in *raddr;
+ u_long program;
+ u_long version;
+ struct timeval wait;
+ register int *sockp;
+ u_int sendsz;
+ u_int recvsz;
+{
+ CLIENT *cl;
+ register struct cu_data *cu = NULL;
+ struct timeval now;
+ struct rpc_msg call_msg;
+ static u_int32_t disrupt;
+
+ if (disrupt == 0)
+ disrupt = (u_int32_t)(long)raddr;
+
+ cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
+ if (cl == NULL) {
+ (void) fprintf(stderr, "clntudp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ sendsz = ((sendsz + 3) / 4) * 4;
+ recvsz = ((recvsz + 3) / 4) * 4;
+ cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
+ if (cu == NULL) {
+ (void) fprintf(stderr, "clntudp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ cu->cu_outbuf = &cu->cu_inbuf[recvsz];
+
+ (void)gettimeofday(&now, (struct timezone *)0);
+ if (raddr->sin_port == 0) {
+ u_short port;
+ if ((port =
+ pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
+ goto fooy;
+ }
+ raddr->sin_port = htons(port);
+ }
+ cl->cl_ops = &udp_ops;
+ cl->cl_private = (caddr_t)cu;
+ cu->cu_raddr = *raddr;
+ cu->cu_rlen = sizeof (cu->cu_raddr);
+ cu->cu_wait = wait;
+ cu->cu_total.tv_sec = -1;
+ cu->cu_total.tv_usec = -1;
+ cu->cu_sendsz = sendsz;
+ cu->cu_recvsz = recvsz;
+ call_msg.rm_xid = (++disrupt) ^ getpid() ^ now.tv_sec ^ now.tv_usec;
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = program;
+ call_msg.rm_call.cb_vers = version;
+ xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
+ sendsz, XDR_ENCODE);
+ if (! xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
+ goto fooy;
+ }
+ cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
+ if (*sockp < 0) {
+ int dontblock = 1;
+
+ *sockp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (*sockp < 0) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ /* attempt to bind to priv port */
+ (void)bindresvport(*sockp, (struct sockaddr_in *)0);
+ /* the sockets rpc controls are non-blocking */
+ (void)ioctl(*sockp, FIONBIO, (char *) &dontblock);
+ cu->cu_closeit = TRUE;
+ } else {
+ cu->cu_closeit = FALSE;
+ }
+ cu->cu_sock = *sockp;
+ cl->cl_auth = authnone_create();
+ return (cl);
+fooy:
+ if (cu)
+ mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
+ if (cl)
+ mem_free((caddr_t)cl, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+}
+
+CLIENT *
+clntudp_create(raddr, program, version, wait, sockp)
+ struct sockaddr_in *raddr;
+ u_long program;
+ u_long version;
+ struct timeval wait;
+ register int *sockp;
+{
+
+ return(clntudp_bufcreate(raddr, program, version, wait, sockp,
+ UDPMSGSIZE, UDPMSGSIZE));
+}
+
+static enum clnt_stat
+clntudp_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
+ register CLIENT *cl; /* client handle */
+ u_long proc; /* procedure number */
+ xdrproc_t xargs; /* xdr routine for args */
+ caddr_t argsp; /* pointer to args */
+ xdrproc_t xresults; /* xdr routine for results */
+ caddr_t resultsp; /* pointer to results */
+ struct timeval utimeout; /* seconds to wait before giving up */
+{
+ register struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ register XDR *xdrs;
+ register int outlen;
+ register int inlen;
+ int fromlen;
+ fd_set *fds, readfds;
+ struct sockaddr_in from;
+ struct rpc_msg reply_msg;
+ XDR reply_xdrs;
+ struct timeval time_waited, start, after, tmp1, tmp2, tv;
+ bool_t ok;
+ int nrefreshes = 2; /* number of times to refresh cred */
+ struct timeval timeout;
+
+ if (cu->cu_total.tv_usec == -1)
+ timeout = utimeout; /* use supplied timeout */
+ else
+ timeout = cu->cu_total; /* use default timeout */
+
+ if (cu->cu_sock + 1 > FD_SETSIZE) {
+ int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL)
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ memset(fds, 0, bytes);
+ } else {
+ fds = &readfds;
+ FD_ZERO(fds);
+ }
+
+ timerclear(&time_waited);
+
+call_again:
+ xdrs = &(cu->cu_outxdrs);
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, cu->cu_xdrpos);
+ /*
+ * the transaction is the first thing in the out buffer
+ */
+ (*(u_short *)(cu->cu_outbuf))++;
+ if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
+ (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
+ (! (*xargs)(xdrs, argsp))) {
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
+ }
+ outlen = (int)XDR_GETPOS(xdrs);
+
+send_again:
+ if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
+ (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) {
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ }
+
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (!timerisset(&timeout)) {
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+ /*
+ * sub-optimal code appears here because we have
+ * some clock time to spare while the packets are in flight.
+ * (We assume that this is actually only executed once.)
+ */
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = resultsp;
+ reply_msg.acpted_rply.ar_results.proc = xresults;
+
+ gettimeofday(&start, NULL);
+ for (;;) {
+ /* XXX we know the other bits are still clear */
+ FD_SET(cu->cu_sock, fds);
+ tv = cu->cu_wait;
+ switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) {
+
+ case 0:
+ timeradd(&time_waited, &cu->cu_wait, &tmp1);
+ time_waited = tmp1;
+ if (timercmp(&time_waited, &timeout, <))
+ goto send_again;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+
+ case -1:
+ if (errno == EINTR) {
+ gettimeofday(&after, NULL);
+ timersub(&after, &start, &tmp1);
+ timeradd(&time_waited, &tmp1, &tmp2);
+ time_waited = tmp2;
+ if (timercmp(&time_waited, &timeout, <))
+ continue;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+
+ do {
+ fromlen = sizeof(struct sockaddr);
+ inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
+ (int) cu->cu_recvsz, 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (inlen < 0 && errno == EINTR);
+ if (inlen < 0) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+ if (inlen < sizeof(u_int32_t))
+ continue;
+ /* see if reply transaction id matches sent id */
+ if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
+ continue;
+ /* we now assume we have the proper reply */
+ break;
+ }
+
+ /*
+ * now decode and validate the response
+ */
+ xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
+ ok = xdr_replymsg(&reply_xdrs, &reply_msg);
+ /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
+ if (ok) {
+ _seterr_reply(&reply_msg, &(cu->cu_error));
+ if (cu->cu_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(cl->cl_auth,
+ &reply_msg.acpted_rply.ar_verf)) {
+ cu->cu_error.re_status = RPC_AUTHERROR;
+ cu->cu_error.re_why = AUTH_INVALIDRESP;
+ }
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else {
+ /* maybe our credentials need to be refreshed ... */
+ if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
+ nrefreshes--;
+ goto call_again;
+ }
+ } /* end of unsuccessful completion */
+ } /* end of valid reply message */
+ else {
+ /*
+ * It's possible for xdr_replymsg() to fail partway
+ * through its attempt to decode the result from the
+ * server. If this happens, it will leave the reply
+ * structure partially populated with dynamically
+ * allocated memory. (This can happen if someone uses
+ * clntudp_bufcreate() to create a CLIENT handle and
+ * specifies a receive buffer size that is too small.)
+ * This memory must be free()ed to avoid a leak.
+ */
+ int op = reply_xdrs.x_op;
+ reply_xdrs.x_op = XDR_FREE;
+ xdr_replymsg(&reply_xdrs, &reply_msg);
+ reply_xdrs.x_op = op;
+ cu->cu_error.re_status = RPC_CANTDECODERES;
+ }
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status);
+}
+
+static void
+clntudp_geterr(cl, errp)
+ CLIENT *cl;
+ struct rpc_err *errp;
+{
+ register struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ *errp = cu->cu_error;
+}
+
+
+static bool_t
+clntudp_freeres(cl, xdr_res, res_ptr)
+ CLIENT *cl;
+ xdrproc_t xdr_res;
+ caddr_t res_ptr;
+{
+ register struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ register XDR *xdrs = &(cu->cu_outxdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+static void
+clntudp_abort(/*h*/)
+ /*CLIENT *h;*/
+{
+}
+
+
+static bool_t
+clntudp_control(cl, request, info)
+ CLIENT *cl;
+ int request;
+ char *info;
+{
+ register struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ register struct timeval *tv;
+ int len;
+
+ switch (request) {
+ case CLSET_FD_CLOSE:
+ cu->cu_closeit = TRUE;
+ break;
+ case CLSET_FD_NCLOSE:
+ cu->cu_closeit = FALSE;
+ break;
+ case CLSET_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ tv = (struct timeval *)info;
+ cu->cu_total.tv_sec = tv->tv_sec;
+ cu->cu_total.tv_usec = tv->tv_usec;
+ break;
+ case CLGET_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ *(struct timeval *)info = cu->cu_total;
+ break;
+ case CLSET_RETRY_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ tv = (struct timeval *)info;
+ cu->cu_wait.tv_sec = tv->tv_sec;
+ cu->cu_wait.tv_usec = tv->tv_usec;
+ break;
+ case CLGET_RETRY_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ *(struct timeval *)info = cu->cu_wait;
+ break;
+ case CLGET_SERVER_ADDR:
+ if (info == NULL)
+ return(FALSE);
+ *(struct sockaddr_in *)info = cu->cu_raddr;
+ break;
+ case CLGET_FD:
+ if (info == NULL)
+ return(FALSE);
+ *(int *)info = cu->cu_sock;
+ break;
+ case CLGET_XID:
+ /*
+ * use the knowledge that xid is the
+ * first element in the call structure *.
+ * This will get the xid of the PREVIOUS call
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)cu->cu_outbuf);
+ break;
+ case CLSET_XID:
+ /* This will set the xid of the NEXT call */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)cu->cu_outbuf = htonl(*(u_long *)info - 1);
+ /* decrement by 1 as clntudp_call() increments once */
+ case CLGET_VERS:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the version number field is the fifth field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
+ 4 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_VERS:
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)(cu->cu_outbuf + 4 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ case CLGET_PROG:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the program number field is the field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)(cu->cu_outbuf +
+ 3 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_PROG:
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)(cu->cu_outbuf + 3 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ case CLGET_LOCAL_ADDR:
+ len = sizeof(struct sockaddr);
+ if (getsockname(cu->cu_sock, (struct sockaddr *)info, &len) <0)
+ return(FALSE);
+ break;
+ case CLGET_SVC_ADDR:
+ case CLSET_SVC_ADDR:
+ case CLSET_PUSH_TIMOD:
+ case CLSET_POP_TIMOD:
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+static void
+clntudp_destroy(cl)
+ CLIENT *cl;
+{
+ register struct cu_data *cu = (struct cu_data *)cl->cl_private;
+
+ if (cu->cu_closeit) {
+ (void)_RPC_close(cu->cu_sock);
+ }
+ XDR_DESTROY(&(cu->cu_outxdrs));
+ mem_free((caddr_t)cu, (sizeof(*cu) + cu->cu_sendsz + cu->cu_recvsz));
+ mem_free((caddr_t)cl, sizeof(CLIENT));
+}
Index: clnt_simple.c
===================================================================
--- clnt_simple.c (nonexistent)
+++ clnt_simple.c (revision 1765)
@@ -0,0 +1,123 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)clnt_simple.c 1.35 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)clnt_simple.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_simple.c,v 1.12 2000/01/27 23:06:35 jasone Exp $";
+#endif
+
+/*
+ * clnt_simple.c
+ * Simplified front end to rpc.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct call_rpc_private {
+ CLIENT *client;
+ int socket;
+ int oldprognum, oldversnum, valid;
+ char *oldhost;
+};
+#define callrpc_private ((struct call_rpc_private *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->call_rpc_private)
+
+int
+callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out)
+ char *host;
+ int prognum, versnum, procnum;
+ xdrproc_t inproc, outproc;
+ char *in, *out;
+{
+ register struct call_rpc_private *crp = callrpc_private;
+ struct sockaddr_in server_addr;
+ enum clnt_stat clnt_stat;
+ struct hostent *hp;
+ struct timeval timeout, tottimeout;
+
+ if (crp == 0) {
+ crp = (struct call_rpc_private *)calloc(1, sizeof (*crp));
+ if (crp == 0)
+ return (0);
+ callrpc_private = crp;
+ }
+ if (crp->oldhost == NULL) {
+ crp->oldhost = malloc(MAXHOSTNAMELEN);
+ crp->oldhost[0] = 0;
+ crp->socket = RPC_ANYSOCK;
+ }
+ if (crp->valid && crp->oldprognum == prognum && crp->oldversnum == versnum
+ && strcmp(crp->oldhost, host) == 0) {
+ /* reuse old client */
+ } else {
+ crp->valid = 0;
+ if (crp->socket != -1)
+ (void)_RPC_close(crp->socket);
+ crp->socket = RPC_ANYSOCK;
+ if (crp->client) {
+ clnt_destroy(crp->client);
+ crp->client = NULL;
+ }
+ if ((hp = gethostbyname(host)) == NULL)
+ return ((int) RPC_UNKNOWNHOST);
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 5;
+ memset(&server_addr, 0, sizeof(server_addr));
+ memcpy((char *)&server_addr.sin_addr, hp->h_addr, hp->h_length);
+ server_addr.sin_len = sizeof(struct sockaddr_in);
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = 0;
+ if ((crp->client = clntudp_create(&server_addr, (u_long)prognum,
+ (u_long)versnum, timeout, &crp->socket)) == NULL)
+ return ((int) rpc_createerr.cf_stat);
+ crp->valid = 1;
+ crp->oldprognum = prognum;
+ crp->oldversnum = versnum;
+ (void) strcpy(crp->oldhost, host);
+ }
+ tottimeout.tv_sec = 25;
+ tottimeout.tv_usec = 0;
+ clnt_stat = clnt_call(crp->client, procnum, inproc, in,
+ outproc, out, tottimeout);
+ /*
+ * if call failed, empty cache
+ */
+ if (clnt_stat != RPC_SUCCESS)
+ crp->valid = 0;
+ return ((int) clnt_stat);
+}
Index: authunix_prot.c
===================================================================
--- authunix_prot.c (nonexistent)
+++ authunix_prot.c (revision 1765)
@@ -0,0 +1,68 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)authunix_prot.c 1.15 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)authunix_prot.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/authunix_prot.c,v 1.6 1999/08/28 00:00:33 peter Exp $";
+#endif
+
+/*
+ * authunix_prot.c
+ * XDR for UNIX style authentication parameters for RPC
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+
+#include
+#include
+#include
+#include
+
+/*
+ * XDR for unix authentication parameters.
+ */
+bool_t
+xdr_authunix_parms(xdrs, p)
+ register XDR *xdrs;
+ register struct authunix_parms *p;
+{
+
+ if (xdr_u_long(xdrs, &(p->aup_time))
+ && xdr_string(xdrs, &(p->aup_machname), MAX_MACHINE_NAME)
+ && xdr_int(xdrs, &(p->aup_uid))
+ && xdr_int(xdrs, &(p->aup_gid))
+ && xdr_array(xdrs, (caddr_t *)&(p->aup_gids),
+ &(p->aup_len), NGRPS, sizeof(int), xdr_int) ) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
Index: rpc_secure.3
===================================================================
--- rpc_secure.3 (nonexistent)
+++ rpc_secure.3 (revision 1765)
@@ -0,0 +1,254 @@
+.\" @(#)rpc_secure.3n 2.1 88/08/08 4.0 RPCSRC; from 1.19 88/06/24 SMI
+.\" $FreeBSD: src/lib/libc/rpc/rpc_secure.3,v 1.6 2000/03/02 09:13:48 sheldonh Exp $
+.\"
+.Dd February 16, 1988
+.Dt RPC 3
+.Sh NAME
+.Nm rpc_secure
+.Nd library routines for secure remote procedure calls
+.Sh SYNOPSIS
+.Fd #include
+.Ft AUTH *
+.Fo authdes_create
+.Fa "char *name"
+.Fa "unsigned window"
+.Fa "struct sockaddr *addr"
+.Fa "des_block *ckey"
+.Fc
+.Ft int
+.Fn authdes_getucred "struct authdes_cred *adc" "uid_t *uid" "gid_t *gid" "int *grouplen" "gid_t *groups"
+.Ft int
+.Fn getnetname "char *name"
+.Ft int
+.Fn host2netname "char *name" "char *host" "char *domain"
+.Ft int
+.Fn key_decryptsession "const char *remotename" "des_block *deskey"
+.Ft int
+.Fn key_encryptsession "const char *remotename" "des_block *deskey"
+.Ft int
+.Fn key_gendes "des_block *deskey"
+.Ft int
+.Fn key_setsecret "const char *key"
+.Ft int
+.Fn netname2host "char *name" "char *host" "int hostlen"
+.Ft int
+.Fn netname2user "char *name" "uid_t *uidp" "gid_t *gidp" "int *gidlenp" "gid_t *gidlist"
+.Ft int
+.Fn user2netname "char *name" "uid_t uid" "char *domain"
+.Sh DESCRIPTION
+These routines are part of the
+.Tn RPC
+library. They implement
+.Tn DES
+Authentication. See
+.Xr rpc 3
+for further details about
+.Tn RPC .
+.Pp
+The
+.Fn authdes_create
+is the first of two routines which interface to the
+.Tn RPC
+secure authentication system, known as
+.Tn DES
+authentication.
+The second is
+.Fn authdes_getucred ,
+below.
+.Pp
+Note: the keyserver daemon
+.Xr keyserv 8
+must be running for the
+.Tn DES
+authentication system to work.
+.Pp
+.Fn Authdes_create ,
+used on the client side, returns an authentication handle that
+will enable the use of the secure authentication system.
+The first parameter
+.Fa name
+is the network name, or
+.Fa netname ,
+of the owner of the server process.
+This field usually
+represents a
+.Fa hostname
+derived from the utility routine
+.Fn host2netname ,
+but could also represent a user name using
+.Fn user2netname .
+The second field is window on the validity of
+the client credential, given in seconds. A small
+window is more secure than a large one, but choosing
+too small of a window will increase the frequency of
+resynchronizations because of clock drift.
+The third
+parameter
+.Fa addr
+is optional. If it is
+.Dv NULL ,
+then the authentication system will assume
+that the local clock is always in sync with the server's
+clock, and will not attempt resynchronizations.
+If an address
+is supplied, however, then the system will use the address
+for consulting the remote time service whenever
+resynchronization
+is required.
+This parameter is usually the
+address of the
+.Tn RPC
+server itself.
+The final parameter
+.Fa ckey
+is also optional. If it is
+.Dv NULL ,
+then the authentication system will
+generate a random
+.Tn DES
+key to be used for the encryption of credentials.
+If it is supplied, however, then it will be used instead.
+.Pp
+.Fn Authdes_getucred ,
+the second of the two
+.Tn DES
+authentication routines,
+is used on the server side for converting a
+.Tn DES
+credential, which is
+operating system independent, into a
+.Ux
+credential.
+This routine differs from utility routine
+.Fn netname2user
+in that
+.Fn authdes_getucred
+pulls its information from a cache, and does not have to do a
+Yellow Pages lookup every time it is called to get its information.
+.Pp
+.Fn Getnetname
+installs the unique, operating-system independent netname of
+the
+caller in the fixed-length array
+.Fa name .
+Returns
+.Dv TRUE
+if it succeeds and
+.Dv FALSE
+if it fails.
+.Pp
+.Fn Host2netname
+converts from a domain-specific hostname to an
+operating-system independent netname.
+Returns
+.Dv TRUE
+if it succeeds and
+.Dv FALSE
+if it fails.
+Inverse of
+.Fn netname2host .
+.Pp
+.Fn Key_decryptsession
+is an interface to the keyserver daemon, which is associated
+with
+.Tn RPC Ns 's
+secure authentication system (
+.Tn DES
+authentication).
+User programs rarely need to call it, or its associated routines
+.Fn key_encryptsession ,
+.Fn key_gendes
+and
+.Fn key_setsecret .
+System commands such as
+.Xr login 1
+and the
+.Tn RPC
+library are the main clients of these four routines.
+.Pp
+.Fn Key_decryptsession
+takes a server netname and a
+.Tn DES
+key, and decrypts the key by
+using the the public key of the the server and the secret key
+associated with the effective uid of the calling process. It
+is the inverse of
+.Fn key_encryptsession .
+.Pp
+.Fn Key_encryptsession
+is a keyserver interface routine.
+It
+takes a server netname and a des key, and encrypts
+it using the public key of the the server and the secret key
+associated with the effective uid of the calling process. It
+is the inverse of
+.Fn key_decryptsession .
+.Pp
+.Fn Key_gendes
+is a keyserver interface routine.
+It
+is used to ask the keyserver for a secure conversation key.
+Choosing one
+.Qq random
+is usually not good enough,
+because
+the common ways of choosing random numbers, such as using the
+current time, are very easy to guess.
+.Pp
+.Fn Key_setsecret
+is a keyserver interface routine.
+It is used to set the key for
+the effective
+.Fa uid
+of the calling process.
+.Pp
+.Fn Netname2host
+converts from an operating-system independent netname to a
+domain-specific hostname.
+Returns
+.Dv TRUE
+if it succeeds and
+.Dv FALSE
+if it fails. Inverse of
+.Fn host2netname .
+.Pp
+.Fn Netname2user
+converts from an operating-system independent netname to a
+domain-specific user ID.
+Returns
+.Dv TRUE
+if it succeeds and
+.Dv FALSE
+if it fails.
+Inverse of
+.Fn user2netname .
+.Pp
+.Fn User2netname
+converts from a domain-specific username to an operating-system
+independent netname.
+Returns
+.Dv TRUE
+if it succeeds and
+.Dv FALSE
+if it fails.
+Inverse of
+.Fn netname2user .
+.Sh SEE ALSO
+.Xr rpc 3 ,
+.Xr xdr 3 ,
+.Xr keyserv 8
+.Pp
+The following manuals:
+.Rs
+.%B Remote Procedure Calls: Protocol Specification
+.Re
+.Rs
+.%B Remote Procedure Call Programming Guide
+.Re
+.Rs
+.%B Rpcgen Programming Guide
+.Re
+.Rs
+.%B RPC: Remote Procedure Call Protocol Specification
+.%O RFC1050, Sun Microsystems Inc., USC-ISI
+.Re
Index: getrpcent.3
===================================================================
--- getrpcent.3 (nonexistent)
+++ getrpcent.3 (revision 1765)
@@ -0,0 +1,98 @@
+.\" @(#)getrpcent.3n 2.2 88/08/02 4.0 RPCSRC; from 1.11 88/03/14 SMI
+.\" $FreeBSD: src/lib/libc/rpc/getrpcent.3,v 1.11 1999/08/28 00:00:39 peter Exp $
+.\"
+.Dd December 14, 1987
+.Dt GETRPCENT 3
+.Os
+.Sh NAME
+.Nm getrpcent ,
+.Nm getrpcbyname ,
+.Nm getrpcbynumber ,
+.Nm endrpcent ,
+.Nm setrpcent
+.Nd get RPC entry
+.Sh SYNOPSIS
+.Fd #include
+.Ft struct rpcent *
+.Fn getrpcent void
+.Ft struct rpcent *
+.Fn getrpcbyname "char *name"
+.Ft struct rpcent *
+.Fn getrpcbynumber "int number"
+.Ft void
+.Fn setrpcent "int stayopen"
+.Ft void
+.Fn endrpcent void
+.Sh DESCRIPTION
+The
+.Fn getrpcent ,
+.Fn getrpcbyname ,
+and
+.Fn getrpcbynumber
+functions each return a pointer to an object with the
+following structure
+containing the broken-out
+fields of a line in the rpc program number data base,
+.Pa /etc/rpc .
+.Bd -literal
+
+struct rpcent {
+ char *r_name; /* name of server for this rpc program */
+ char **r_aliases; /* alias list */
+ long r_number; /* rpc program number */
+};
+.Ed
+.Pp
+The members of this structure are:
+.Bl -tag -width r_aliasesxxx
+.It Fa r_name
+The name of the server for this rpc program.
+.It Fa r_aliases
+A zero terminated list of alternate names for the rpc program.
+.It Fa r_number
+The rpc program number for this service.
+.El
+.Pp
+The
+.Fn getrpcent
+function reads the next line of the file, opening the file if necessary.
+The
+.Nm getrpcent
+function opens and rewinds the file. If the
+.Fa stayopen
+flag is non-zero,
+the net data base will not be closed after each call to
+.Fn getrpcent
+(either directly, or indirectly through one of
+the other
+.Fn getrpcent
+function family.
+.Pp
+.Fn endrpcent
+closes the file.
+.Pp
+.Fn getrpcbyname
+and
+.Fn getrpcbynumber
+sequentially search from the beginning
+of the file until a matching rpc program name or
+program number is found, or until end-of-file is encountered.
+.Sh FILES
+.Bl -tag -width /etc/rpc -compact
+.It Pa /etc/rpc
+.El
+.Sh "SEE ALSO"
+.Xr rpc 5 ,
+.Xr rpcinfo 8 ,
+.Xr ypserv 8
+.Sh DIAGNOSTICS
+A
+.Dv NULL
+pointer is returned on
+.Dv EOF
+or error.
+.Sh BUGS
+All information
+is contained in a static area
+so it must be copied if it is
+to be saved.
Index: des_crypt.3
===================================================================
--- des_crypt.3 (nonexistent)
+++ des_crypt.3 (revision 1765)
@@ -0,0 +1,130 @@
+.\" @(#)des_crypt.3 2.1 88/08/11 4.0 RPCSRC; from 1.16 88/03/02 SMI;
+.\" $FreeBSD: src/lib/libc/rpc/des_crypt.3,v 1.4 2000/03/02 09:13:45 sheldonh Exp $
+.\"
+.TH DES_CRYPT 3 "6 October 1987"
+.SH NAME
+des_crypt, ecb_crypt, cbc_crypt, des_setparity \- fast DES encryption
+.SH SYNOPSIS
+.nf
+.B #include
+.LP
+.B int ecb_crypt(key, data, datalen, mode)
+.B char *key;
+.B char *data;
+.B unsigned datalen;
+.B unsigned mode;
+.LP
+.B int cbc_crypt(key, data, datalen, mode, ivec)
+.B char *key;
+.B char *data;
+.B unsigned datalen;
+.B unsigned mode;
+.B char *ivec;
+.LP
+.B void des_setparity(key)
+.B char *key;
+.fi
+.SH DESCRIPTION
+.IX encryption cbc_crypt "" \fLcbc_crypt\fP
+.IX "des encryption" cbc_crypt "DES encryption" \fLcbc_crypt\fP
+.IX encryption des_setparity "" \fLdes_setparity\fP
+.IX "des encryption" des_setparity "DES encryption" \fLdes_setparity\fP
+.B ecb_crypt(\|)
+and
+.B cbc_crypt(\|)
+implement the
+.SM NBS
+.SM DES
+(Data Encryption Standard).
+These routines are faster and more general purpose than
+.BR crypt (3).
+They also are able to utilize
+.SM DES
+hardware if it is available.
+.B ecb_crypt(\|)
+encrypts in
+.SM ECB
+(Electronic Code Book)
+mode, which encrypts blocks of data independently.
+.B cbc_crypt(\|)
+encrypts in
+.SM CBC
+(Cipher Block Chaining)
+mode, which chains together
+successive blocks.
+.SM CBC
+mode protects against insertions, deletions and
+substitutions of blocks.
+Also, regularities in the clear text will
+not appear in the cipher text.
+.LP
+Here is how to use these routines. The first parameter,
+.IR key ,
+is the 8-byte encryption key with parity.
+To set the key's parity, which for
+.SM DES
+is in the low bit of each byte, use
+.IR des_setparity .
+The second parameter,
+.IR data ,
+contains the data to be encrypted or decrypted.
+The
+third parameter,
+.IR datalen ,
+is the length in bytes of
+.IR data ,
+which must be a multiple of 8. The fourth parameter,
+.IR mode ,
+is formed by
+.SM OR\s0'ing
+together some things. For the encryption direction 'or' in either
+.SM DES_ENCRYPT
+or
+.SM DES_DECRYPT\s0.
+For software versus hardware
+encryption, 'or' in either
+.SM DES_HW
+or
+.SM DES_SW\s0.
+If
+.SM DES_HW
+is specified, and there is no hardware, then the encryption is performed
+in software and the routine returns
+.SM DESERR_NOHWDEVICE\s0.
+For
+.IR cbc_crypt ,
+the parameter
+.I ivec
+is the the 8-byte initialization
+vector for the chaining. It is updated to the next initialization
+vector upon return.
+.LP
+.SH "SEE ALSO"
+.BR des (1),
+.BR crypt (3)
+.SH DIAGNOSTICS
+.PD 0
+.TP 20
+.SM DESERR_NONE
+No error.
+.TP
+.SM DESERR_NOHWDEVICE
+Encryption succeeded, but done in software instead of the requested hardware.
+.TP
+.SM DESERR_HWERR
+An error occurred in the hardware or driver.
+.TP
+.SM DESERR_BADPARAM
+Bad parameter to routine.
+.PD
+.LP
+Given a result status
+.IR stat ,
+the macro
+.SM DES_FAILED\c
+.BR ( stat )
+is false only for the first two statuses.
+.SH RESTRICTIONS
+These routines are not available in RPCSRC 4.0.
+This information is provided to describe the DES interface expected by
+Secure RPC.
Index: rpc_callmsg.c
===================================================================
--- rpc_callmsg.c (nonexistent)
+++ rpc_callmsg.c (revision 1765)
@@ -0,0 +1,193 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)rpc_callmsg.c 1.4 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)rpc_callmsg.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/rpc_callmsg.c,v 1.9 1999/08/28 00:00:45 peter Exp $";
+#endif
+
+/*
+ * rpc_callmsg.c
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+/*
+ * XDR a call message
+ */
+bool_t
+xdr_callmsg(xdrs, cmsg)
+ register XDR *xdrs;
+ register struct rpc_msg *cmsg;
+{
+ register int32_t *buf;
+ register struct opaque_auth *oa;
+
+ if (xdrs->x_op == XDR_ENCODE) {
+ if (cmsg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ if (cmsg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT
+ + RNDUP(cmsg->rm_call.cb_cred.oa_length)
+ + 2 * BYTES_PER_XDR_UNIT
+ + RNDUP(cmsg->rm_call.cb_verf.oa_length));
+ if (buf != NULL) {
+ IXDR_PUT_LONG(buf, cmsg->rm_xid);
+ IXDR_PUT_ENUM(buf, cmsg->rm_direction);
+ if (cmsg->rm_direction != CALL) {
+ return (FALSE);
+ }
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_rpcvers);
+ if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) {
+ return (FALSE);
+ }
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_prog);
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_vers);
+ IXDR_PUT_LONG(buf, cmsg->rm_call.cb_proc);
+ oa = &cmsg->rm_call.cb_cred;
+ IXDR_PUT_ENUM(buf, oa->oa_flavor);
+ IXDR_PUT_LONG(buf, oa->oa_length);
+ if (oa->oa_length) {
+ memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
+ buf += RNDUP(oa->oa_length) / sizeof (int32_t);
+ }
+ oa = &cmsg->rm_call.cb_verf;
+ IXDR_PUT_ENUM(buf, oa->oa_flavor);
+ IXDR_PUT_LONG(buf, oa->oa_length);
+ if (oa->oa_length) {
+ memcpy((caddr_t)buf, oa->oa_base, oa->oa_length);
+ /* no real need....
+ buf += RNDUP(oa->oa_length) / sizeof (int32_t);
+ */
+ }
+ return (TRUE);
+ }
+ }
+ if (xdrs->x_op == XDR_DECODE) {
+ buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT);
+ if (buf != NULL) {
+ cmsg->rm_xid = IXDR_GET_LONG(buf);
+ cmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type);
+ if (cmsg->rm_direction != CALL) {
+ return (FALSE);
+ }
+ cmsg->rm_call.cb_rpcvers = IXDR_GET_LONG(buf);
+ if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) {
+ return (FALSE);
+ }
+ cmsg->rm_call.cb_prog = IXDR_GET_LONG(buf);
+ cmsg->rm_call.cb_vers = IXDR_GET_LONG(buf);
+ cmsg->rm_call.cb_proc = IXDR_GET_LONG(buf);
+ oa = &cmsg->rm_call.cb_cred;
+ oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
+ oa->oa_length = IXDR_GET_LONG(buf);
+ if (oa->oa_length) {
+ if (oa->oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ if (oa->oa_base == NULL) {
+ oa->oa_base = (caddr_t)
+ mem_alloc(oa->oa_length);
+ }
+ buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length));
+ if (buf == NULL) {
+ if (xdr_opaque(xdrs, oa->oa_base,
+ oa->oa_length) == FALSE) {
+ return (FALSE);
+ }
+ } else {
+ memcpy(oa->oa_base, (caddr_t)buf,
+ oa->oa_length);
+ /* no real need....
+ buf += RNDUP(oa->oa_length) /
+ sizeof (int32_t);
+ */
+ }
+ }
+ oa = &cmsg->rm_call.cb_verf;
+ buf = XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT);
+ if (buf == NULL) {
+ if (xdr_enum(xdrs, &oa->oa_flavor) == FALSE ||
+ xdr_u_int(xdrs, &oa->oa_length) == FALSE) {
+ return (FALSE);
+ }
+ } else {
+ oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t);
+ oa->oa_length = IXDR_GET_LONG(buf);
+ }
+ if (oa->oa_length) {
+ if (oa->oa_length > MAX_AUTH_BYTES) {
+ return (FALSE);
+ }
+ if (oa->oa_base == NULL) {
+ oa->oa_base = (caddr_t)
+ mem_alloc(oa->oa_length);
+ }
+ buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length));
+ if (buf == NULL) {
+ if (xdr_opaque(xdrs, oa->oa_base,
+ oa->oa_length) == FALSE) {
+ return (FALSE);
+ }
+ } else {
+ memcpy(oa->oa_base, (caddr_t)buf,
+ oa->oa_length);
+ /* no real need...
+ buf += RNDUP(oa->oa_length) /
+ sizeof (int32_t);
+ */
+ }
+ }
+ return (TRUE);
+ }
+ }
+ if (
+ xdr_u_int32_t(xdrs, &(cmsg->rm_xid)) &&
+ xdr_enum(xdrs, (enum_t *)&(cmsg->rm_direction)) &&
+ (cmsg->rm_direction == CALL) &&
+ xdr_u_int32_t(xdrs, &(cmsg->rm_call.cb_rpcvers)) &&
+ (cmsg->rm_call.cb_rpcvers == RPC_MSG_VERSION) &&
+ xdr_u_int32_t(xdrs, &(cmsg->rm_call.cb_prog)) &&
+ xdr_u_int32_t(xdrs, &(cmsg->rm_call.cb_vers)) &&
+ xdr_u_int32_t(xdrs, &(cmsg->rm_call.cb_proc)) &&
+ xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_cred)) )
+ return (xdr_opaque_auth(xdrs, &(cmsg->rm_call.cb_verf)));
+ return (FALSE);
+}
+
Index: get_myaddress.c
===================================================================
--- get_myaddress.c (nonexistent)
+++ get_myaddress.c (revision 1765)
@@ -0,0 +1,112 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)get_myaddress.c 1.4 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/get_myaddress.c,v 1.17 2000/01/27 23:06:37 jasone Exp $";
+#endif
+
+/*
+ * get_myaddress.c
+ *
+ * Get client's IP address via ioctl. This avoids using the yellowpages.
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * don't use gethostbyname, which would invoke yellow pages
+ *
+ * Avoid loopback interfaces. We return information from a loopback
+ * interface only if there are no other possible interfaces.
+ */
+int
+get_myaddress(addr)
+ struct sockaddr_in *addr;
+{
+ int s;
+ char buf[BUFSIZ];
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr, *end;
+ int loopback = 0, gotit = 0;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ return(-1);
+ }
+ ifc.ifc_len = sizeof (buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
+ _RPC_close(s);
+ return(-1);
+ }
+again:
+ ifr = ifc.ifc_req;
+ end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+
+ while (ifr < end) {
+ ifreq = *ifr;
+ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ _RPC_close(s);
+ return(-1);
+ }
+ if (((ifreq.ifr_flags & IFF_UP) &&
+ ifr->ifr_addr.sa_family == AF_INET &&
+ !(ifreq.ifr_flags & IFF_LOOPBACK)) ||
+ (loopback == 1 && (ifreq.ifr_flags & IFF_LOOPBACK)
+ && (ifr->ifr_addr.sa_family == AF_INET)
+ && (ifreq.ifr_flags & IFF_UP))) {
+ *addr = *((struct sockaddr_in *)&ifr->ifr_addr);
+ addr->sin_port = htons(PMAPPORT);
+ gotit = 1;
+ break;
+ }
+ if (ifr->ifr_addr.sa_len)
+ ifr = (struct ifreq *) ((caddr_t) ifr +
+ ifr->ifr_addr.sa_len -
+ sizeof(struct sockaddr));
+ ifr++;
+ }
+ if (gotit == 0 && loopback == 0) {
+ loopback = 1;
+ goto again;
+ }
+ (void)_RPC_close(s);
+ return (gotit ? 0 : -1);
+}
Index: auth_des.c
===================================================================
--- auth_des.c (nonexistent)
+++ auth_des.c (revision 1765)
@@ -0,0 +1,554 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1988 by Sun Microsystems, Inc.
+ */
+/*
+ * auth_des.c, client-side implementation of DES authentication
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include /* XXX: just to get htonl() and ntohl() */
+#include
+#undef NIS
+#include
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/* from: static char sccsid[] = "@(#)auth_des.c 2.2 88/07/29 4.0 RPCSRC; from 1.9 88/02/08 SMI"; */
+static const char rcsid[] = "$FreeBSD: src/lib/libc/rpc/auth_des.c,v 1.3 1999/08/28 00:00:32 peter Exp $";
+#endif
+
+extern bool_t __rpc_get_time_offset __P(( struct timeval *, nis_server *,
+ char *, char **, struct sockaddr_in * ));
+extern int rtime __P(( struct sockaddr_in *, struct timeval *, struct timeval *));
+extern bool_t xdr_authdes_cred __P(( XDR *, struct authdes_cred * ));
+extern bool_t xdr_authdes_verf __P(( XDR *, struct authdes_verf * ));
+
+#define MILLION 1000000L
+#define RTIME_TIMEOUT 5 /* seconds to wait for sync */
+
+#define AUTH_PRIVATE(auth) (struct ad_private *) auth->ah_private
+#define ALLOC(object_type) (object_type *) mem_alloc(sizeof(object_type))
+#define FREE(ptr, size) mem_free((char *)(ptr), (int) size)
+#define ATTEMPT(xdr_op) if (!(xdr_op)) return (FALSE)
+
+#define debug(msg) /*printf("%s\n", msg) */
+
+/*
+ * DES authenticator operations vector
+ */
+static void authdes_nextverf();
+static bool_t authdes_marshal();
+static bool_t authdes_validate();
+static bool_t authdes_refresh();
+static void authdes_destroy();
+static struct auth_ops authdes_ops = {
+ authdes_nextverf,
+ authdes_marshal,
+ authdes_validate,
+ authdes_refresh,
+ authdes_destroy
+};
+#ifdef foo
+static bool_t synchronize __P(( struct sockaddr *, struct timeval *));
+#endif
+/*
+ * This struct is pointed to by the ah_private field of an "AUTH *"
+ */
+struct ad_private {
+ char *ad_fullname; /* client's full name */
+ u_int ad_fullnamelen; /* length of name, rounded up */
+ char *ad_servername; /* server's full name */
+ u_int ad_servernamelen; /* length of name, rounded up */
+ u_int ad_window; /* client specified window */
+ bool_t ad_dosync; /* synchronize? */
+ struct sockaddr ad_syncaddr; /* remote host to synch with */
+ char *ad_timehost; /* remote host to synch with */
+ struct timeval ad_timediff; /* server's time - client's time */
+ u_long ad_nickname; /* server's nickname for client */
+ struct authdes_cred ad_cred; /* storage for credential */
+ struct authdes_verf ad_verf; /* storage for verifier */
+ struct timeval ad_timestamp; /* timestamp sent */
+ des_block ad_xkey; /* encrypted conversation key */
+ u_char ad_pkey[1024]; /* Server's actual public key */
+ char *ad_netid; /* Timehost netid */
+ char *ad_uaddr; /* Timehost uaddr */
+ nis_server *ad_nis_srvr; /* NIS+ server struct */
+};
+
+
+/*
+ * Create the client des authentication object
+ */
+AUTH *
+authdes_create(servername, window, syncaddr, ckey)
+ char *servername; /* network name of server */
+ u_int window; /* time to live */
+ struct sockaddr *syncaddr; /* optional addr of host to sync with */
+ des_block *ckey; /* optional conversation key to use*/
+{
+
+ AUTH *auth;
+ struct ad_private *ad;
+ char namebuf[MAXNETNAMELEN+1];
+ u_char pkey_data[1024];
+
+ if (!getpublickey(servername, pkey_data))
+ return(NULL);
+
+ /*
+ * Allocate everything now
+ */
+ auth = ALLOC(AUTH);
+ ad = ALLOC(struct ad_private);
+ (void) getnetname(namebuf);
+
+ ad->ad_fullnamelen = RNDUP(strlen(namebuf));
+ ad->ad_fullname = (char *)mem_alloc(ad->ad_fullnamelen + 1);
+
+ ad->ad_servernamelen = strlen(servername);
+ ad->ad_servername = (char *)mem_alloc(ad->ad_servernamelen + 1);
+
+ if (auth == NULL || ad == NULL || ad->ad_fullname == NULL ||
+ ad->ad_servername == NULL) {
+ debug("authdes_create: out of memory");
+ goto failed;
+ }
+
+ /*
+ * Set up private data
+ */
+ bcopy(namebuf, ad->ad_fullname, ad->ad_fullnamelen + 1);
+ bcopy(servername, ad->ad_servername, ad->ad_servernamelen + 1);
+ bcopy(pkey_data, ad->ad_pkey, strlen(pkey_data) + 1);
+ if (syncaddr != NULL) {
+ ad->ad_syncaddr = *syncaddr;
+ ad->ad_dosync = TRUE;
+ } else {
+ ad->ad_dosync = FALSE;
+ }
+ ad->ad_window = window;
+ if (ckey == NULL) {
+ if (key_gendes(&auth->ah_key) < 0) {
+ debug("authdes_create: unable to gen conversation key");
+ return (NULL);
+ }
+ } else {
+ auth->ah_key = *ckey;
+ }
+
+ /*
+ * Set up auth handle
+ */
+ auth->ah_cred.oa_flavor = AUTH_DES;
+ auth->ah_verf.oa_flavor = AUTH_DES;
+ auth->ah_ops = &authdes_ops;
+ auth->ah_private = (caddr_t)ad;
+
+ if (!authdes_refresh(auth)) {
+ goto failed;
+ }
+ return (auth);
+
+failed:
+ if (auth != NULL)
+ FREE(auth, sizeof(AUTH));
+ if (ad != NULL)
+ FREE(ad, sizeof(struct ad_private));
+ if (ad->ad_fullname != NULL)
+ FREE(ad->ad_fullname, ad->ad_fullnamelen + 1);
+ if (ad->ad_servername != NULL)
+ FREE(ad->ad_servername, ad->ad_servernamelen + 1);
+ return (NULL);
+}
+
+/*
+ * Slightly modified version of authdes_create which takes the public key
+ * of the server principal as an argument. This spares us a call to
+ * getpublickey() which in the nameserver context can cause a deadlock.
+ */
+AUTH *
+authdes_pk_create(servername, pkey, window, timehost, ckey, srvr)
+ char *servername; /* network name of server */
+ netobj *pkey; /* public key of server */
+ u_int window; /* time to live */
+ char *timehost; /* optional hostname to sync with */
+ des_block *ckey; /* optional conversation key to use */
+ nis_server *srvr; /* optional NIS+ server struct */
+{
+ AUTH *auth;
+ struct ad_private *ad;
+ char namebuf[MAXNETNAMELEN+1];
+
+ /*
+ * Allocate everything now
+ */
+ auth = ALLOC(AUTH);
+ if (auth == NULL) {
+ debug("authdes_pk_create: out of memory");
+ return (NULL);
+ }
+ ad = ALLOC(struct ad_private);
+ if (ad == NULL) {
+ debug("authdes_pk_create: out of memory");
+ goto failed;
+ }
+ ad->ad_fullname = ad->ad_servername = NULL; /* Sanity reasons */
+ ad->ad_timehost = NULL;
+ ad->ad_netid = NULL;
+ ad->ad_uaddr = NULL;
+ ad->ad_nis_srvr = NULL;
+ ad->ad_timediff.tv_sec = 0;
+ ad->ad_timediff.tv_usec = 0;
+ memcpy(ad->ad_pkey, pkey->n_bytes, pkey->n_len);
+ if (!getnetname(namebuf))
+ goto failed;
+ ad->ad_fullnamelen = RNDUP((u_int) strlen(namebuf));
+ ad->ad_fullname = (char *)mem_alloc(ad->ad_fullnamelen + 1);
+ ad->ad_servernamelen = strlen(servername);
+ ad->ad_servername = (char *)mem_alloc(ad->ad_servernamelen + 1);
+
+ if (ad->ad_fullname == NULL || ad->ad_servername == NULL) {
+ debug("authdes_pk_create: out of memory");
+ goto failed;
+ }
+ if (timehost != NULL) {
+ ad->ad_timehost = (char *)mem_alloc(strlen(timehost) + 1);
+ if (ad->ad_timehost == NULL) {
+ debug("authdes_pk_create: out of memory");
+ goto failed;
+ }
+ memcpy(ad->ad_timehost, timehost, strlen(timehost) + 1);
+ ad->ad_dosync = TRUE;
+ } else if (srvr != NULL) {
+ ad->ad_nis_srvr = srvr; /* transient */
+ ad->ad_dosync = TRUE;
+ } else {
+ ad->ad_dosync = FALSE;
+ }
+ memcpy(ad->ad_fullname, namebuf, ad->ad_fullnamelen + 1);
+ memcpy(ad->ad_servername, servername, ad->ad_servernamelen + 1);
+ ad->ad_window = window;
+ if (ckey == NULL) {
+ if (key_gendes(&auth->ah_key) < 0) {
+ debug("authdes_pk_create: unable to gen conversation key");
+ goto failed;
+ }
+ } else {
+ auth->ah_key = *ckey;
+ }
+
+ /*
+ * Set up auth handle
+ */
+ auth->ah_cred.oa_flavor = AUTH_DES;
+ auth->ah_verf.oa_flavor = AUTH_DES;
+ auth->ah_ops = &authdes_ops;
+ auth->ah_private = (caddr_t)ad;
+
+ if (!authdes_refresh(auth)) {
+ goto failed;
+ }
+ ad->ad_nis_srvr = NULL; /* not needed any longer */
+ return (auth);
+
+failed:
+ if (auth)
+ FREE(auth, sizeof (AUTH));
+ if (ad) {
+ if (ad->ad_fullname)
+ FREE(ad->ad_fullname, ad->ad_fullnamelen + 1);
+ if (ad->ad_servername)
+ FREE(ad->ad_servername, ad->ad_servernamelen + 1);
+ if (ad->ad_timehost)
+ FREE(ad->ad_timehost, strlen(ad->ad_timehost) + 1);
+ if (ad->ad_netid)
+ free(ad->ad_netid);
+ if (ad->ad_uaddr)
+ free(ad->ad_uaddr);
+ FREE(ad, sizeof (struct ad_private));
+ }
+ return (NULL);
+}
+/*
+ * Implement the five authentication operations
+ */
+
+
+/*
+ * 1. Next Verifier
+ */
+/*ARGSUSED*/
+static void
+authdes_nextverf(auth)
+ AUTH *auth;
+{
+ /* what the heck am I supposed to do??? */
+}
+
+
+
+/*
+ * 2. Marshal
+ */
+static bool_t
+authdes_marshal(auth, xdrs)
+ AUTH *auth;
+ XDR *xdrs;
+{
+ struct ad_private *ad = AUTH_PRIVATE(auth);
+ struct authdes_cred *cred = &ad->ad_cred;
+ struct authdes_verf *verf = &ad->ad_verf;
+ des_block cryptbuf[2];
+ des_block ivec;
+ int status;
+ long len;
+ int32_t *ixdr;
+
+ /*
+ * Figure out the "time", accounting for any time difference
+ * with the server if necessary.
+ */
+ (void) gettimeofday(&ad->ad_timestamp, (struct timezone *)NULL);
+ ad->ad_timestamp.tv_sec += ad->ad_timediff.tv_sec;
+ ad->ad_timestamp.tv_usec += ad->ad_timediff.tv_usec;
+ if (ad->ad_timestamp.tv_usec >= MILLION) {
+ ad->ad_timestamp.tv_usec -= MILLION;
+ ad->ad_timestamp.tv_sec += 1;
+ }
+
+ /*
+ * XDR the timestamp and possibly some other things, then
+ * encrypt them.
+ */
+ ixdr = (int32_t *)cryptbuf;
+ IXDR_PUT_LONG(ixdr, ad->ad_timestamp.tv_sec);
+ IXDR_PUT_LONG(ixdr, ad->ad_timestamp.tv_usec);
+ if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
+ IXDR_PUT_U_LONG(ixdr, ad->ad_window);
+ IXDR_PUT_U_LONG(ixdr, ad->ad_window - 1);
+ ivec.key.high = ivec.key.low = 0;
+ status = cbc_crypt((char *)&auth->ah_key, (char *)cryptbuf,
+ 2*sizeof(des_block), DES_ENCRYPT | DES_HW, (char *)&ivec);
+ } else {
+ status = ecb_crypt((char *)&auth->ah_key, (char *)cryptbuf,
+ sizeof(des_block), DES_ENCRYPT | DES_HW);
+ }
+ if (DES_FAILED(status)) {
+ debug("authdes_marshal: DES encryption failure");
+ return (FALSE);
+ }
+ ad->ad_verf.adv_xtimestamp = cryptbuf[0];
+ if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
+ ad->ad_cred.adc_fullname.window = cryptbuf[1].key.high;
+ ad->ad_verf.adv_winverf = cryptbuf[1].key.low;
+ } else {
+ ad->ad_cred.adc_nickname = ad->ad_nickname;
+ ad->ad_verf.adv_winverf = 0;
+ }
+
+ /*
+ * Serialize the credential and verifier into opaque
+ * authentication data.
+ */
+ if (ad->ad_cred.adc_namekind == ADN_FULLNAME) {
+ len = ((1 + 1 + 2 + 1)*BYTES_PER_XDR_UNIT + ad->ad_fullnamelen);
+ } else {
+ len = (1 + 1)*BYTES_PER_XDR_UNIT;
+ }
+
+ if ((ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT))) {
+ IXDR_PUT_LONG(ixdr, AUTH_DES);
+ IXDR_PUT_LONG(ixdr, len);
+ } else {
+ ATTEMPT(xdr_putlong(xdrs, (long *)&auth->ah_cred.oa_flavor));
+ ATTEMPT(xdr_putlong(xdrs, &len));
+ }
+ ATTEMPT(xdr_authdes_cred(xdrs, cred));
+
+ len = (2 + 1)*BYTES_PER_XDR_UNIT;
+ if ((ixdr = xdr_inline(xdrs, 2*BYTES_PER_XDR_UNIT))) {
+ IXDR_PUT_LONG(ixdr, AUTH_DES);
+ IXDR_PUT_LONG(ixdr, len);
+ } else {
+ ATTEMPT(xdr_putlong(xdrs, (long *)&auth->ah_verf.oa_flavor));
+ ATTEMPT(xdr_putlong(xdrs, &len));
+ }
+ ATTEMPT(xdr_authdes_verf(xdrs, verf));
+ return (TRUE);
+}
+
+
+/*
+ * 3. Validate
+ */
+static bool_t
+authdes_validate(auth, rverf)
+ AUTH *auth;
+ struct opaque_auth *rverf;
+{
+ struct ad_private *ad = AUTH_PRIVATE(auth);
+ struct authdes_verf verf;
+ int status;
+ register u_long *ixdr;
+
+ if (rverf->oa_length != (2 + 1) * BYTES_PER_XDR_UNIT) {
+ return (FALSE);
+ }
+ ixdr = (u_long *)rverf->oa_base;
+ verf.adv_xtimestamp.key.high = (u_long)*ixdr++;
+ verf.adv_xtimestamp.key.low = (u_long)*ixdr++;
+ verf.adv_int_u = (u_long)*ixdr++; /* nickname not XDR'd ! */
+
+ /*
+ * Decrypt the timestamp
+ */
+ status = ecb_crypt((char *)&auth->ah_key, (char *)&verf.adv_xtimestamp,
+ sizeof(des_block), DES_DECRYPT | DES_HW);
+
+ if (DES_FAILED(status)) {
+ debug("authdes_validate: DES decryption failure");
+ return (FALSE);
+ }
+
+ /*
+ * xdr the decrypted timestamp
+ */
+ ixdr = (u_long *)verf.adv_xtimestamp.c;
+ verf.adv_timestamp.tv_sec = IXDR_GET_LONG(ixdr) + 1;
+ verf.adv_timestamp.tv_usec = IXDR_GET_LONG(ixdr);
+
+ /*
+ * validate
+ */
+ if (bcmp((char *)&ad->ad_timestamp, (char *)&verf.adv_timestamp,
+ sizeof(struct timeval)) != 0) {
+ debug("authdes_validate: verifier mismatch\n");
+ return (FALSE);
+ }
+
+ /*
+ * We have a nickname now, let's use it
+ */
+ ad->ad_nickname = verf.adv_nickname;
+ ad->ad_cred.adc_namekind = ADN_NICKNAME;
+ return (TRUE);
+}
+
+/*
+ * 4. Refresh
+ */
+static bool_t
+authdes_refresh(auth)
+ AUTH *auth;
+{
+ struct ad_private *ad = AUTH_PRIVATE(auth);
+ struct authdes_cred *cred = &ad->ad_cred;
+ netobj pkey;
+
+ if (ad->ad_dosync &&
+#ifdef old
+ !synchronize(&ad->ad_syncaddr, &ad->ad_timediff)) {
+#else
+ !__rpc_get_time_offset(&ad->ad_timediff,ad->ad_nis_srvr,
+ ad->ad_timehost, &(ad->ad_uaddr),
+ (struct sockaddr_in *)&(ad->ad_syncaddr))) {
+#endif
+ /*
+ * Hope the clocks are synced!
+ */
+ ad->ad_timediff.tv_sec = ad->ad_timediff.tv_usec = 0;
+ ad->ad_dosync = 0;
+ debug("authdes_refresh: unable to synchronize with server");
+ }
+ ad->ad_xkey = auth->ah_key;
+ pkey.n_bytes = (char *)(ad->ad_pkey);
+ pkey.n_len = strlen((char *)ad->ad_pkey) + 1;
+ if (key_encryptsession_pk(ad->ad_servername, &pkey, &ad->ad_xkey) < 0) {
+ debug("authdes_create: unable to encrypt conversation key");
+ return (FALSE);
+ }
+ cred->adc_fullname.key = ad->ad_xkey;
+ cred->adc_namekind = ADN_FULLNAME;
+ cred->adc_fullname.name = ad->ad_fullname;
+ return (TRUE);
+}
+
+
+/*
+ * 5. Destroy
+ */
+static void
+authdes_destroy(auth)
+ AUTH *auth;
+{
+ struct ad_private *ad = AUTH_PRIVATE(auth);
+
+ FREE(ad->ad_fullname, ad->ad_fullnamelen + 1);
+ FREE(ad->ad_servername, ad->ad_servernamelen + 1);
+ FREE(ad, sizeof(struct ad_private));
+ FREE(auth, sizeof(AUTH));
+}
+
+
+#ifdef old
+/*
+ * Synchronize with the server at the given address, that is,
+ * adjust timep to reflect the delta between our clocks
+ */
+static bool_t
+synchronize(syncaddr, timep)
+ struct sockaddr *syncaddr;
+ struct timeval *timep;
+{
+ struct timeval mytime;
+ struct timeval timeout;
+
+ timeout.tv_sec = RTIME_TIMEOUT;
+ timeout.tv_usec = 0;
+ if (rtime((struct sockaddr_in *)syncaddr, timep, NULL /*&timeout*/) < 0) {
+ return (FALSE);
+ }
+ (void) gettimeofday(&mytime, (struct timezone *)NULL);
+ timep->tv_sec -= mytime.tv_sec;
+ if (mytime.tv_usec > timep->tv_usec) {
+ timep->tv_sec -= 1;
+ timep->tv_usec += MILLION;
+ }
+ timep->tv_usec -= mytime.tv_usec;
+ return (TRUE);
+}
+#endif
Index: svc_tcp.c
===================================================================
--- svc_tcp.c (nonexistent)
+++ svc_tcp.c (revision 1765)
@@ -0,0 +1,481 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc_tcp.c 1.21 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc_tcp.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc_tcp.c,v 1.18 2000/01/27 23:06:41 jasone Exp $";
+#endif
+
+/*
+ * svc_tcp.c, Server side for TCP/IP based RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * Actually implements two flavors of transporter -
+ * a tcp rendezvouser (a listner and connection establisher)
+ * and a record/tcp stream.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Ops vector for TCP/IP based rpc service handle
+ */
+static bool_t svctcp_recv();
+static enum xprt_stat svctcp_stat();
+static bool_t svctcp_getargs();
+static bool_t svctcp_reply();
+static bool_t svctcp_freeargs();
+static void svctcp_destroy();
+
+static struct xp_ops svctcp_op = {
+ svctcp_recv,
+ svctcp_stat,
+ svctcp_getargs,
+ svctcp_reply,
+ svctcp_freeargs,
+ svctcp_destroy
+};
+
+/*
+ * Ops vector for TCP/IP rendezvous handler
+ */
+static bool_t rendezvous_request();
+static enum xprt_stat rendezvous_stat();
+
+static struct xp_ops svctcp_rendezvous_op = {
+ rendezvous_request,
+ rendezvous_stat,
+ (bool_t (*)())abort,
+ (bool_t (*)())abort,
+ (bool_t (*)())abort,
+ svctcp_destroy
+};
+
+static int readtcp(), writetcp();
+static SVCXPRT *makefd_xprt();
+
+struct tcp_rendezvous { /* kept in xprt->xp_p1 */
+ u_int sendsize;
+ u_int recvsize;
+};
+
+struct tcp_conn { /* kept in xprt->xp_p1 */
+ enum xprt_stat strm_stat;
+ u_long x_id;
+ XDR xdrs;
+ char verf_body[MAX_AUTH_BYTES];
+};
+
+/*
+ * Usage:
+ * xprt = svctcp_create(sock, send_buf_size, recv_buf_size);
+ *
+ * Creates, registers, and returns a (rpc) tcp based transporter.
+ * Once *xprt is initialized, it is registered as a transporter
+ * see (svc.h, xprt_register). This routine returns
+ * a NULL if a problem occurred.
+ *
+ * If sock<0 then a socket is created, else sock is used.
+ * If the socket, sock is not bound to a port then svctcp_create
+ * binds it to an arbitrary port. The routine then starts a tcp
+ * listener on the socket's associated port. In any (successful) case,
+ * xprt->xp_sock is the registered socket number and xprt->xp_port is the
+ * associated port number.
+ *
+ * Since tcp streams do buffered io similar to stdio, the caller can specify
+ * how big the send and receive buffers are via the second and third parms;
+ * 0 => use the system default.
+ */
+SVCXPRT *
+svctcp_create(sock, sendsize, recvsize)
+ register int sock;
+ u_int sendsize;
+ u_int recvsize;
+{
+ bool_t madesock = FALSE;
+ register SVCXPRT *xprt;
+ register struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+ int len = sizeof(struct sockaddr_in);
+ int on;
+
+ if (sock == RPC_ANYSOCK) {
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ perror("svctcp_.c - udp socket creation problem");
+ return ((SVCXPRT *)NULL);
+ }
+ madesock = TRUE;
+ }
+ on = 1;
+ if (ioctl(sock, FIONBIO, &on) < 0) {
+ perror("svc_tcp.c - cannot turn on non-blocking mode");
+ if (madesock)
+ (void)_RPC_close(sock);
+ return ((SVCXPRT *)NULL);
+ }
+ memset(&addr, 0, sizeof (addr));
+ addr.sin_len = sizeof(struct sockaddr_in);
+ addr.sin_family = AF_INET;
+ if (bindresvport(sock, &addr)) {
+ addr.sin_port = 0;
+ (void)bind(sock, (struct sockaddr *)&addr, len);
+ }
+ if ((getsockname(sock, (struct sockaddr *)&addr, &len) != 0) ||
+ (listen(sock, 2) != 0)) {
+ perror("svctcp_.c - cannot getsockname or listen");
+ if (madesock)
+ (void)_RPC_close(sock);
+ return ((SVCXPRT *)NULL);
+ }
+ r = (struct tcp_rendezvous *)mem_alloc(sizeof(*r));
+ if (r == NULL) {
+ (void) fprintf(stderr, "svctcp_create: out of memory\n");
+ return (NULL);
+ }
+ r->sendsize = sendsize;
+ r->recvsize = recvsize;
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == NULL) {
+ (void) fprintf(stderr, "svctcp_create: out of memory\n");
+ return (NULL);
+ }
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (caddr_t)r;
+ xprt->xp_verf = _null_auth;
+ xprt->xp_ops = &svctcp_rendezvous_op;
+ xprt->xp_port = ntohs(addr.sin_port);
+ xprt->xp_sock = sock;
+ xprt_register(xprt);
+ return (xprt);
+}
+
+/*
+ * Like svtcp_create(), except the routine takes any *open* UNIX file
+ * descriptor as its first input.
+ */
+SVCXPRT *
+svcfd_create(fd, sendsize, recvsize)
+ int fd;
+ u_int sendsize;
+ u_int recvsize;
+{
+
+ return (makefd_xprt(fd, sendsize, recvsize));
+}
+
+static SVCXPRT *
+makefd_xprt(fd, sendsize, recvsize)
+ int fd;
+ u_int sendsize;
+ u_int recvsize;
+{
+ register SVCXPRT *xprt;
+ register struct tcp_conn *cd;
+
+ xprt = (SVCXPRT *)mem_alloc(sizeof(SVCXPRT));
+ if (xprt == (SVCXPRT *)NULL) {
+ (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n");
+ goto done;
+ }
+ cd = (struct tcp_conn *)mem_alloc(sizeof(struct tcp_conn));
+ if (cd == (struct tcp_conn *)NULL) {
+ (void) fprintf(stderr, "svc_tcp: makefd_xprt: out of memory\n");
+ mem_free((char *) xprt, sizeof(SVCXPRT));
+ xprt = (SVCXPRT *)NULL;
+ goto done;
+ }
+ cd->strm_stat = XPRT_IDLE;
+ xdrrec_create(&(cd->xdrs), sendsize, recvsize,
+ (caddr_t)xprt, readtcp, writetcp);
+ xprt->xp_p2 = NULL;
+ xprt->xp_p1 = (caddr_t)cd;
+ xprt->xp_verf.oa_base = cd->verf_body;
+ xprt->xp_addrlen = 0;
+ xprt->xp_ops = &svctcp_op; /* truely deals with calls */
+ xprt->xp_port = 0; /* this is a connection, not a rendezvouser */
+ xprt->xp_sock = fd;
+ xprt_register(xprt);
+ done:
+ return (xprt);
+}
+
+static bool_t
+rendezvous_request(xprt)
+ register SVCXPRT *xprt;
+{
+ int sock;
+ struct tcp_rendezvous *r;
+ struct sockaddr_in addr;
+ int len;
+ int off;
+
+ r = (struct tcp_rendezvous *)xprt->xp_p1;
+ again:
+ len = sizeof(struct sockaddr_in);
+ if ((sock = accept(xprt->xp_sock, (struct sockaddr *)&addr,
+ &len)) < 0) {
+ if (errno == EINTR)
+ goto again;
+ return (FALSE);
+ }
+ /*
+ * Guard against FTP bounce attacks.
+ */
+ if (addr.sin_port == htons(20)) {
+ _RPC_close(sock);
+ return (FALSE);
+ }
+ /*
+ * The listening socket is in FIONBIO mode and we inherit it.
+ */
+ off = 0;
+ if (ioctl(sock, FIONBIO, &off) < 0) {
+ _RPC_close(sock);
+ return (FALSE);
+ }
+ /*
+ * make a new transporter (re-uses xprt)
+ */
+ xprt = makefd_xprt(sock, r->sendsize, r->recvsize);
+ xprt->xp_raddr = addr;
+ xprt->xp_addrlen = len;
+ return (FALSE); /* there is never an rpc msg to be processed */
+}
+
+static enum xprt_stat
+rendezvous_stat()
+{
+
+ return (XPRT_IDLE);
+}
+
+static void
+svctcp_destroy(xprt)
+ register SVCXPRT *xprt;
+{
+ register struct tcp_conn *cd = (struct tcp_conn *)xprt->xp_p1;
+
+ xprt_unregister(xprt);
+ (void)_RPC_close(xprt->xp_sock);
+ if (xprt->xp_port != 0) {
+ /* a rendezvouser socket */
+ xprt->xp_port = 0;
+ } else {
+ /* an actual connection socket */
+ XDR_DESTROY(&(cd->xdrs));
+ }
+ mem_free((caddr_t)cd, sizeof(struct tcp_conn));
+ mem_free((caddr_t)xprt, sizeof(SVCXPRT));
+}
+
+/*
+ * All read operations timeout after 35 seconds.
+ * A timeout is fatal for the connection.
+ */
+static struct timeval wait_per_try = { 35, 0 };
+
+/*
+ * reads data from the tcp conection.
+ * any error is fatal and the connection is closed.
+ * (And a read of zero bytes is a half closed stream => error.)
+ *
+ * Note: we have to be careful here not to allow ourselves to become
+ * blocked too long in this routine. While we're waiting for data from one
+ * client, another client may be trying to connect. To avoid this situation,
+ * some code from svc_run() is transplanted here: the select() loop checks
+ * all RPC descriptors including the one we want and calls svc_getreqset2()
+ * to handle new requests if any are detected.
+ */
+static int
+readtcp(xprt, buf, len)
+ register SVCXPRT *xprt;
+ caddr_t buf;
+ register int len;
+{
+ register int sock = xprt->xp_sock;
+ struct timeval start, delta, tv;
+ struct timeval tmp1, tmp2;
+ fd_set *fds;
+
+ delta = wait_per_try;
+ fds = NULL;
+ gettimeofday(&start, NULL);
+ do {
+ int bytes = sizeof (fd_set);
+ if (fds != NULL)
+ free(fds);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL)
+ goto fatal_err;
+ memcpy(fds, __svc_fdset, bytes);
+
+ /* XXX we know the other bits are still clear */
+ FD_SET(sock, fds);
+ tv = delta; /* in case select() implements writeback */
+ switch (select(svc_maxfd + 1, fds, NULL, NULL, &tv)) {
+ case -1:
+ if (errno != EINTR)
+ goto fatal_err;
+ gettimeofday(&tmp1, NULL);
+ timersub(&tmp1, &start, &tmp2);
+ timersub(&wait_per_try, &tmp2, &tmp1);
+ if (tmp1.tv_sec < 0 || !timerisset(&tmp1))
+ goto fatal_err;
+ delta = tmp1;
+ continue;
+ case 0:
+ goto fatal_err;
+ default:
+ if (!FD_ISSET(sock, fds)) {
+ svc_getreqset2(fds, svc_maxfd + 1);
+ gettimeofday(&tmp1, NULL);
+ timersub(&tmp1, &start, &tmp2);
+ timersub(&wait_per_try, &tmp2, &tmp1);
+ if (tmp1.tv_sec < 0 || !timerisset(&tmp1))
+ goto fatal_err;
+ delta = tmp1;
+ continue;
+ }
+ }
+ } while (!FD_ISSET(sock, fds));
+ if ((len = _RPC_read(sock, buf, len)) > 0) {
+ if (fds != NULL)
+ free(fds);
+ return (len);
+ }
+fatal_err:
+ ((struct tcp_conn *)(xprt->xp_p1))->strm_stat = XPRT_DIED;
+ if (fds != NULL)
+ free(fds);
+ return (-1);
+}
+
+/*
+ * writes data to the tcp connection.
+ * Any error is fatal and the connection is closed.
+ */
+static int
+writetcp(xprt, buf, len)
+ register SVCXPRT *xprt;
+ caddr_t buf;
+ int len;
+{
+ register int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i) {
+ if ((i = _RPC_write(xprt->xp_sock, buf, cnt)) < 0) {
+ ((struct tcp_conn *)(xprt->xp_p1))->strm_stat =
+ XPRT_DIED;
+ return (-1);
+ }
+ }
+ return (len);
+}
+
+static enum xprt_stat
+svctcp_stat(xprt)
+ SVCXPRT *xprt;
+{
+ register struct tcp_conn *cd =
+ (struct tcp_conn *)(xprt->xp_p1);
+
+ if (cd->strm_stat == XPRT_DIED)
+ return (XPRT_DIED);
+ if (! xdrrec_eof(&(cd->xdrs)))
+ return (XPRT_MOREREQS);
+ return (XPRT_IDLE);
+}
+
+static bool_t
+svctcp_recv(xprt, msg)
+ SVCXPRT *xprt;
+ register struct rpc_msg *msg;
+{
+ register struct tcp_conn *cd =
+ (struct tcp_conn *)(xprt->xp_p1);
+ register XDR *xdrs = &(cd->xdrs);
+
+ xdrs->x_op = XDR_DECODE;
+ (void)xdrrec_skiprecord(xdrs);
+ if (xdr_callmsg(xdrs, msg)) {
+ cd->x_id = msg->rm_xid;
+ return (TRUE);
+ }
+ cd->strm_stat = XPRT_DIED; /* XXXX */
+ return (FALSE);
+}
+
+static bool_t
+svctcp_getargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+
+ return ((*xdr_args)(&(((struct tcp_conn *)(xprt->xp_p1))->xdrs), args_ptr));
+}
+
+static bool_t
+svctcp_freeargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+ register XDR *xdrs =
+ &(((struct tcp_conn *)(xprt->xp_p1))->xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args)(xdrs, args_ptr));
+}
+
+static bool_t
+svctcp_reply(xprt, msg)
+ SVCXPRT *xprt;
+ register struct rpc_msg *msg;
+{
+ register struct tcp_conn *cd =
+ (struct tcp_conn *)(xprt->xp_p1);
+ register XDR *xdrs = &(cd->xdrs);
+ register bool_t stat;
+
+ xdrs->x_op = XDR_ENCODE;
+ msg->rm_xid = cd->x_id;
+ stat = xdr_replymsg(xdrs, msg);
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ return (stat);
+}
Index: pmap_getport.c
===================================================================
--- pmap_getport.c (nonexistent)
+++ pmap_getport.c (revision 1765)
@@ -0,0 +1,91 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)pmap_getport.c 1.9 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)pmap_getport.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/pmap_getport.c,v 1.10 2000/01/27 23:06:40 jasone Exp $";
+#endif
+
+/*
+ * pmap_getport.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+static struct timeval timeout = { 5, 0 };
+static struct timeval tottimeout = { 60, 0 };
+
+/*
+ * Find the mapped port for program,version.
+ * Calls the pmap service remotely to do the lookup.
+ * Returns 0 if no map exists.
+ */
+u_short
+pmap_getport(address, program, version, protocol)
+ struct sockaddr_in *address;
+ u_long program;
+ u_long version;
+ u_int protocol;
+{
+ u_short port = 0;
+ int socket = -1;
+ register CLIENT *client;
+ struct pmap parms;
+
+ address->sin_port = htons(PMAPPORT);
+ client = clntudp_bufcreate(address, PMAPPROG,
+ PMAPVERS, timeout, &socket, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client != (CLIENT *)NULL) {
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = 0; /* not needed or used */
+ if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
+ xdr_u_short, &port, tottimeout) != RPC_SUCCESS){
+ rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ } else if (port == 0) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ CLNT_DESTROY(client);
+ }
+ if (socket != -1)
+ (void)_RPC_close(socket);
+ address->sin_port = 0;
+ return (port);
+}
Index: svc_simple.c
===================================================================
--- svc_simple.c (nonexistent)
+++ svc_simple.c (revision 1765)
@@ -0,0 +1,151 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc_simple.c 1.18 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc_simple.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc_simple.c,v 1.9 1999/08/28 00:00:50 peter Exp $";
+#endif
+
+/*
+ * svc_simple.c
+ * Simplified front end to rpc.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct prog_lst {
+ char *(*p_progname)();
+ int p_prognum;
+ int p_procnum;
+ xdrproc_t p_inproc, p_outproc;
+ struct prog_lst *p_nxt;
+};
+static void universal();
+#define proglst ((struct prog_lst *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_simple_proglst)
+#define pl ((struct prog_lst *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_simple_pl)
+#define transp ((SVCXPRT *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_simple_transp)
+
+int
+registerrpc(prognum, versnum, procnum, progname, inproc, outproc)
+ int prognum, versnum, procnum;
+ char *(*progname)();
+ xdrproc_t inproc, outproc;
+{
+
+ if (procnum == NULLPROC) {
+ (void) fprintf(stderr,
+ "can't reassign procedure number %ld\n", NULLPROC);
+ return (-1);
+ }
+ if (transp == 0) {
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL) {
+ (void) fprintf(stderr, "couldn't create an rpc server\n");
+ return (-1);
+ }
+ }
+ (void) pmap_unset((u_long)prognum, (u_long)versnum);
+ if (!svc_register(transp, (u_long)prognum, (u_long)versnum,
+ universal, IPPROTO_UDP)) {
+ (void) fprintf(stderr, "couldn't register prog %d vers %d\n",
+ prognum, versnum);
+ return (-1);
+ }
+ pl = (struct prog_lst *)malloc(sizeof(struct prog_lst));
+ if (pl == NULL) {
+ (void) fprintf(stderr, "registerrpc: out of memory\n");
+ return (-1);
+ }
+ pl->p_progname = progname;
+ pl->p_prognum = prognum;
+ pl->p_procnum = procnum;
+ pl->p_inproc = inproc;
+ pl->p_outproc = outproc;
+ pl->p_nxt = proglst;
+ proglst = pl;
+ return (0);
+}
+
+static void
+universal(rqstp, atransp)
+ struct svc_req *rqstp;
+ SVCXPRT *atransp;
+{
+ int prog, proc;
+ char *outdata;
+ char xdrbuf[UDPMSGSIZE];
+ struct prog_lst *lpl;
+
+ /*
+ * enforce "procnum 0 is echo" convention
+ */
+ if (rqstp->rq_proc == NULLPROC) {
+ if (svc_sendreply(atransp, xdr_void, NULL) == FALSE) {
+ (void) fprintf(stderr, "xxx\n");
+ exit(1);
+ }
+ return;
+ }
+ prog = rqstp->rq_prog;
+ proc = rqstp->rq_proc;
+ for (lpl = proglst; lpl != NULL; lpl = lpl->p_nxt)
+ if (lpl->p_prognum == prog && lpl->p_procnum == proc) {
+ /* decode arguments into a CLEAN buffer */
+ memset(xdrbuf, 0, sizeof(xdrbuf)); /* required ! */
+ if (!svc_getargs(atransp, lpl->p_inproc, xdrbuf)) {
+ svcerr_decode(atransp);
+ return;
+ }
+ outdata = (*(lpl->p_progname))(xdrbuf);
+ if (outdata == NULL && lpl->p_outproc != xdr_void)
+ /* there was an error */
+ return;
+ if (!svc_sendreply(atransp, lpl->p_outproc, outdata)) {
+ (void) fprintf(stderr,
+ "trouble replying to prog %d\n",
+ lpl->p_prognum);
+ exit(1);
+ }
+ /* free the decoded arguments */
+ (void)svc_freeargs(atransp, lpl->p_inproc, xdrbuf);
+ return;
+ }
+ (void) fprintf(stderr, "never registered prog %d\n", prog);
+ exit(1);
+}
+
Index: svc_raw.c
===================================================================
--- svc_raw.c (nonexistent)
+++ svc_raw.c (revision 1765)
@@ -0,0 +1,169 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)svc_raw.c 1.15 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)svc_raw.c 2.1 88/07/29 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/svc_raw.c,v 1.7 1999/08/28 00:00:49 peter Exp $";
+#endif
+
+/*
+ * svc_raw.c, This a toy for simple testing and timing.
+ * Interface to create an rpc client and server in the same UNIX process.
+ * This lets us similate rpc and get rpc (round trip) overhead, without
+ * any interference from the kernal.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+
+/*
+ * This is the "network" that we will be moving data over
+ */
+struct svc_raw_private {
+ char _raw_buf[UDPMSGSIZE];
+ SVCXPRT server;
+ XDR xdr_stream;
+ char verf_body[MAX_AUTH_BYTES];
+};
+#define svcraw_private ((struct svc_raw_private *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->svc_raw_private)
+
+static bool_t svcraw_recv();
+static enum xprt_stat svcraw_stat();
+static bool_t svcraw_getargs();
+static bool_t svcraw_reply();
+static bool_t svcraw_freeargs();
+static void svcraw_destroy();
+
+static struct xp_ops server_ops = {
+ svcraw_recv,
+ svcraw_stat,
+ svcraw_getargs,
+ svcraw_reply,
+ svcraw_freeargs,
+ svcraw_destroy
+};
+
+SVCXPRT *
+svcraw_create()
+{
+ register struct svc_raw_private *srp = svcraw_private;
+
+ if (srp == 0) {
+ srp = (struct svc_raw_private *)calloc(1, sizeof (*srp));
+ if (srp == 0)
+ return (0);
+ }
+ srp->server.xp_sock = 0;
+ srp->server.xp_port = 0;
+ srp->server.xp_ops = &server_ops;
+ srp->server.xp_verf.oa_base = srp->verf_body;
+ xdrmem_create(&srp->xdr_stream, srp->_raw_buf, UDPMSGSIZE, XDR_FREE);
+ return (&srp->server);
+}
+
+static enum xprt_stat
+svcraw_stat()
+{
+
+ return (XPRT_IDLE);
+}
+
+static bool_t
+svcraw_recv(xprt, msg)
+ SVCXPRT *xprt;
+ struct rpc_msg *msg;
+{
+ register struct svc_raw_private *srp = svcraw_private;
+ register XDR *xdrs;
+
+ if (srp == 0)
+ return (0);
+ xdrs = &srp->xdr_stream;
+ xdrs->x_op = XDR_DECODE;
+ XDR_SETPOS(xdrs, 0);
+ if (! xdr_callmsg(xdrs, msg))
+ return (FALSE);
+ return (TRUE);
+}
+
+static bool_t
+svcraw_reply(xprt, msg)
+ SVCXPRT *xprt;
+ struct rpc_msg *msg;
+{
+ register struct svc_raw_private *srp = svcraw_private;
+ register XDR *xdrs;
+
+ if (srp == 0)
+ return (FALSE);
+ xdrs = &srp->xdr_stream;
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, 0);
+ if (! xdr_replymsg(xdrs, msg))
+ return (FALSE);
+ (void)XDR_GETPOS(xdrs); /* called just for overhead */
+ return (TRUE);
+}
+
+static bool_t
+svcraw_getargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+ register struct svc_raw_private *srp = svcraw_private;
+
+ if (srp == 0)
+ return (FALSE);
+ return ((*xdr_args)(&srp->xdr_stream, args_ptr));
+}
+
+static bool_t
+svcraw_freeargs(xprt, xdr_args, args_ptr)
+ SVCXPRT *xprt;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+{
+ register struct svc_raw_private *srp = svcraw_private;
+ register XDR *xdrs;
+
+ if (srp == 0)
+ return (FALSE);
+ xdrs = &srp->xdr_stream;
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_args)(xdrs, args_ptr));
+}
+
+static void
+svcraw_destroy()
+{
+}
Index: auth_unix.c
===================================================================
--- auth_unix.c (nonexistent)
+++ auth_unix.c (revision 1765)
@@ -0,0 +1,349 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)auth_unix.c 1.19 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)auth_unix.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/auth_unix.c,v 1.12 1999/12/29 05:04:16 peter Exp $";
+#endif
+
+/*
+ * auth_unix.c, Implements UNIX style authentication parameters.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * The system is very weak. The client uses no encryption for it's
+ * credentials and only sends null verifiers. The server sends backs
+ * null verifiers or optionally a verifier that suggests a new short hand
+ * for the credentials.
+ *
+ */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Unix authenticator operations vector
+ */
+static void authunix_nextverf();
+static bool_t authunix_marshal();
+static bool_t authunix_validate();
+static bool_t authunix_refresh();
+static void authunix_destroy();
+
+static struct auth_ops auth_unix_ops = {
+ authunix_nextverf,
+ authunix_marshal,
+ authunix_validate,
+ authunix_refresh,
+ authunix_destroy
+};
+
+/*
+ * This struct is pointed to by the ah_private field of an auth_handle.
+ */
+struct audata {
+ struct opaque_auth au_origcred; /* original credentials */
+ struct opaque_auth au_shcred; /* short hand cred */
+ u_long au_shfaults; /* short hand cache faults */
+ char au_marshed[MAX_AUTH_BYTES];
+ u_int au_mpos; /* xdr pos at end of marshed */
+};
+#define AUTH_PRIVATE(auth) ((struct audata *)auth->ah_private)
+
+static void marshal_new_auth();
+
+/*
+ * This goop is here because some servers refuse to accept a
+ * credential with more than some number (usually 8) supplementary
+ * groups. Blargh!
+ */
+static int authunix_maxgrouplist = 0;
+
+void
+set_rpc_maxgrouplist(int num)
+{
+ authunix_maxgrouplist = num;
+}
+
+/*
+ * Create a unix style authenticator.
+ * Returns an auth handle with the given stuff in it.
+ */
+AUTH *
+authunix_create(machname, uid, gid, len, aup_gids)
+ char *machname;
+ int uid;
+ int gid;
+ register int len;
+ int *aup_gids;
+{
+ struct authunix_parms aup;
+ char mymem[MAX_AUTH_BYTES];
+ struct timeval now;
+ XDR xdrs;
+ register AUTH *auth;
+ register struct audata *au;
+
+ /*
+ * Allocate and set up auth handle
+ */
+ auth = (AUTH *)mem_alloc(sizeof(*auth));
+#ifndef _KERNEL
+ if (auth == NULL) {
+ (void)fprintf(stderr, "authunix_create: out of memory\n");
+ return (NULL);
+ }
+#endif
+ au = (struct audata *)mem_alloc(sizeof(*au));
+#ifndef _KERNEL
+ if (au == NULL) {
+ (void)fprintf(stderr, "authunix_create: out of memory\n");
+ return (NULL);
+ }
+#endif
+ auth->ah_ops = &auth_unix_ops;
+ auth->ah_private = (caddr_t)au;
+ auth->ah_verf = au->au_shcred = _null_auth;
+ au->au_shfaults = 0;
+
+ /*
+ * fill in param struct from the given params
+ */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ aup.aup_time = now.tv_sec;
+ aup.aup_machname = machname;
+ aup.aup_uid = uid;
+ aup.aup_gid = gid;
+ /* GW: continuation of max group list hack */
+ if(authunix_maxgrouplist != 0) {
+ aup.aup_len = ((len < authunix_maxgrouplist) ? len
+ : authunix_maxgrouplist);
+ } else {
+ aup.aup_len = (u_int)len;
+ }
+ aup.aup_gids = aup_gids;
+
+ /*
+ * Serialize the parameters into origcred
+ */
+ xdrmem_create(&xdrs, mymem, MAX_AUTH_BYTES, XDR_ENCODE);
+ if (! xdr_authunix_parms(&xdrs, &aup))
+ abort();
+ au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs);
+ au->au_origcred.oa_flavor = AUTH_UNIX;
+#ifdef _KERNEL
+ au->au_origcred.oa_base = mem_alloc((u_int) len);
+#else
+ if ((au->au_origcred.oa_base = mem_alloc((u_int) len)) == NULL) {
+ (void)fprintf(stderr, "authunix_create: out of memory\n");
+ return (NULL);
+ }
+#endif
+ memcpy(au->au_origcred.oa_base, mymem, (u_int)len);
+
+ /*
+ * set auth handle to reflect new cred.
+ */
+ auth->ah_cred = au->au_origcred;
+ marshal_new_auth(auth);
+ return (auth);
+}
+
+/*
+ * Returns an auth handle with parameters determined by doing lots of
+ * syscalls.
+ */
+AUTH *
+authunix_create_default()
+{
+ register int len;
+ char machname[MAX_MACHINE_NAME + 1];
+ register int uid;
+ register int gid;
+ int gids[NGRPS];
+ int i;
+ gid_t real_gids[NGROUPS];
+
+ if (gethostname(machname, MAX_MACHINE_NAME) == -1)
+ abort();
+ machname[MAX_MACHINE_NAME] = 0;
+ uid = (int)geteuid();
+ gid = (int)getegid();
+ if ((len = getgroups(NGROUPS, real_gids)) < 0)
+ abort();
+ if(len > NGRPS) len = NGRPS; /* GW: turn `gid_t's into `int's */
+ for(i = 0; i < len; i++) {
+ gids[i] = (int)real_gids[i];
+ }
+ return (authunix_create(machname, uid, gid, len, gids));
+}
+
+/*
+ * authunix operations
+ */
+
+static void
+authunix_nextverf(auth)
+ AUTH *auth;
+{
+ /* no action necessary */
+}
+
+static bool_t
+authunix_marshal(auth, xdrs)
+ AUTH *auth;
+ XDR *xdrs;
+{
+ register struct audata *au = AUTH_PRIVATE(auth);
+
+ return (XDR_PUTBYTES(xdrs, au->au_marshed, au->au_mpos));
+}
+
+static bool_t
+authunix_validate(auth, verf)
+ register AUTH *auth;
+ struct opaque_auth verf;
+{
+ register struct audata *au;
+ XDR xdrs;
+
+ if (verf.oa_flavor == AUTH_SHORT) {
+ au = AUTH_PRIVATE(auth);
+ xdrmem_create(&xdrs, verf.oa_base, verf.oa_length, XDR_DECODE);
+
+ if (au->au_shcred.oa_base != NULL) {
+ mem_free(au->au_shcred.oa_base,
+ au->au_shcred.oa_length);
+ au->au_shcred.oa_base = NULL;
+ }
+ if (xdr_opaque_auth(&xdrs, &au->au_shcred)) {
+ auth->ah_cred = au->au_shcred;
+ } else {
+ xdrs.x_op = XDR_FREE;
+ (void)xdr_opaque_auth(&xdrs, &au->au_shcred);
+ au->au_shcred.oa_base = NULL;
+ auth->ah_cred = au->au_origcred;
+ }
+ marshal_new_auth(auth);
+ }
+ return (TRUE);
+}
+
+static bool_t
+authunix_refresh(auth)
+ register AUTH *auth;
+{
+ register struct audata *au = AUTH_PRIVATE(auth);
+ struct authunix_parms aup;
+ struct timeval now;
+ XDR xdrs;
+ register int stat;
+
+ if (auth->ah_cred.oa_base == au->au_origcred.oa_base) {
+ /* there is no hope. Punt */
+ return (FALSE);
+ }
+ au->au_shfaults ++;
+
+ /* first deserialize the creds back into a struct authunix_parms */
+ aup.aup_machname = NULL;
+ aup.aup_gids = (int *)NULL;
+ xdrmem_create(&xdrs, au->au_origcred.oa_base,
+ au->au_origcred.oa_length, XDR_DECODE);
+ stat = xdr_authunix_parms(&xdrs, &aup);
+ if (! stat)
+ goto done;
+
+ /* update the time and serialize in place */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ aup.aup_time = now.tv_sec;
+ xdrs.x_op = XDR_ENCODE;
+ XDR_SETPOS(&xdrs, 0);
+ stat = xdr_authunix_parms(&xdrs, &aup);
+ if (! stat)
+ goto done;
+ auth->ah_cred = au->au_origcred;
+ marshal_new_auth(auth);
+done:
+ /* free the struct authunix_parms created by deserializing */
+ xdrs.x_op = XDR_FREE;
+ (void)xdr_authunix_parms(&xdrs, &aup);
+ XDR_DESTROY(&xdrs);
+ return (stat);
+}
+
+static void
+authunix_destroy(auth)
+ register AUTH *auth;
+{
+ register struct audata *au = AUTH_PRIVATE(auth);
+
+ mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length);
+
+ if (au->au_shcred.oa_base != NULL)
+ mem_free(au->au_shcred.oa_base, au->au_shcred.oa_length);
+
+ mem_free(auth->ah_private, sizeof(struct audata));
+
+ if (auth->ah_verf.oa_base != NULL)
+ mem_free(auth->ah_verf.oa_base, auth->ah_verf.oa_length);
+
+ mem_free((caddr_t)auth, sizeof(*auth));
+}
+
+/*
+ * Marshals (pre-serializes) an auth struct.
+ * sets private data, au_marshed and au_mpos
+ */
+static void
+marshal_new_auth(auth)
+ register AUTH *auth;
+{
+ XDR xdr_stream;
+ register XDR *xdrs = &xdr_stream;
+ register struct audata *au = AUTH_PRIVATE(auth);
+
+ xdrmem_create(xdrs, au->au_marshed, MAX_AUTH_BYTES, XDR_ENCODE);
+ if ((! xdr_opaque_auth(xdrs, &(auth->ah_cred))) ||
+ (! xdr_opaque_auth(xdrs, &(auth->ah_verf)))) {
+ perror("auth_none.c - Fatal marshalling problem");
+ } else {
+ au->au_mpos = XDR_GETPOS(xdrs);
+ }
+ XDR_DESTROY(xdrs);
+}
Index: clnt_tcp.c
===================================================================
--- clnt_tcp.c (nonexistent)
+++ clnt_tcp.c (revision 1765)
@@ -0,0 +1,580 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)clnt_tcp.c 1.37 87/10/05 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)clnt_tcp.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_tcp.c,v 1.14 2000/01/27 23:06:36 jasone Exp $";
+#endif
+
+/*
+ * clnt_tcp.c, Implements a TCP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * TCP based RPC supports 'batched calls'.
+ * A sequence of calls may be batched-up in a send buffer. The rpc call
+ * return immediately to the client even though the call was not necessarily
+ * sent. The batching occurs if the results' xdr routine is NULL (0) AND
+ * the rpc timeout value is zero (see clnt.h, rpc).
+ *
+ * Clients should NOT casually batch calls that in fact return results; that is,
+ * the server side should be aware that a call is batched and not produce any
+ * return message. Batched calls that produce many result messages can
+ * deadlock (netlock) the client and the server....
+ *
+ * Now go hang yourself.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MCALL_MSG_SIZE 24
+
+static int readtcp();
+static int writetcp();
+
+static enum clnt_stat clnttcp_call();
+static void clnttcp_abort();
+static void clnttcp_geterr();
+static bool_t clnttcp_freeres();
+static bool_t clnttcp_control();
+static void clnttcp_destroy();
+
+static struct clnt_ops tcp_ops = {
+ clnttcp_call,
+ clnttcp_abort,
+ clnttcp_geterr,
+ clnttcp_freeres,
+ clnttcp_destroy,
+ clnttcp_control
+};
+
+struct ct_data {
+ int ct_sock;
+ bool_t ct_closeit;
+ struct timeval ct_wait;
+ bool_t ct_waitset; /* wait set by clnt_control? */
+ struct sockaddr_in ct_addr;
+ struct rpc_err ct_error;
+ char ct_mcall[MCALL_MSG_SIZE]; /* marshalled callmsg */
+ u_int ct_mpos; /* pos after marshal */
+ XDR ct_xdrs;
+};
+
+/*
+ * Create a client handle for a tcp/ip connection.
+ * If *sockp<0, *sockp is set to a newly created TCP socket and it is
+ * connected to raddr. If *sockp non-negative then
+ * raddr is ignored. The rpc/tcp package does buffering
+ * similar to stdio, so the client must pick send and receive buffer sizes,];
+ * 0 => use the default.
+ * If raddr->sin_port is 0, then a binder on the remote machine is
+ * consulted for the right port number.
+ * NB: *sockp is copied into a private area.
+ * NB: It is the clients responsibility to close *sockp.
+ * NB: The rpch->cl_auth is set null authentication. Caller may wish to set this
+ * something more useful.
+ */
+CLIENT *
+clnttcp_create(raddr, prog, vers, sockp, sendsz, recvsz)
+ struct sockaddr_in *raddr;
+ u_long prog;
+ u_long vers;
+ register int *sockp;
+ u_int sendsz;
+ u_int recvsz;
+{
+ CLIENT *h;
+ register struct ct_data *ct = NULL;
+ struct timeval now;
+ struct rpc_msg call_msg;
+ static u_int32_t disrupt;
+
+ if (disrupt == 0)
+ disrupt = (u_int32_t)(long)raddr;
+
+ h = (CLIENT *)mem_alloc(sizeof(*h));
+ if (h == NULL) {
+ (void)fprintf(stderr, "clnttcp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+ ct = (struct ct_data *)mem_alloc(sizeof(*ct));
+ if (ct == NULL) {
+ (void)fprintf(stderr, "clnttcp_create: out of memory\n");
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ goto fooy;
+ }
+
+ /*
+ * If no port number given ask the pmap for one
+ */
+ if (raddr->sin_port == 0) {
+ u_short port;
+ if ((port = pmap_getport(raddr, prog, vers, IPPROTO_TCP)) == 0) {
+ mem_free((caddr_t)ct, sizeof(struct ct_data));
+ mem_free((caddr_t)h, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+ }
+ raddr->sin_port = htons(port);
+ }
+
+ /*
+ * If no socket given, open one
+ */
+ if (*sockp < 0) {
+ *sockp = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ (void)bindresvport(*sockp, (struct sockaddr_in *)0);
+ if ((*sockp < 0)
+ || (connect(*sockp, (struct sockaddr *)raddr,
+ sizeof(*raddr)) < 0)) {
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (*sockp != -1)
+ (void)_RPC_close(*sockp);
+ goto fooy;
+ }
+ ct->ct_closeit = TRUE;
+ } else {
+ ct->ct_closeit = FALSE;
+ }
+
+ /*
+ * Set up private data struct
+ */
+ ct->ct_sock = *sockp;
+ ct->ct_wait.tv_usec = 0;
+ ct->ct_waitset = FALSE;
+ ct->ct_addr = *raddr;
+
+ /*
+ * Initialize call message
+ */
+ (void)gettimeofday(&now, (struct timezone *)0);
+ call_msg.rm_xid = (++disrupt) ^ getpid() ^ now.tv_sec ^ now.tv_usec;
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+
+ /*
+ * pre-serialize the static part of the call msg and stash it away
+ */
+ xdrmem_create(&(ct->ct_xdrs), ct->ct_mcall, MCALL_MSG_SIZE,
+ XDR_ENCODE);
+ if (! xdr_callhdr(&(ct->ct_xdrs), &call_msg)) {
+ if (ct->ct_closeit) {
+ (void)_RPC_close(*sockp);
+ }
+ goto fooy;
+ }
+ ct->ct_mpos = XDR_GETPOS(&(ct->ct_xdrs));
+ XDR_DESTROY(&(ct->ct_xdrs));
+
+ /*
+ * Create a client handle which uses xdrrec for serialization
+ * and authnone for authentication.
+ */
+ xdrrec_create(&(ct->ct_xdrs), sendsz, recvsz,
+ (caddr_t)ct, readtcp, writetcp);
+ h->cl_ops = &tcp_ops;
+ h->cl_private = (caddr_t) ct;
+ h->cl_auth = authnone_create();
+ return (h);
+
+fooy:
+ /*
+ * Something goofed, free stuff and barf
+ */
+ if (ct)
+ mem_free((caddr_t)ct, sizeof(struct ct_data));
+ if (h)
+ mem_free((caddr_t)h, sizeof(CLIENT));
+ return ((CLIENT *)NULL);
+}
+
+static enum clnt_stat
+clnttcp_call(h, proc, xdr_args, args_ptr, xdr_results, results_ptr, timeout)
+ register CLIENT *h;
+ u_long proc;
+ xdrproc_t xdr_args;
+ caddr_t args_ptr;
+ xdrproc_t xdr_results;
+ caddr_t results_ptr;
+ struct timeval timeout;
+{
+ register struct ct_data *ct = (struct ct_data *) h->cl_private;
+ register XDR *xdrs = &(ct->ct_xdrs);
+ struct rpc_msg reply_msg;
+ u_long x_id;
+ u_int32_t *msg_x_id = (u_int32_t *)(ct->ct_mcall); /* yuk */
+ register bool_t shipnow;
+ int refreshes = 2;
+
+ if (!ct->ct_waitset) {
+ ct->ct_wait = timeout;
+ }
+
+ shipnow =
+ (xdr_results == (xdrproc_t)0 && timeout.tv_sec == 0
+ && timeout.tv_usec == 0) ? FALSE : TRUE;
+
+call_again:
+ xdrs->x_op = XDR_ENCODE;
+ ct->ct_error.re_status = RPC_SUCCESS;
+ x_id = ntohl(--(*msg_x_id));
+ if ((! XDR_PUTBYTES(xdrs, ct->ct_mcall, ct->ct_mpos)) ||
+ (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
+ (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
+ (! (*xdr_args)(xdrs, args_ptr))) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTENCODEARGS;
+ (void)xdrrec_endofrecord(xdrs, TRUE);
+ return (ct->ct_error.re_status);
+ }
+ if (! xdrrec_endofrecord(xdrs, shipnow))
+ return (ct->ct_error.re_status = RPC_CANTSEND);
+ if (! shipnow)
+ return (RPC_SUCCESS);
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) {
+ return(ct->ct_error.re_status = RPC_TIMEDOUT);
+ }
+
+
+ /*
+ * Keep receiving until we get a valid transaction id
+ */
+ xdrs->x_op = XDR_DECODE;
+ while (TRUE) {
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = NULL;
+ reply_msg.acpted_rply.ar_results.proc = xdr_void;
+ if (! xdrrec_skiprecord(xdrs))
+ return (ct->ct_error.re_status);
+ /* now decode and validate the response header */
+ if (! xdr_replymsg(xdrs, &reply_msg)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ continue;
+ return (ct->ct_error.re_status);
+ }
+ if (reply_msg.rm_xid == x_id)
+ break;
+ }
+
+ /*
+ * process header
+ */
+ _seterr_reply(&reply_msg, &(ct->ct_error));
+ if (ct->ct_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(h->cl_auth, &reply_msg.acpted_rply.ar_verf)) {
+ ct->ct_error.re_status = RPC_AUTHERROR;
+ ct->ct_error.re_why = AUTH_INVALIDRESP;
+ } else if (! (*xdr_results)(xdrs, results_ptr)) {
+ if (ct->ct_error.re_status == RPC_SUCCESS)
+ ct->ct_error.re_status = RPC_CANTDECODERES;
+ }
+ /* free verifier ... */
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs, &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else {
+ /* maybe our credentials need to be refreshed ... */
+ if (refreshes-- && AUTH_REFRESH(h->cl_auth))
+ goto call_again;
+ } /* end of unsuccessful completion */
+ return (ct->ct_error.re_status);
+}
+
+static void
+clnttcp_geterr(h, errp)
+ CLIENT *h;
+ struct rpc_err *errp;
+{
+ register struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ *errp = ct->ct_error;
+}
+
+static bool_t
+clnttcp_freeres(cl, xdr_res, res_ptr)
+ CLIENT *cl;
+ xdrproc_t xdr_res;
+ caddr_t res_ptr;
+{
+ register struct ct_data *ct = (struct ct_data *)cl->cl_private;
+ register XDR *xdrs = &(ct->ct_xdrs);
+
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+static void
+clnttcp_abort()
+{
+}
+
+
+static bool_t
+clnttcp_control(cl, request, info)
+ CLIENT *cl;
+ int request;
+ char *info;
+{
+ register struct ct_data *ct = (struct ct_data *)cl->cl_private;
+ register struct timeval *tv;
+ int len;
+
+ switch (request) {
+ case CLSET_FD_CLOSE:
+ ct->ct_closeit = TRUE;
+ break;
+ case CLSET_FD_NCLOSE:
+ ct->ct_closeit = FALSE;
+ break;
+ case CLSET_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ tv = (struct timeval *)info;
+ ct->ct_wait.tv_sec = tv->tv_sec;
+ ct->ct_wait.tv_usec = tv->tv_usec;
+ ct->ct_waitset = TRUE;
+ break;
+ case CLGET_TIMEOUT:
+ if (info == NULL)
+ return(FALSE);
+ *(struct timeval *)info = ct->ct_wait;
+ break;
+ case CLGET_SERVER_ADDR:
+ if (info == NULL)
+ return(FALSE);
+ *(struct sockaddr_in *)info = ct->ct_addr;
+ break;
+ case CLGET_FD:
+ if (info == NULL)
+ return(FALSE);
+ *(int *)info = ct->ct_sock;
+ break;
+ case CLGET_XID:
+ /*
+ * use the knowledge that xid is the
+ * first element in the call structure *.
+ * This will get the xid of the PREVIOUS call
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)ct->ct_mcall);
+ break;
+ case CLSET_XID:
+ /* This will set the xid of the NEXT call */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)ct->ct_mcall = htonl(*(u_long *)info - 1);
+ /* decrement by 1 as clnttcp_call() increments once */
+ case CLGET_VERS:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the version number field is the fifth field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
+ 4 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_VERS:
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)(ct->ct_mcall + 4 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ case CLGET_PROG:
+ /*
+ * This RELIES on the information that, in the call body,
+ * the program number field is the field from the
+ * begining of the RPC header. MUST be changed if the
+ * call_struct is changed
+ */
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)info = ntohl(*(u_long *)(ct->ct_mcall +
+ 3 * BYTES_PER_XDR_UNIT));
+ break;
+ case CLSET_PROG:
+ if (info == NULL)
+ return(FALSE);
+ *(u_long *)(ct->ct_mcall + 3 * BYTES_PER_XDR_UNIT)
+ = htonl(*(u_long *)info);
+ break;
+ case CLGET_LOCAL_ADDR:
+ len = sizeof(struct sockaddr);
+ if (getsockname(ct->ct_sock, (struct sockaddr *)info, &len) <0)
+ return(FALSE);
+ break;
+ case CLGET_RETRY_TIMEOUT:
+ case CLSET_RETRY_TIMEOUT:
+ case CLGET_SVC_ADDR:
+ case CLSET_SVC_ADDR:
+ case CLSET_PUSH_TIMOD:
+ case CLSET_POP_TIMOD:
+ default:
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+static void
+clnttcp_destroy(h)
+ CLIENT *h;
+{
+ register struct ct_data *ct =
+ (struct ct_data *) h->cl_private;
+
+ if (ct->ct_closeit) {
+ (void)_RPC_close(ct->ct_sock);
+ }
+ XDR_DESTROY(&(ct->ct_xdrs));
+ mem_free((caddr_t)ct, sizeof(struct ct_data));
+ mem_free((caddr_t)h, sizeof(CLIENT));
+}
+
+/*
+ * Interface between xdr serializer and tcp connection.
+ * Behaves like the system calls, read & write, but keeps some error state
+ * around for the rpc level.
+ */
+static int
+readtcp(ct, buf, len)
+ register struct ct_data *ct;
+ caddr_t buf;
+ register int len;
+{
+ fd_set *fds, readfds;
+ struct timeval start, after, duration, delta, tmp, tv;
+ int r, save_errno;
+
+ if (len == 0)
+ return (0);
+
+ if (ct->ct_sock + 1 > FD_SETSIZE) {
+ int bytes = howmany(ct->ct_sock + 1, NFDBITS) * sizeof(fd_mask);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL)
+ return (-1);
+ memset(fds, 0, bytes);
+ } else {
+ fds = &readfds;
+ FD_ZERO(fds);
+ }
+
+ gettimeofday(&start, NULL);
+ delta = ct->ct_wait;
+ while (TRUE) {
+ /* XXX we know the other bits are still clear */
+ FD_SET(ct->ct_sock, fds);
+ tv = delta; /* in case select writes back */
+ r = select(ct->ct_sock+1, fds, NULL, NULL, &tv);
+ save_errno = errno;
+
+ gettimeofday(&after, NULL);
+ timersub(&start, &after, &duration);
+ timersub(&ct->ct_wait, &duration, &tmp);
+ delta = tmp;
+ if (delta.tv_sec < 0 || !timerisset(&delta))
+ r = 0;
+
+ switch (r) {
+ case 0:
+ if (fds != &readfds)
+ free(fds);
+ ct->ct_error.re_status = RPC_TIMEDOUT;
+ return (-1);
+
+ case -1:
+ if (errno == EINTR)
+ continue;
+ if (fds != &readfds)
+ free(fds);
+ ct->ct_error.re_status = RPC_CANTRECV;
+ ct->ct_error.re_errno = save_errno;
+ return (-1);
+ }
+ break;
+ }
+ switch (len = _RPC_read(ct->ct_sock, buf, len)) {
+
+ case 0:
+ /* premature eof */
+ ct->ct_error.re_errno = ECONNRESET;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ len = -1; /* it's really an error */
+ break;
+
+ case -1:
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTRECV;
+ break;
+ }
+ return (len);
+}
+
+static int
+writetcp(ct, buf, len)
+ struct ct_data *ct;
+ caddr_t buf;
+ int len;
+{
+ register int i, cnt;
+
+ for (cnt = len; cnt > 0; cnt -= i, buf += i) {
+ if ((i = _RPC_write(ct->ct_sock, buf, cnt)) == -1) {
+ ct->ct_error.re_errno = errno;
+ ct->ct_error.re_status = RPC_CANTSEND;
+ return (-1);
+ }
+ }
+ return (len);
+}
Index: publickey.3
===================================================================
--- publickey.3 (nonexistent)
+++ publickey.3 (revision 1765)
@@ -0,0 +1,47 @@
+.\" @(#)publickey.3r 2.1 88/08/07 4.0 RPCSRC
+.\" $FreeBSD: src/lib/libc/rpc/publickey.3,v 1.4 2000/03/02 09:13:46 sheldonh Exp $
+.\"
+.TH PUBLICKEY 3R "6 October 1987"
+.SH NAME
+publickey, getpublickey, getsecretkey \- get public or secret key
+.SH SYNOPSIS
+.nf
+.B #include
+.B #include
+.LP
+.B getpublickey(netname, publickey)
+.B char netname[\s-1MAXNETNAMELEN\s0+1];
+.B char publickey[\s-1HEXKEYBYTES\s0+1];
+.LP
+.B getsecretkey(netname, secretkey, passwd)
+.B char netname[\s-1MAXNETNAMELEN\s0+1];
+.B char secretkey[\s-1HEXKEYBYTES\s0+1];
+.B char *passwd;
+.fi
+.SH DESCRIPTION
+.IX "getpublickey function" "" "\fLgetpublickey()\fP function"
+.IX "getsecretkey function" "" "\fLgetsecretkey()\fP function"
+These routines are used to get public and secret keys from the
+.SM YP
+database.
+.B getsecretkey(\|)
+has an extra argument,
+.IR passwd ,
+which is used to decrypt the encrypted secret key stored in the database.
+Both routines return 1 if they are successful in finding the key, 0 otherwise.
+The keys are returned as
+.SM NULL\s0-terminated,
+hexadecimal strings.
+If the password supplied to
+.B getsecretkey(\|)
+fails to decrypt the secret key, the routine will return 1 but the
+.I secretkey
+argument will be a
+.SM NULL
+string (``'').
+.SH "SEE ALSO"
+.BR publickey (5)
+.LP
+.I \s-1RPC\s0 Programmer's Manual
+in
+.TX NETP
Index: getrpcport.3
===================================================================
--- getrpcport.3 (nonexistent)
+++ getrpcport.3 (revision 1765)
@@ -0,0 +1,31 @@
+.\" @(#)getrpcport.3r 2.2 88/08/02 4.0 RPCSRC; from 1.12 88/02/26 SMI
+.\" $FreeBSD: src/lib/libc/rpc/getrpcport.3,v 1.6 1999/08/28 00:00:40 peter Exp $
+.\"
+.Dd October 6, 1987
+.Dt GETRPCPORT 3
+.Os
+.Sh NAME
+.Nm getrpcport
+.Nd get RPC port number
+.Sh SYNOPSIS
+.Ft int
+.Fn getrpcport "char *host" "int prognum" "int versnum" "int proto"
+.Sh DESCRIPTION
+.Fn getrpcport
+returns the port number for version
+.Fa versnum
+of the RPC program
+.Fa prognum
+running on
+.Fa host
+and using protocol
+.Fa proto .
+It returns 0 if it cannot contact the portmapper, or if
+.Fa prognum
+is not registered. If
+.Fa prognum
+is registered but not with version
+.Fa versnum ,
+it will still return a port number (for some version of the program)
+indicating that the program is indeed registered.
+The version mismatch will be detected upon the first call to the service.
Index: clnt_raw.c
===================================================================
--- clnt_raw.c (nonexistent)
+++ clnt_raw.c (revision 1765)
@@ -0,0 +1,243 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)clnt_raw.c 1.22 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)clnt_raw.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/clnt_raw.c,v 1.10 1999/08/28 00:00:36 peter Exp $";
+#endif
+
+/*
+ * clnt_raw.c
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ *
+ * Memory based rpc for simple testing and timing.
+ * Interface to create an rpc client and server in the same process.
+ * This lets us similate rpc and get round trip overhead, without
+ * any interference from the kernal.
+ */
+
+#include
+#include
+#include
+
+#define MCALL_MSG_SIZE 24
+
+/*
+ * This is the "network" we will be moving stuff over.
+ */
+struct clnt_raw_private {
+ CLIENT client_object;
+ XDR xdr_stream;
+ char _raw_buf[UDPMSGSIZE];
+ char mashl_callmsg[MCALL_MSG_SIZE];
+ u_int mcnt;
+};
+#define clntraw_private ((struct clnt_raw_private *)((struct rtems_rpc_task_variables *)rtems_rpc_task_variables)->clnt_raw_private)
+
+static enum clnt_stat clntraw_call();
+static void clntraw_abort();
+static void clntraw_geterr();
+static bool_t clntraw_freeres();
+static bool_t clntraw_control();
+static void clntraw_destroy();
+
+static struct clnt_ops client_ops = {
+ clntraw_call,
+ clntraw_abort,
+ clntraw_geterr,
+ clntraw_freeres,
+ clntraw_destroy,
+ clntraw_control
+};
+
+void svc_getreq();
+
+/*
+ * Create a client handle for memory based rpc.
+ */
+CLIENT *
+clntraw_create(prog, vers)
+ u_long prog;
+ u_long vers;
+{
+ register struct clnt_raw_private *clp = clntraw_private;
+ struct rpc_msg call_msg;
+ XDR *xdrs = &clp->xdr_stream;
+ CLIENT *client = &clp->client_object;
+
+ if (clp == 0) {
+ clp = (struct clnt_raw_private *)calloc(1, sizeof (*clp));
+ if (clp == 0)
+ return (0);
+ clntraw_private = clp;
+ }
+ /*
+ * pre-serialize the static part of the call msg and stash it away
+ */
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = prog;
+ call_msg.rm_call.cb_vers = vers;
+ xdrmem_create(xdrs, clp->mashl_callmsg, MCALL_MSG_SIZE, XDR_ENCODE);
+ if (! xdr_callhdr(xdrs, &call_msg)) {
+ perror("clnt_raw.c - Fatal header serialization error.");
+ }
+ clp->mcnt = XDR_GETPOS(xdrs);
+ XDR_DESTROY(xdrs);
+
+ /*
+ * Set xdrmem for client/server shared buffer
+ */
+ xdrmem_create(xdrs, clp->_raw_buf, UDPMSGSIZE, XDR_FREE);
+
+ /*
+ * create client handle
+ */
+ client->cl_ops = &client_ops;
+ client->cl_auth = authnone_create();
+ return (client);
+}
+
+static enum clnt_stat
+clntraw_call(h, proc, xargs, argsp, xresults, resultsp, timeout)
+ CLIENT *h;
+ u_long proc;
+ xdrproc_t xargs;
+ caddr_t argsp;
+ xdrproc_t xresults;
+ caddr_t resultsp;
+ struct timeval timeout;
+{
+ register struct clnt_raw_private *clp = clntraw_private;
+ register XDR *xdrs = &clp->xdr_stream;
+ struct rpc_msg msg;
+ enum clnt_stat status;
+ struct rpc_err error;
+
+ if (clp == 0)
+ return (RPC_FAILED);
+call_again:
+ /*
+ * send request
+ */
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, 0);
+ ((struct rpc_msg *)clp->mashl_callmsg)->rm_xid ++ ;
+ if ((! XDR_PUTBYTES(xdrs, clp->mashl_callmsg, clp->mcnt)) ||
+ (! XDR_PUTLONG(xdrs, (long *)&proc)) ||
+ (! AUTH_MARSHALL(h->cl_auth, xdrs)) ||
+ (! (*xargs)(xdrs, argsp))) {
+ return (RPC_CANTENCODEARGS);
+ }
+ (void)XDR_GETPOS(xdrs); /* called just to cause overhead */
+
+ /*
+ * We have to call server input routine here because this is
+ * all going on in one process. Yuk.
+ */
+ svc_getreq(1);
+
+ /*
+ * get results
+ */
+ xdrs->x_op = XDR_DECODE;
+ XDR_SETPOS(xdrs, 0);
+ msg.acpted_rply.ar_verf = _null_auth;
+ msg.acpted_rply.ar_results.where = resultsp;
+ msg.acpted_rply.ar_results.proc = xresults;
+ if (! xdr_replymsg(xdrs, &msg))
+ return (RPC_CANTDECODERES);
+ _seterr_reply(&msg, &error);
+ status = error.re_status;
+
+ if (status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) {
+ status = RPC_AUTHERROR;
+ }
+ } /* end successful completion */
+ else {
+ if (AUTH_REFRESH(h->cl_auth))
+ goto call_again;
+ } /* end of unsuccessful completion */
+
+ if (status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(h->cl_auth, &msg.acpted_rply.ar_verf)) {
+ status = RPC_AUTHERROR;
+ }
+ if (msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs, &(msg.acpted_rply.ar_verf));
+ }
+ }
+
+ return (status);
+}
+
+static void
+clntraw_geterr()
+{
+}
+
+
+static bool_t
+clntraw_freeres(cl, xdr_res, res_ptr)
+ CLIENT *cl;
+ xdrproc_t xdr_res;
+ caddr_t res_ptr;
+{
+ register struct clnt_raw_private *clp = clntraw_private;
+ register XDR *xdrs = &clp->xdr_stream;
+ bool_t rval;
+
+ if (clp == 0)
+ {
+ rval = (bool_t) RPC_FAILED;
+ return (rval);
+ }
+ xdrs->x_op = XDR_FREE;
+ return ((*xdr_res)(xdrs, res_ptr));
+}
+
+static void
+clntraw_abort()
+{
+}
+
+static bool_t
+clntraw_control()
+{
+ return (FALSE);
+}
+
+static void
+clntraw_destroy()
+{
+}
Index: publickey.5
===================================================================
--- publickey.5 (nonexistent)
+++ publickey.5 (revision 1765)
@@ -0,0 +1,38 @@
+.\" $FreeBSD: src/lib/libc/rpc/publickey.5,v 1.5 2000/03/02 09:13:46 sheldonh Exp $
+.\" @(#)publickey.5 2.1 88/08/07 4.0 RPCSRC; from 1.6 88/02/29 SMI;
+.TH PUBLICKEY 5 "19 October 1987"
+.SH NAME
+publickey \- public key database
+.SH SYNOPSIS
+.B /etc/publickey
+.SH DESCRIPTION
+.LP
+.B /etc/publickey
+is the public key database used for secure
+networking.
+Each entry in
+the database consists of a network user
+name (which may either refer to
+a user or a hostname), followed by the user's
+public key (in hex
+notation), a colon, and then the user's
+secret key encrypted with
+its login password (also in hex notation).
+.LP
+This file is altered either by the user through the
+.BR chkey (1)
+command or by the system administrator through the
+.BR newkey (8)
+command.
+The file
+.B /etc/publickey
+should only contain data on the NIS master machine, where it
+is converted into the
+.SM NIS
+database
+.BR publickey.byname .
+.SH SEE ALSO
+.BR chkey (1),
+.BR publickey (3R),
+.BR newkey (8),
+.BR ypupdated (8C)
Index: pmap_getmaps.c
===================================================================
--- pmap_getmaps.c (nonexistent)
+++ pmap_getmaps.c (revision 1765)
@@ -0,0 +1,86 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)pmap_getmaps.c 1.10 87/08/11 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)pmap_getmaps.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/pmap_getmaps.c,v 1.11 2000/01/27 23:06:39 jasone Exp $";
+#endif
+
+/*
+ * pmap_getmap.c
+ * Client interface to pmap rpc service.
+ * contains pmap_getmaps, which is only tcp service involved
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#define NAMELEN 255
+#define MAX_BROADCAST_SIZE 1400
+
+/*
+ * Get a copy of the current port maps.
+ * Calls the pmap service remotely to do get the maps.
+ */
+struct pmaplist *
+pmap_getmaps(address)
+ struct sockaddr_in *address;
+{
+ struct pmaplist *head = (struct pmaplist *)NULL;
+ int socket = -1;
+ struct timeval minutetimeout;
+ register CLIENT *client;
+
+ minutetimeout.tv_sec = 60;
+ minutetimeout.tv_usec = 0;
+ address->sin_port = htons(PMAPPORT);
+ client = clnttcp_create(address, PMAPPROG,
+ PMAPVERS, &socket, 50, 500);
+ if (client != (CLIENT *)NULL) {
+ if (CLNT_CALL(client, PMAPPROC_DUMP, xdr_void, NULL, xdr_pmaplist,
+ &head, minutetimeout) != RPC_SUCCESS) {
+ clnt_perror(client, "pmap_getmaps rpc problem");
+ }
+ CLNT_DESTROY(client);
+ }
+ if (socket != -1)
+ (void)_RPC_close(socket);
+ address->sin_port = 0;
+ return (head);
+}
Index: pmap_rmt.c
===================================================================
--- pmap_rmt.c (nonexistent)
+++ pmap_rmt.c (revision 1765)
@@ -0,0 +1,415 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+/*static char *sccsid = "from: @(#)pmap_rmt.c 1.21 87/08/27 Copyr 1984 Sun Micro";*/
+/*static char *sccsid = "from: @(#)pmap_rmt.c 2.2 88/08/01 4.0 RPCSRC";*/
+static char *rcsid = "$FreeBSD: src/lib/libc/rpc/pmap_rmt.c,v 1.15 2000/01/27 23:06:40 jasone Exp $";
+#endif
+
+/*
+ * pmap_rmt.c
+ * Client interface to pmap rpc service.
+ * remote call and broadcast service
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#define MAX_BROADCAST_SIZE 1400
+
+static struct timeval timeout = { 3, 0 };
+
+/*
+ * pmapper remote-call-service interface.
+ * This routine is used to call the pmapper remote call service
+ * which will look up a service program in the port maps, and then
+ * remotely call that routine with the given parameters. This allows
+ * programs to do a lookup and call in one step.
+*/
+enum clnt_stat
+pmap_rmtcall(addr, prog, vers, proc, xdrargs, argsp, xdrres, resp, tout, port_ptr)
+ struct sockaddr_in *addr;
+ u_long prog, vers, proc;
+ xdrproc_t xdrargs, xdrres;
+ caddr_t argsp, resp;
+ struct timeval tout;
+ u_long *port_ptr;
+{
+ int socket = -1;
+ register CLIENT *client;
+ struct rmtcallargs a;
+ struct rmtcallres r;
+ enum clnt_stat stat;
+
+ addr->sin_port = htons(PMAPPORT);
+ client = clntudp_create(addr, PMAPPROG, PMAPVERS, timeout, &socket);
+ if (client != (CLIENT *)NULL) {
+ a.prog = prog;
+ a.vers = vers;
+ a.proc = proc;
+ a.args_ptr = argsp;
+ a.xdr_args = xdrargs;
+ r.port_ptr = port_ptr;
+ r.results_ptr = resp;
+ r.xdr_results = xdrres;
+ stat = CLNT_CALL(client, PMAPPROC_CALLIT, xdr_rmtcall_args, &a,
+ xdr_rmtcallres, &r, tout);
+ CLNT_DESTROY(client);
+ } else {
+ stat = RPC_FAILED;
+ }
+ if (socket != -1)
+ (void)_RPC_close(socket);
+ addr->sin_port = 0;
+ return (stat);
+}
+
+
+/*
+ * XDR remote call arguments
+ * written for XDR_ENCODE direction only
+ */
+bool_t
+xdr_rmtcall_args(xdrs, cap)
+ register XDR *xdrs;
+ register struct rmtcallargs *cap;
+{
+ u_int lenposition, argposition, position;
+
+ if (xdr_u_long(xdrs, &(cap->prog)) &&
+ xdr_u_long(xdrs, &(cap->vers)) &&
+ xdr_u_long(xdrs, &(cap->proc))) {
+ lenposition = XDR_GETPOS(xdrs);
+ if (! xdr_u_long(xdrs, &(cap->arglen)))
+ return (FALSE);
+ argposition = XDR_GETPOS(xdrs);
+ if (! (*(cap->xdr_args))(xdrs, cap->args_ptr))
+ return (FALSE);
+ position = XDR_GETPOS(xdrs);
+ cap->arglen = (u_long)position - (u_long)argposition;
+ XDR_SETPOS(xdrs, lenposition);
+ if (! xdr_u_long(xdrs, &(cap->arglen)))
+ return (FALSE);
+ XDR_SETPOS(xdrs, position);
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*
+ * XDR remote call results
+ * written for XDR_DECODE direction only
+ */
+bool_t
+xdr_rmtcallres(xdrs, crp)
+ register XDR *xdrs;
+ register struct rmtcallres *crp;
+{
+ caddr_t port_ptr;
+
+ port_ptr = (caddr_t)crp->port_ptr;
+ if (xdr_reference(xdrs, &port_ptr, sizeof (u_long),
+ xdr_u_long) && xdr_u_long(xdrs, &crp->resultslen)) {
+ crp->port_ptr = (u_long *)port_ptr;
+ return ((*(crp->xdr_results))(xdrs, crp->results_ptr));
+ }
+ return (FALSE);
+}
+
+
+/*
+ * The following is kludged-up support for simple rpc broadcasts.
+ * Someday a large, complicated system will replace these trivial
+ * routines which only support udp/ip .
+ */
+
+static int
+getbroadcastnets(addrs, sock, buf)
+ struct in_addr *addrs;
+ int sock; /* any valid socket will do */
+ char *buf; /* why allocxate more when we can use existing... */
+{
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ struct sockaddr_in *sin;
+ struct in_addr addr;
+ char *cp, *cplim;
+ int n, i = 0;
+
+ ifc.ifc_len = UDPMSGSIZE;
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
+ perror("broadcast: ioctl (get interface configuration)");
+ return (0);
+ }
+#define max(a, b) (a > b ? a : b)
+#define size(p) max((p).sa_len, sizeof(p))
+ cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
+ for (cp = buf; cp < cplim;
+ cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
+ ifr = (struct ifreq *)cp;
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ifreq = *ifr;
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+ perror("broadcast: ioctl (get interface flags)");
+ continue;
+ }
+ if ((ifreq.ifr_flags & IFF_BROADCAST) &&
+ (ifreq.ifr_flags & IFF_UP)) {
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+#ifdef SIOCGIFBRDADDR /* 4.3BSD */
+ if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ addr =
+ inet_makeaddr(inet_netof(sin->sin_addr),
+ INADDR_ANY);
+ } else {
+ addr = ((struct sockaddr_in*)
+ &ifreq.ifr_addr)->sin_addr;
+ }
+#else /* 4.2 BSD */
+ addr = inet_makeaddr(inet_netof(sin->sin_addr),
+ INADDR_ANY);
+#endif
+ for (n=i-1; n>=0; n--) {
+ if (addr.s_addr == addrs[n].s_addr)
+ break;
+ }
+ if (n<0) {
+ addrs[i++] = addr;
+ }
+ }
+ }
+ return (i);
+}
+
+typedef bool_t (*resultproc_t)();
+
+enum clnt_stat
+clnt_broadcast(prog, vers, proc, xargs, argsp, xresults, resultsp, eachresult)
+ u_long prog; /* program number */
+ u_long vers; /* version number */
+ u_long proc; /* procedure number */
+ xdrproc_t xargs; /* xdr routine for args */
+ caddr_t argsp; /* pointer to args */
+ xdrproc_t xresults; /* xdr routine for results */
+ caddr_t resultsp; /* pointer to results */
+ resultproc_t eachresult; /* call with each result obtained */
+{
+ enum clnt_stat stat;
+ AUTH *unix_auth = authunix_create_default();
+ XDR xdr_stream;
+ register XDR *xdrs = &xdr_stream;
+ int outlen, inlen, fromlen, nets;
+ register int sock;
+ int on = 1;
+ fd_set *fds = 0, readfds; /* initialized to avoid warning */
+ register int i;
+ bool_t done = FALSE;
+ register u_long xid;
+ u_long port;
+ struct in_addr addrs[20];
+ struct sockaddr_in baddr, raddr; /* broadcast and response addresses */
+ struct rmtcallargs a;
+ struct rmtcallres r;
+ struct rpc_msg msg;
+ struct timeval t, tv;
+ char outbuf[MAX_BROADCAST_SIZE], inbuf[UDPMSGSIZE];
+ static u_int32_t disrupt;
+
+ if (disrupt == 0)
+ disrupt = (u_int32_t)(long)resultsp;
+
+ /*
+ * initialization: create a socket, a broadcast address, and
+ * preserialize the arguments into a send buffer.
+ */
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ perror("Cannot create socket for broadcast rpc");
+ stat = RPC_CANTSEND;
+ goto done_broad;
+ }
+#ifdef SO_BROADCAST
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
+ perror("Cannot set socket option SO_BROADCAST");
+ stat = RPC_CANTSEND;
+ goto done_broad;
+ }
+#endif /* def SO_BROADCAST */
+ if (sock + 1 > FD_SETSIZE) {
+ int bytes = howmany(sock + 1, NFDBITS) * sizeof(fd_mask);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL) {
+ stat = RPC_CANTSEND;
+ goto done_broad;
+ }
+ memset(fds, 0, bytes);
+ } else {
+ fds = &readfds;
+ FD_ZERO(fds);
+ }
+
+ nets = getbroadcastnets(addrs, sock, inbuf);
+ memset(&baddr, 0, sizeof (baddr));
+ baddr.sin_len = sizeof(struct sockaddr_in);
+ baddr.sin_family = AF_INET;
+ baddr.sin_port = htons(PMAPPORT);
+ baddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ (void)gettimeofday(&t, (struct timezone *)0);
+ msg.rm_xid = xid = (++disrupt) ^ getpid() ^ t.tv_sec ^ t.tv_usec;
+ t.tv_usec = 0;
+ msg.rm_direction = CALL;
+ msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ msg.rm_call.cb_prog = PMAPPROG;
+ msg.rm_call.cb_vers = PMAPVERS;
+ msg.rm_call.cb_proc = PMAPPROC_CALLIT;
+ msg.rm_call.cb_cred = unix_auth->ah_cred;
+ msg.rm_call.cb_verf = unix_auth->ah_verf;
+ a.prog = prog;
+ a.vers = vers;
+ a.proc = proc;
+ a.xdr_args = xargs;
+ a.args_ptr = argsp;
+ r.port_ptr = &port;
+ r.xdr_results = xresults;
+ r.results_ptr = resultsp;
+ xdrmem_create(xdrs, outbuf, MAX_BROADCAST_SIZE, XDR_ENCODE);
+ if ((! xdr_callmsg(xdrs, &msg)) || (! xdr_rmtcall_args(xdrs, &a))) {
+ stat = RPC_CANTENCODEARGS;
+ goto done_broad;
+ }
+ outlen = (int)xdr_getpos(xdrs);
+ xdr_destroy(xdrs);
+ /*
+ * Basic loop: broadcast a packet and wait a while for response(s).
+ * The response timeout grows larger per iteration.
+ *
+ * XXX This will loop about 5 times the stop. If there are
+ * lots of signals being received by the process it will quit
+ * send them all in one quick burst, not paying attention to
+ * the intended function of sending them slowly over half a
+ * minute or so
+ */
+ for (t.tv_sec = 4; t.tv_sec <= 14; t.tv_sec += 2) {
+ for (i = 0; i < nets; i++) {
+ baddr.sin_addr = addrs[i];
+ if (sendto(sock, outbuf, outlen, 0,
+ (struct sockaddr *)&baddr,
+ sizeof (struct sockaddr)) != outlen) {
+ perror("Cannot send broadcast packet");
+ stat = RPC_CANTSEND;
+ goto done_broad;
+ }
+ }
+ if (eachresult == NULL) {
+ stat = RPC_SUCCESS;
+ goto done_broad;
+ }
+ recv_again:
+ msg.acpted_rply.ar_verf = _null_auth;
+ msg.acpted_rply.ar_results.where = (caddr_t)&r;
+ msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
+ /* XXX we know the other bits are still clear */
+ FD_SET(sock, fds);
+ tv = t; /* for select() that copies back */
+ switch (select(sock + 1, fds, NULL, NULL, &tv)) {
+
+ case 0: /* timed out */
+ stat = RPC_TIMEDOUT;
+ continue;
+
+ case -1: /* some kind of error */
+ if (errno == EINTR)
+ goto recv_again;
+ perror("Broadcast select problem");
+ stat = RPC_CANTRECV;
+ goto done_broad;
+
+ } /* end of select results switch */
+ try_again:
+ fromlen = sizeof(struct sockaddr);
+ inlen = recvfrom(sock, inbuf, UDPMSGSIZE, 0,
+ (struct sockaddr *)&raddr, &fromlen);
+ if (inlen < 0) {
+ if (errno == EINTR)
+ goto try_again;
+ perror("Cannot receive reply to broadcast");
+ stat = RPC_CANTRECV;
+ goto done_broad;
+ }
+ if (inlen < sizeof(u_int32_t))
+ goto recv_again;
+ /*
+ * see if reply transaction id matches sent id.
+ * If so, decode the results.
+ */
+ xdrmem_create(xdrs, inbuf, (u_int)inlen, XDR_DECODE);
+ if (xdr_replymsg(xdrs, &msg)) {
+ if ((msg.rm_xid == xid) &&
+ (msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
+ (msg.acpted_rply.ar_stat == SUCCESS)) {
+ raddr.sin_port = htons((u_short)port);
+ done = (*eachresult)(resultsp, &raddr);
+ }
+ /* otherwise, we just ignore the errors ... */
+ }
+ xdrs->x_op = XDR_FREE;
+ msg.acpted_rply.ar_results.proc = xdr_void;
+ (void)xdr_replymsg(xdrs, &msg);
+ (void)(*xresults)(xdrs, resultsp);
+ xdr_destroy(xdrs);
+ if (done) {
+ stat = RPC_SUCCESS;
+ goto done_broad;
+ } else {
+ goto recv_again;
+ }
+ }
+done_broad:
+ if (fds != &readfds)
+ free(fds);
+ if (sock >= 0)
+ (void)_RPC_close(sock);
+ AUTH_DESTROY(unix_auth);
+ return (stat);
+}
+
Index: auth_time.c
===================================================================
--- auth_time.c (nonexistent)
+++ auth_time.c (revision 1765)
@@ -0,0 +1,503 @@
+#pragma ident "@(#)auth_time.c 1.4 92/11/10 SMI"
+
+/*
+ * auth_time.c
+ *
+ * This module contains the private function __rpc_get_time_offset()
+ * which will return the difference in seconds between the local system's
+ * notion of time and a remote server's notion of time. This must be
+ * possible without calling any functions that may invoke the name
+ * service. (netdir_getbyxxx, getXbyY, etc). The function is used in the
+ * synchronize call of the authdes code to synchronize clocks between
+ * NIS+ clients and their servers.
+ *
+ * Note to minimize the amount of duplicate code, portions of the
+ * synchronize() function were folded into this code, and the synchronize
+ * call becomes simply a wrapper around this function. Further, if this
+ * function is called with a timehost it *DOES* recurse to the name
+ * server so don't use it in that mode if you are doing name service code.
+ *
+ * Copyright (c) 1992 Sun Microsystems Inc.
+ * All rights reserved.
+ *
+ * Side effects :
+ * When called a client handle to a RPCBIND process is created
+ * and destroyed. Two strings "netid" and "uaddr" are malloc'd
+ * and returned. The SIGALRM processing is modified only if
+ * needed to deal with TCP connections.
+ *
+ * NOTE: This code has had the crap beaten out it in order to convert
+ * it from TI-RPC back to TD-RPC for use on FreeBSD.
+ *
+ * $FreeBSD: src/lib/libc/rpc/auth_time.c,v 1.4 2000/01/27 23:06:35 jasone Exp $
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include