Documentation Home Reference Class Hierarchy Index Help
This introduction is addressed to programmers who wish to write their own distributed applications in Java using the DistinctÒ ONC RPC/XDR Toolkit for JavaÔ. The sections in this programming guide provide an overview of the following topics.
This document assumes that you are already familiar with Java, the basic concepts of RPC client/server computing and ONC RPC and XDR in particular. It also assumes a familiarity with the basics of the C binding of ONC RPC. If you wish to write Java ONC RPC applications and are not yet familiar with these concepts you should do further reading before using the toolkit.
You may need to refer to the reference part of this manual to understand parts of the examples given in this programmer’s guide.
This toolkit may be used to help you out in several different scenarios. Here are some:
In all these scenarios Distinct ONC RPC/XDR for Java will certainly help you to write portable, high-quality Java code in a minimum amount of time.
This guide discusses how to write client server applications using the Distinct ONC RPC/XDR Toolkit for Java. It first explains how to write an ONC RPC client and then illustrates how a server is built in Java. It then explains how to pass RPCs through Internet firewalls and then describes some other concepts the understanding of which are fundamental to using this toolkit.
What comes with the Distinct ONC RPC/XDR for Java Toolkit?
The Distinct ONC RPC/XDR Toolkit for Java is a set of tools and libraries that enables you to write pure Java ONC RPC clients and servers. It consists of:
Building a Client with Distinct ONC RPC/XDR for Java
In this section we will illustrate how to use this toolkit to build a Java client. For our example we will use the scenario of an application programmer who wants to write a Java-based front end for an existing ONC RPC service. This front end is an RPC client that may be a stand-alone application or a Java Applet for use inside a Web browser. Both types of clients can be built using the Distinct ONC RPC/XDR Toolkit for Java.
When writing an ONC RPC application the first thing to do is to write an XDR interface definition file. This file describes the data types and the signature of your interface. Distinct ONC RPC/XDR for Java understands the XDR language as described in RFC 1832 (XDR: External Data Representation Standard). By common programming convention XDR files have a .x extension.
The XDR Interface Definition Language File
For the remainder of this guide we will work with the XDR interface definition language (IDL) file demo.x as shown below. It defines a simple service that returns a sequence of consecutive lines from a text:
%/*
***************************************
% * Distinct ONC
RPC/XDR for Java Example *
%
****************************************/
struct request {
int
from;
int
to;
};
struct result {
int
number;
string
line<>;
struct result *next;
};
typedef result *res_list;
program DEMO_SERVER
{
version DEMO_VERSION {
res_list get_line(request) = 1;
} =
1;
} = 0x20000023;
This IDL file has 5 sections:
The Server Implementation in C
Although the functionality of the service might be obvious at this point, we show here its implementation in C to clarify any questions you may have. In this simple version the lines of text that can be returned are hard coded and there is no error processing done (e.g. if a client requests a non-existing range of lines).
#include
<stdio.h>
#include
<rpc/rpc.h>
#include
"demo.h"
char *text[] = {
"This is the first line of text",
"This is the second line of text",
"This is the third line.",
"This is the 4th line",
"This is the 5th line",
"This is the 6th line.",
"This is the 7th line.",
"This is the 8th line."
};
res_list *get_line_1(request
*req)
{
static res_list rl;
result *p, *q;
result **n;
int
c;
/* free any dynamically
allocated memory from previous requests */
p =
rl;
while (p != NULL) {
q = p;
p = p->next;
free(q);
}
/* the handling of the
new request starts here*/
rl
= NULL;
/* n points to the
pointer where the next list element is inserted */
n =
&rl;
for
(c = req->from; c < req->to; c++) {
/* allocate the next
element of the list */
*n = malloc(sizeof(result));
/* assign the line
number */
(*n)->number = c;
/* assign the line
(string) */
(*n)->line = text[c];
n = &((*n)->next);
*n = NULL;
}
return &rl;
}
To build the server binary with just the demo.x IDL file and the demo_server.c file above, run the standard ONC RPC rpcgen (for C) on demo.x and compile and link the generated files demo_svc.c, demo_xdr.c, demo.h, and demo_server.c into one native binary.
Once you have built the above server and have it up and running with the ONC RPC portmapper, you can proceed to build a Java stand-alone client that calls this server.
The following pages will take you through the steps needed to build the Java client. The first thing to do is to run the Jrpcgen provided with the Distinct ONC RPC/XDR for Java Toolkit on the XDR file. The Jrpcgen application translates XDR files into Java stubs. For a complete description of the Jrpcgen command please refer to the reference portion of this manual.
You run Jrpcgen on demo.x by typing:
>java Jrpcgen demo.x
Jrpcgen V4.0, Copyright 1997 - 2001
by Distinct Corporation
demo.x
writing: request.java
writing: result.java
writing: res_list.java
writing: demo.java
The execution of jrpcgen creates four Java classes, and subsequently four files, that implement the client stub for calling the demo service described: There is one file per type definition for request.java, result.java, and res_list.java, and one main stub file called demo.java.
We will now take a look at the demo.java main stub file that has been generated.
/*
**************************************
* Distinct ONC RPC/XDR
for Java Example *
****************************************/
import com.distinct.rpc.*;
import
java.io.IOException;
import
java.net.InetAddress;
/**
*This class was automatically
generated by Jrpcgen from the RPC/XDR file "demo.x" <br>.
* It defines the client interface to
a server implementing the "demo" interface.
*/
public class demo extends JRPCClient {
/** Program ID of the interface*/
public static final
int DEMO_SERVER = 0x20000023;
/** Interface Version */
public static final int
DEMO_VERSION = 1;
/**
* Creates and connects an RPC
client for a server that implements the "demo" interface.
* Calls the remote Portmapper
in order to get the server.port
* @param host The host on
which the server resides.
* @param stream true for a TCP
connection, false for UDP.
* @exception RPCError When the
calls fail for any reason.
*/
public
demo(InetAddress host, boolean stream) throws RPCError {
super(host, DEMO_SERVER, DEMO_VERSION, stream);
}
/**
* Creates and connects an RPC client
for a server that implements the "demo" interface.
* The client is connected to a server
with a known port. (No interaction with a portmapper)
* @param host The host on which the
server resides.
* @param port The port on which the
server listens.
* @param stream True for a TCP
connection, false for UDP.
* @exception RPCError When the calls
fail for any reason.
*/
public
demo(InetAddress host, int port, boolean stream) throws RPCError {
super(host, DEMO_SERVER, DEMO_VERSION, port, stream);
}
/**
* Creates an RPC client for a server
that implements the "demo" interface.
* It initializes it with an
externally created protocol client object.
* @param protocol The protocol object
that implements the client connection.
*/
public demo(ClientGeneric
protocol) {
super(protocol);
}
public static final int get_line = 1;
/**
* Stub method that invokes the server
function "get_line" (version 1).
*/
public res_list
get_line_1(request arg) throws RPCError, IOException {
res_list retval = new res_list();
GetClient().Call(get_line, arg, retval);
return retval;
}
};
An instance of this demo stub class represents a client to the demo server. It maintains the complete necessary connection status. Without going into all details we can easily see that this class consists of a number of constants, three constructors, and a public method get_line_1() that has the same signature as get_line() in the XDR file. You can also see that the lines from the beginning of the XDR file (the three comment lines) were copied over to the Java source generated, without the leading % characters. This is because Jrpcgen generates comments that are understood by the javadoc auto documentation tool.
Like all main client-stub classes in Distinct ONC RPC/XDR for Java, the demo class is derived from the base class JRPCClient. This class provides the framework for calling RPC servers using the various possible protocols. Similar to the C binding of ONC RPC, the constants are the program's number and version as well as an ordinal number for each procedure. Of the three constructors the first (public demo(InetAddress host, boolean stream) throws RPCError) is probably the most commonly used one. We will use it in our application example. Finally, the get_line_1() method is what we have to invoke when we want to interact with the server. The _1 extension results from the fact that this is the implementation of version 1 of this RPC program. As with the C binding the version number from the XDR file is always appended with an underscore in front of it.
We now take a look into the result.java file, which is one of the type definition files that were generated.
/****************************************
* Distinct ONC RPC/XDR
for Java Example *
****************************************/
import com.distinct.rpc.*;
/**
* This class was automatically
generated by Jrpcgen from the RPC/XDR file demo.x.<br>
*result: was struct
*/
public class result
implements XDRType {
public int number;
public String line;
public result next;
/**
* Encodes an object of class result in
compliance to RFC 1832 (XDR).
* @param xdrs The XDR output stream.
*/
public void
xdr_encode(XDRStream xdrs)
{
xdrs.xdr_encode_int(number);
xdrs.xdr_encode_string(line);
xdrs.xdr_encode_boolean(next != null);
if
(next != null)
next.xdr_encode(xdrs);
return;
}
/**
* Decodes an object of class result in
compliance to RFC 1832 (XDR).
* @param xdrs The XDR input stream.
* @exception RPCError When the call
fails for any reason.
*/
public void
xdr_decode(XDRStream xdrs) throws RPCError
{
number = xdrs.xdr_decode_int();
line = xdrs.xdr_decode_string();
next = null;
if
(xdrs.xdr_decode_boolean()) {
next = new result();
next.xdr_decode(xdrs);
}
return;
}
};
Once again you can see that the lines from the beginning of the XDR file (the three comment lines) were copied over to the Java file generated. This happens for all files generated. The data members of the result class mirror the C like definition of the struct result in the XDR file.
The two encoding/decoding methods use the methods of the XDRStream class to encode/decode the data members. The linked-list structure of the result type is mapped in an XDR-typical manner to a recursive invocation guided by a boolean value that indicates a NULL pointer value (and thus, the end of the list).
In most cases the mapping of XDR types to Java types and classes is quite
obvious. For an overview of the complete mapping please have a look at the
following table:
|
XDR Type |
Java Type |
|
(unsigned) int |
int |
|
(unsigned) long |
int |
|
(unsigned) short |
short |
|
(unsigned) char |
char |
|
(unsigned) hyper |
long |
|
float |
float |
|
double |
double |
|
bool |
boolean |
|
string |
String |
|
opaque |
byte array |
|
fixed length array |
Java array |
|
variable length array |
Java array |
|
optional data (pointer like *x) |
Reference to an object of class x. If x is a basic type a special wrapper class XDRx that implements XDRType is used instead. |
|
enum x |
Class x implements XDRType with member variable int value and one constant int per enum constant. |
|
struct x |
Class x implements XDRType with member variables for each struct member. |
|
union x |
Class x implements XDRType with member variables for each union member including discriminant. No overlaying of members is supported (neither for type conversion nor for saving space). |
|
typedef x y |
Class x implements XDRType with member variable value of the redefined type y. |
The most notable difficulty in the Java mapping is the handling of typedefs. As Java has no notion of type name aliasing, the only viable solution that preserves the introduced names is the definition of a new class. In the Java mapping of typedef this new class has always only one data member, called value, that is a variable of the redefined type (for an illustration please refer to the generated file res_list.java).
Like all classes that are created by Jrpcgen from type definitions, result implements the XDRType interface. This interface defines the two methods xdr_encode() and xdr_decode() that are used by the stub implementation for marshalling and unmarshalling the parameter into and from an XDRStream object. Jrpcgen generates the necessary code to implement these methods for the given XDR type.
Typically, if you are using the RPC functionality of the Distinct ONC RPC/XDR for Java Toolkit, you will not have to bother with these methods. As with the C binding, the stub implementation hides all details of their usage. You just have to construct objects of the defined input parameter class and pass them to the stub method (in our case e.g. of class request to demo.get_line()) and you will receive a newly created reply object from the stub (in our case of class res_list). However, if you use the Distinct ONC RPC/XDR Toolkit just for encoding/decoding XDR data (e.g. from a file) you will have to call these methods directly in conjunction with an appropriate XDRStream object.
The other two type definition classes, request.java and res_list.java, are very similar and have exactly the same structure.
Now we are ready to write our first Distinct ONC RPC/XDR for Java application. Below we show the listing of a simple stand-alone Java application that uses the generated stubs to make some invocations of the demo server:
import java.net.*;
public class
testapp {
static public void main(String[] args) {
demo client; // this is the RPC client object
request req = new request();
try {
client = new demo(
InetAddress.getByName(args[0]), // the host
true); // use TCP
for (int i = 0; i < 8; i++)
for (int j = i + 1; j < 8; j++) {
req.from = i;
req.to = j;
System.out.println("from " + i + " to " + j);
res_list rl = client.get_line_1(req);
result res = rl.value;
while (res != null) {
System.out.println(res.number+":"+res.line);
res = res.next;
}
}
client.CloseClient();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
...
}
}
Here we take a step by step look at this application:
Here we are. This is our first RPC client written in Java. Now we only have to compile the stubs and our testapp.java with the Java compiler.
If you have compiled the files and you run the program, the output will look like this:
>java testapp localhost
from 0 to 1
0:This is the first line of text
from 0 to 2
0:This is the first line of text
1:This is the second line of text
from 0 to 3
0:This is the first line of text
1:This is the second line of text
2:This is the third line.
from 0 to 4
0:This is the first line of text
1:This is the second line of text
2:This is the third line.
3:This is the 4th line
from 0 to 5
...
If the output looks like this:
>java testapp localhost
Server not available.
This means that our program has caught an exception because we have most probably forgotten to start the demo server (in this case on localhost, our local machine).
In principle, you can use the Distinct ONC RPC/XDR for Java classes and stubs generated by Jrpcgen inside an applet as described for the stand-alone program above. But, please note that by default all Java-enabled browsers do not allow network connections to hosts other than the one an applet was loaded from. In order to connect your applet to arbitrary RPC servers (or to run an RPC server) inside a browser you have to undertake additional action to work around this restriction:
Unfortunately both methods are currently highly browser specific. Please look into your browser's manuals for further information or have a look at the security features of Netscape Communicator, HotJava, or Microsoft Internet Explorer 4.0. In particular you should look for the terms applet security, signed code (JAR files, CAB files), and certificates.
If both methods are not viable for your application or you have to connect to an RPC server that is protected by a firewall you might want to use the HTTP tunneling protocol that is described below in the section "Connecting Through a Firewall". With this unique feature of the Distinct ONC RPC/XDR for Java Toolkit you can easily separate the Web server and other application or database servers. You should be aware that you could expect performance degradation compared to the direct connection as described earlier.
Building a Server with Distinct ONC RPC/XDR for Java
This section illustrates how to build a Java server. To build a Java server, again we have to run Jrpcgen from the Distinct ONC RPC/XDR Toolkit for Java on the XDR file. This time you run Jrpcgen on demo.x with the -S option, telling it that it has to create the server stub as well. Thus, you type:
>java Jrpcgen -S demo.x
Jrpcgen V4.0, Copyright 1997 - 2001
by Distinct Corporation
demo.x
writing: request.java
writing: result.java
writing: res_list.java
writing: demo.java
writing: demoServer.java
The execution of jrpcgen creates the same four Java classes as before (even if we do not need the client part demo.java this time) plus the additional server stub class named demoServer.java.
We will now take a look into the demoServer.java main stub file that was generated.
/*
**************************************
* Distinct ONC RPC/XDR
for Java Example *
****************************************/
import com.distinct.rpc.*;
/**
* <b>DEMO_SERVER Server Stub</b>
* This class was automatically generated by Jrpcgen from
the RPC/XDR file "demo.x".<br>
* demo: was interface DEMO_SERVER
*/
abstract public class demoServer extends JRPCServer {
/** Interface Program */
public static final
int DEMO_SERVER = 0x20000023;
/** Interface Version */
public static final
int DEMO_VERSION = 1;
public static final int get_line = 1;
/**
* Constructor that creates the RPC server that
implements the "demo" interface.
* @exception RPCError When the calls fail for any reason.
*/
public demoServer()
throws RPCError {
super(DEMO_SERVER, DEMO_VERSION, true);
UnregisterServer();
RegisterServer(StartUDP(0), false);
RegisterServer(StartTCP(0), true);
}
/**
* Constructor that creates the RPC server that
implements the "demo" interface at a fixed port.
* @param port The port on which the server listens.
* @exception RPCError When the calls fails for any reason.
*/
public
demoServer(int port) throws RPCError {
super(DEMO_SERVER, DEMO_VERSION, true);
UnregisterServer();
RegisterServer(StartUDP(port), false);
RegisterServer(StartTCP(port), true);
}
/**
* Dispatcher Routine that interprets the call requests.
* @param proc The index of the requested function.
* @param xin read the input parameter from this XDR
stream.
* @param xout write the return parameter to this XDR
stream.
* @return true, if the function with the index proc can
be served, false otherwise.
*/
synchronized public
boolean DoCall(int proc, XDRStream xin, XDRStream xout) {
try
{
switch (proc) {
case 0: {
return true;
}
case get_line: {
request arg = new request();
arg.xdr_decode(xin);
res_list ret = get_line_1(arg);
ret.xdr_encode(xout);
return true;
}
default:
return false;
}
}
catch (Exception e) {
return false;
}
}
/*
* Overwrite these abstract server methods for
implementing the server's functionality.
*/
abstract public
res_list get_line_1(request arg);
};
This stub class demoServer represents the frame for a server that implements the DEMO_SERVER interface described in demo.java. Similar to the client stub we see, this class consists of a number of constants, two constructors, and an abstract method get_line_1() that has the same signature as get_line() in the XDR file. You can also see that the lines from the beginning of the XDR file (the three comment lines) were copied over to the generated Java source without the leading % characters. This is because Jrpcgen generates comments that are understood by the javadoc auto documentation tool.
Like all server stub classes in Distinct ONC RPC/XDR for Java, the class demoServer is derived from the base class JRPCServer. This class provides the framework for implementing RPC servers using the various possible protocols. Again, the constants are the program's number and version and ordinal numbers for the remote procedure. The method DoCall() is the core of the server. It implements dispatching of the remotely called procedures depending on the given ordinal number and it calls marshalling and unmarshalling of the parameters. Finally, the abstract get_line_1() method is the procedure we have to implement for our demo server by overriding it in a derived class.
In Distinct ONC RPC/XDR for Java DoCall() and, consequently, all implemented remote procedures can be called concurrently by different server threads. In the Distinct ONC RPC/XDR for Java implementation of an ONC RPC server, each TCP client is served by its own thread. One additional thread handles all UDP requests.
Now we are ready to write our first RPC server in Java. Below is the listing of a simple Java application that uses the generated stub to implement the demo server:
public class MyServer extends demoServer {
static String
text[] = {
"This is the first line of text",
"This is the second line of text",
"This is the third line.",
"This is the 4th line",
"This is the 5th line",
"This is the 6th line.",
"This is the 7th line.",
"This is the 8th line."
};
static public void
main(String[] args) {
try
{
new MyServer ();
}
catch (RPCError e) {
System.out.println(e.getMessage());
}
}
public MyServer()
throws RPCError {
super();
}
// here we override and implement the
real remote procedure
public res_list
get_line_1(request arg) {
res_list rl = new res_list();
result n = new result();
for (int c = arg.from; c < arg.to; c++) {
// allocate the next element in the list
n.next = new result();
n = n.next;
if (c == arg.from)
rl.value = n;
// assign the line number
n.number = c;
// assign the line (string)
n.line = text[c];
}
return rl;
}
}
This Java code implements the same simple server functionality as the C version shown earlier. It defines a new class named MyServer that is derived from the demoServer class and that finally implements the remote procedure get_line_1() in a straightforward manner. Compared to the implementation in C there are two differences worth noting:
If you now compile this program and run it as a stand alone Java program together with the RPC client as shown above you will see that our first Java ONC RPC server behaves exactly like the initial C version.
If you are not using fixed server ports (see next section) you will need an instance of the ONC Portmapper running on each machine that hosts ONC RPC servers. It is required to bind RPC clients to servers. Basically, the Portmapper is a name server. Server ports of ONC RPC servers are chosen randomly. It is the responsibility of the Portmapper to tell the clients the port number of a server with a given program/version number. The Portmapper itself is a RPC server that listens on a well-known port (111) for these requests. Typically, the Portmapper is running as a demon in the background and it is usually provided by the standard ONC RPC infrastructure that is part of e.g. any network connected UNIX host (e.g. NFS relies on ONC RPC). However, if you are starting your RPC server on a machine that does not provide this infrastructure by default (e.g. all flavors of MS Windows) you have to make sure that a Portmapper is present to enable clients to connect to your new server.
The Distinct ONC RPC/XDR for Java Toolkit provides its own implementation of the Portmapper. This pure Java implementation of the Portmapper implements the portmap interface version 2. As the Portmapper uses a fixed port number there can be only one Portmapper instance per node. Therefore, any server that uses the Jrpcgen generated stub will first check, whether the system already provides a Portmapper, and if not, it will start its own Portmapper. Please note, that all ONC RPC servers that are subsequently started on that node will now also use this instance of the Portmapper. This means, if you shut down the Distinct ONC RPC/XDR for Java server that started the Portmapper, you will also loose the ability to bind new clients to the other, still running servers, as the portmapper that stores the binding information is gone. Connections that were established before the Portmapper went down are not affected, as the binding is established only once during the connection setup. You can avoid this by starting the portmapper manually before starting the server.
To check whether a Portmapper is really running you can use the standard ONC RPC rpcinfo tool with the -p <hostname> option. This should enumerate the servers registered with the portmapper of this host. If this tool reports an error or does not show the servers you were expecting, there is obviously a problem with the Portmapper.
If you have to disable the Portmapper autostart feature of Jrpcgen generated server stubs for some reason, you currently have to edit the xxxServer class. Simply change the third parameter of the invocation super(DEMO_SERVER, DEMO_VERSION, true); in the constructors to false and the server will not try to start a Portmapper.
Most RPC applications are designed for Intranets, and although RPC can be made to run over the Internet, the original specifications were not really designed for this. On the Internet connections over arbitrary ports (like those established by the RPC protocol) are often blocked by firewalls. Please ask your firewall administrator for the details of your network security.
Distinct ONC RPC/XDR for Java allows you to build servers and clients with well-known (fixed) server ports. Depending on the configuration of your firewalls this may be sufficient for you to access certain servers over the Internet.
The fact that RPC server ports are not known in advance makes it difficult to configure a firewall accordingly. When this turns out to be a problem it might help to be able to configure the used ports statically. Distinct ONC RPC/XDR for Java supports this additional feature for Java clients and servers. If you use fixed ports you will not need to run the portmapper application.
To connect a client using a fixed port you only have to make a small change to the initialization sequence. You have to use another constructor of the client class. In our example the demo class can be created using the following code:
client = new demo(
InetAddress.getByName(args[0]), // the host
Integer.parseInt(args[1]), // the port
true); // use TCP
Now, the second command line parameter can be used to specify the known server port of the demo server.
Distinct ONC RPC/XDR for Java comes with a powerful mechanism that wraps RPC requests completely with standard HTTP transactions. The server side CGI script (RPCGw) together with a special protocol client (ClientHTTP) allows for transparent tunneling of the RPC protocol in HTTP. This mechanism allows executing arbitrary RPCs on the Internet where firewalls are blocking all protocols, such as RPC, that do not use fixed ports. It also allows you to call RPC servers that are not located on the same host as the web server but elsewhere in your LAN, from a Java applet. With this feature you can easily separate the Web server and other application or database servers on different machines.
RPCGw translates and executes RPCs that are encapsulated in HTTP requests. It runs as an add on to the Web server at the server site. It uses the standard CGI interface for communication with the Web server. RPCGw receives the RPC request encoded in an HTTP POST request from the ClientHTTP object on its standard input stream via the CGI interface. It decodes the request parameter (host, program number, version number, procedure number, and protocol type (TCP/UDP)), optionally checks an access control list, creates an RPC connection, and forwards the request to the RPC server via the standard RPC protocol. It then waits for the reply and sends the return parameters encapsulated in an HTTP reply (of mime type application/octet stream) via the Web server back to the original requestor. RPCGw is generic in the sense that it does not know about the server interfaces and the parameter types. It simply forwards all requests (if permitted by the access control list). This means, there is no need to adapt RPCGw to a specific RPC server interface; it will work for any interface.
RPCGw is provided in source code as it is a C program and as it should run on any common Web server architecture, it is hard to provide all the different binary formats. With the fairly portable sources and the simple Makefile you should be able to install it on any server architecture.
To install RPCGw you should first compile it for your server architecture. Please take a look into the Makefile provided. You have to customize the compile options for selecting the security policy (none or access control list) and the logging features (on/off and log filename). For compilation of RPCGw you will need the standard ONC RPC library (for all types of UNIX usually included in the standard distribution, for Win32 please check out the Distinct ONC RPC/XDR Toolkit for C developers). After compilation, copy the executable into the script directory of your Web server.
In order to preserve the integrity and the privacy of your LAN RPCGw uses a sophisticated access control list that allows you to specify exactly which procedure of which RPC program/version number can be executed on which host via which protocol. In addition it allows the logging of all RPC activities to a local file. If you have chosen to work with the access control list (highly recommended) use a text editor to create a file named rpcgw.cfg in the same directory. This file holds the access control list. It will be read by RPCGw each time it is invoked. Each line in this file has five entries delimited by white spaces (e.g. tabs): hostname (symbolic name), program(decimal number), version(decimal number), protocol(tcp or udp), procedure(decimal number). Each entry is exactly one string or the * wildcard character. Thus, a simple rpcgw.cfg file might look like this:
island 77 1 tcp
1
* 79 1 * *
Line one of the above example for an access control list allows procedure number 1 in an RPC service with program number 77, version number 1 on host named island via the TCP protocol to be called. Line two allows any procedure in an RPC service with program number 79, version number 1 on any host via TCP or UDP to be called. In general, '*' matches any parameter provided by the remote client. Other regular expressions are not recognized. In order not to harm the security of your local network you should be highly restrictive when creating the RPCGw access control list and allow only access to those services, hosts and procedures that are really required by the application and that cannot be abused by an unauthorized person. In general, please contact your local security manager before installing RPCGw on a Web server with high security priorities or that is visible to the outside world.
Building a Client that uses RPCGw
To create an RPC client object in a Java applet that calls the RPC server via HTTP and RPCGw use the following sequence in your code. First you create a ClientHTTP protocol object that knows how to access the server via RPCGw, and then you initialize the RPC client object (of class demo in our example) with this protocol object.
The following code sequence is nearly identical to the previous client
example. Only the client initialization is changed (the gray part) to use a
ClientHTTP protocol object instead of standard TCP or UDP. The sample code
given here assumes that the code is part of an applet (otherwise
getDocumentBase().getHost() would be undefined) but this is not required. You
may use HTTP tunneling also in stand-alone applications. Also, please note,
that the URL of the RPCGw script (in our case /cgi-bin/RPCGw) may depend
on the configuration of your Web server.
...
demo client; // this is the RPC client object
request req = new request();
try {
// Construct the HTTP protocol object
ClientHTTP Protocol = new ClientHTTP(getDocumentBase().getHost(), // my
Web-server
"/cgi-bin/rpcgw", // the URL of the RPCGw program
"island", // host of the RPC server
demo.DEMO_SERVER, // the program number
demo.DEMO_VERSION, // the version number
true); // use TCP ("false" for UDP)
// Initialize the RPC client object
client = new demo(Protocol);
for (int i = 0; i < 8; i++)
for (int j = i + 1; j < 8; j++) {
req.from = i;
req.to = j;
System.out.println("from " + i + " to " + j);
res_list rl = client.get_line_1(req);
result res = rl.value;
while (res != null) {
System.out.println(res.number+":"+res.line);
res = res.next;
}
}
client.CloseClient();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
...
This section discusses how to do certain things in the Distinct ONC RPC/XDR for Java environment. It includes information on how to authenticate, how to broadcast RPCs, how to run RPCs in batch mode and discusses server data flow, using XDR streams and error and timeout handling.
By default, Distinct ONC RPC/XDR for Java uses NULL authentication. However, this default can be changed at the client object by using setCredential() and setVerifier(). In the other direction the returned verifier from the server can be obtained after each call with getReturnedVerifier(). Please note that Distinct ONC RPC/XDR for Java (like the C binding) provides the data structures for transferring authentication data and the methods for setting, retrieving and checking this data. It does not provide protection against forged authentication data. Any security and access control features have to be implemented by your application. Below is a code sample that creates a Unix authentication (sometimes also called System authentication) and sets it as the new credential of this client. From now on each request will be sent with this authentication.
import java.net.*;
public class
testapp {
static public void main(String[] args) {
demo client; // this is the RPC client object
request req = new request();
try {
client = new demo(
InetAddress.getByName(args[0]), true);
// a new Unix
authentication is created (UID = GID = 0)
client.GetClient().setCredential(new AuthUnix(
(int)(System.currentTimeMillis()/1000),
"myhostname", 0, 0, new int[0]);
for (int i = 0; i < 8; i++)
for (int j = i + 1; j < 8; j++) {
req.from = i;
req.to = j;
System.out.println("from " + i + " to " + j);
res_list rl = client.get_line_1(req);
result res = rl.value;
while (res != null) {
System.out.println(res.number+":"+res.line);
res = res.next;
}
// Here it is assumed
that the server returned an authentication
// (typically of type
short) that is now used for any subsequent
calls
client.GetClient().setCredential(client.GetClient().GetReturnedVerifier());
}
}
...
}
}
The class com.distinct.rpc.JRPCServer provides the DoAuth() method for handling authentication at the server side. Override this method if you want your server to check authentication (the default is no check). DoAuth() receives the credential and verifier provided by the client as well as the call identifier and returns the verifier that should be sent back with the reply to the calling client (simply return null for NULL authentication). It throws a com.distinct.rpc.RPCAuthError if it wants to signal an unsuccessful authentication. The following code fragment illustrates how DoAuth() can be used in the server code.
...
synchronized public Auth DoAuth(int proc, Auth cred, Auth verf)throws RPCError
{
if (cred.getFlavor() != JRPC.kAUTH_UNIX)
throw new RPCAuthError(JRPC.kAUTH_TOOWEAK);
// convert it to the
correct type
AuthUnix unix_cred = new AuthUnix(cred);
if (unix_cred.getUid() != 0)
throw new RPCAuthError(JRPC.kAUTH_BADCRED);
// use the same
authenticator as returned verifier
// usually you would
use some shortcut here
return unix_cred;
}
...
This example implementation of DoAuth() checks for each call to see if it has a credential of type AuthUnix and if so if the user id is 0. If either condition is not fulfilled it throws a com.distinct.rpc.RPCAuthError exception in order to terminate any further processing of this call. The remote procedure itself is not called at all in this case.
If you need to use other types of authentication (e.g Kerberos style) you can implement it easily by deriving a new authentication class from the com.distinct.rpc.Auth base type.
As mentioned in the Authentication section above, new authentication types can be derived from the basic com.distinct.rpc.Auth type. Distinct ONC RPC/XDR for Java provides a standard implementation for advanced security with authentication of type AUTH_DES. The AUTH_DES authentication relies on Sun's Java Cryptography Extensions (JCE) version 1.2. Please refer to the vendors instructions on how to install and configure the cryptography package for your location and environment.
Please note that using the Sun JCE 1.2 package requires the Java Runtime Environment (JRE) version 1.2 (also known as Java 2). In all other cases, Distinct ONC RPC/XDR for Java can be used with any JRE version 1.1 or higher.
Secure RPC Client
The following example illustrates the required steps to set up a connection to a Secure RPC server from a Java program.
import java.net.*;
public class
testapp {
static public void main(String[] args) {
demo client; // this is the RPC client object
try {
// Get the key database
PublicKeyDB DB = new PublicKeyDB(new File("/etc/publickey"));
// Authenticate yourself by entering the password
if (!DB.doLogin(false))
System.exit(-1);
// Create the authenticator
AuthDes a = new AuthDes(DB.getNetname(),
DB.getPassword(),
"unix.wied@home",
DB,
1800);
// Construct the Secure RPC protocol object
ClientSecureRPC Protocol = new ClientSecureRPC(
InetAddress.getByName("localhost"),
demo.DEMO_SERVER,
demo.DEMO_VERSION,
true, // use TCP
a); // the authenticator
// Initialize the RPC client object
client = new demo(Protocol);
request req = new request();
for (int i = 0; i 8; i++)
for (int j = i + 1; j 8; j++) {
req.from = i;
req.to = j;
System.out.println("from " + i + " to " + j);
res_list rl = client.get_line_1(req);
result res = rl.value;
while (res != null) {
System.out.println(res.number+":"+res.line);
res = res.next;
}
}
}
catch (Exception e) { // an RPC error happened !
printStr(e.getMessage());
}
}
This is our standard example again. Initialization of the client happens in five steps.
nobody 7f4bbd1ce1f69a593d5c39758773a33d0f4db3baad7e3c53:c4f50767351d6168f65300
e81cb6c934ea5034f0859ffbd165445ada89de8beb
dummy
436a4d3789b6e9781c70b1eeac40a51f81a9fed65bfec2ab:4c37bc3d9605ea2be26ea92
39bd54f9ad81ffaa6b447dc987b5b226cb8e3ecd7
Usually each “publickey” database contains at least one entry for the
pseudo-user “nobody” (something like a guest account) that always requires just
an empty password to login.
Secure RPC Server
In the server handling of secure RPC is even simpler. You just have to create a “AuthDesServerDB” in quite the same way as we did it in the previous section with the “AuthDes” authenticator and to invoke it in the server’s “doAuth()” method to check whether the incoming authenticators are valid. This is illustrated in the sample code below.
...
public MyServer() throws RPCError, IOException {
super();
PublicKeyDB DB = new PublicKeyDB(new
File("/etc/publickey"));
if (!DB.doLogin(false))
System.exit(-1);
adb = new
AuthDesServerDB(DB.getNetname(), DB.getPassword(), DB);
}
...
synchronized public Auth DoAuth(int proc, Auth cred, Auth verf) throws
RPCAuthError {
AuthDes a = adb.checkAuth(cred,
verf);
SetClientData(a);