An RPC Primer

An RPC Primer

The purpose of this section is to explain key concepts and terminology of the RPC world. If you understand RPC, feel free to move on to the Secure RPC Best Practices section. However, you might find it a worthwhile exercise to read this section first RPC can be somewhat daunting at first.

What Is RPC?

RPC is a communication mechanism that allows a client and a server application to communicate with each other through function calls sent from the client to the server. The client thinks it's calling a client-side function, but the function is sent by the RPC runtime to the server, which performs the function and returns any results to the client.

NOTE
RPC is primarily a C and C++ technology. Although it includes wrappers for other languages, frankly, if you're considering using RPC from other languages such as Perl, Microsoft JScript, or Microsoft Visual Basic, you should simply use COM and DCOM.

The RPC functionality built into Microsoft Windows is based on Open Software Foundation RPC (OSF RPC) and thus offers interoperability with other operating systems, such as Unix and Apple.

The majority of system services in Windows including the Print Spooler, Event Log, Remote Registry, and Secondary Logon use RPC to some degree, as do hundreds of third-party applications from independent software vendors. Also, many applications communicate locally using a local version of RPC, named LRPC.

Creating RPC Applications

Creating an RPC application can be a little confusing at first. It helps if you design an application from the outset to use RPC, rather than attempting to retrofit RPC functionality later. When creating the RPC application, you create the following files:

  • The client code

  • The server code

  • An interface definition language file (.IDL file)

  • Optionally, an application configuration file (.ACF file)

The client code is normal C or C++. It calls various functions, some RPC configuration functions, some local functions, and other remote RPC functions. The server code also has RPC startup code; however, most important, it contains the real functions that are called by the RPC clients. The IDL file is incredibly important. It defines the remote interface function signatures that is, the function name, arguments, and return values and allows the developer to group the functions in easy-to- manage interfaces. The ACF file allows you to customize your application's RPC capabilities but does not change the network representation of the data.

Compiling the Code

Compiling the RPC application involves the following stages:

  1. Compile the IDL and ACF files by using Midl.exe. This creates three files: the server stub code, the client stub code, and a header file.

  2. Compile the client code and the client stub code. Note that the client code also includes the header file created during step 1.

  3. Link the client code with the appropriate RPC run-time library, usually Rpcrt4.lib.

  4. Compile the server code and the server stub code. Note that the server code also includes the header file created during step 1.

  5. Link the server code with the appropriate RPC run-time library, usually Rpcrt4.lib.

That's it! Let's look at an example. Assume your application (a phonelike application) is to be named Phone, the client code is contained in a C source file named Phonec.c, the server code is in Phones.c, and the IDL and ACF files are Phone.idl and Phone.acf, respectively. When you compile Phone.idl using Midl.exe, the compiler creates three files: a header file, Phone.h, and the client and server stubs, Phone_c.c and Phone_s.c. Next you compile Phonec.c and Phone_c.c and link with Rpcrt4.lib to create the client code, Phonec.exe. You then compile Phones.c and Phone_s.c and link with Rpcrt4.lib to create the server code, Phones.exe. Figure 16-1 outlines the process.

figure 16-1 the rpc development process.

Figure 16-1. The RPC development process.

It's really not as complex as it looks! The Phone application is available with the book's sample files in the folder Secureco2\Chapter 16\RPC folder.

How RPC Applications Communicate

When the client application communicates with the server application, the client calls the client stub code, which in turn marshals the data to send to the server. Marshalling involves packing function information and function arguments in such a way that any appropriate RPC server, on any platform, can read the client request. Once the client request is made, the data travels from the client to the server, where the server stub code unpacks the data and forwards the request to the server code. The server then does the work, and any return data is marshaled back to the client.

RPC applications communicate using various network transport protocols, such as named pipes and TCP/IP-based sockets. The good news is that as an application developer, you do not need to understand much about the network protocols themselves the work is left to RPC.

To communicate with a server, the client must bind with it, which involves building a binding handle from a binding string. This string is composed of several parts. The first is the protocol sequence, which specifies which network protocol will be used. Each protocol has a specific name. Table 16-1 outlines some of the most commonly used protocol sequences.

Table 16-1. Example Protocol Sequences

Protocol Sequence

Comments

ncacn_np

Named pipes

ncalrpc

Local interprocess communication, not remotable

ncacn_ip_tcp

TCP/IP

After the protocol sequence comes the server address, which is usually the name of the server in a format understood by the protocol sequence. Following that is the endpoint, which specifies the particular network resource on the host that should be used. Last come the options, which are rarely used. The resulting string is then used to connect, or bind, to the server. Also, a function exists that will build the string for you, RpcStringBindingCompose. For example, this binding string ncacn_np:northwindtraders[\\pipe\\phone] is created by the following code:

LPBYTE pszUuid = (LPBYTE)NULL; LPBYTE pszProtocolSequence = (LPBYTE)"ncacn_np"; LPBYTE pszNetworkAddress = (LPBYTE)"northwindtraders"; LPBYTE pszEndpoint = (LPBYTE)"\\pipe\\phone"; LPBYTE pszOptions = (LPBYTE)NULL; LPBYTE pszStringBinding = (LPBYTE)NULL; RPC_STATUS status = RpcStringBindingCompose(pszUuid, pszProtocolSequence, pszNetworkAddress, pszEndpoint, pszOptions, &pszStringBinding);

Once the client software has created a binding handle, it's ready to start calling RPC functions.

Context Handles and State

Technically, RPC is stateless when a user connects to the RPC server, it does not maintain data for that client. However, some applications require the server program to maintain state information between client calls; hence, the server must keep the state information for each client. This is achieved through the use of context handles, which are opaque data structures passed to the client by the server. On each request, the client sends the context handle to the server. The concept is similar to Web-based cookies.

You might have noticed that RPC uses two main kinds of handles: binding handles and context handles. A binding handle is used to identify the logical connection between a client and a server. It's similar, in principle, to a file handle. A context handle allows the server to maintain state for the client between function calls.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2001
Pages: 286

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net