DCE Recipes
Harold W. Lockhart Jr.
Chief Technical Architect
PLATINUM Solutions, Inc.
A PLATINUM technology Company
8 New England Executive Park
Burlington, MA 01803
(617) 229-4980 x1202
hal@platsol.com
Overview
- Provide a cookbook of practial DCE recipes
- Show how to combine DCE features to meet common needs
- Assume audience knowledgeable in basic DCE development
- Present key technical details, but no working example code
- Feedback requested
- Plan to publish as companion to DCE FAQ
- Contributions of example code requested
CONTENTS
Program That is Both Client & Server
- Recipe 1 - Tiered Server
- Server that calls other servers
- Recipe 2 - Asynchronous Callback
- Server returns results via callback
- Recipe 3 - Peer to Peer
- Application that calls (other instances of) itself
- A server that calls another server(s)
- Makes use of (at least) two interfaces
- Server of one interface
- Client of the other
- Client calls made from within remote procedure
- Building block approach for modularity and reuse
General Technique
- Compile both IDL (& ACF) files
- Include both
*.h
files
- Link with server stub from one, client stub from other(s)
- Perform standard server initilization, including security
- Perform client side binding and security on call
- Alternatively, do binding and security at startup
Fine Points
- With DCE 1.1 use delegation or impersonation
- Stubs will automatically use
rpc_ss_allocate()
as needed
- For multiple calls from server consider parallel execution
- Create a thread for each call
- Use join to synchronize completion
- Client makes non-blocking RPC
- Server returns results via callback
- Interactive client
- Issues request
- Result received in separate thread
- Server is a daemon
- Receives request and issues callback
General Technique
- Define two Interfaces ``Request'' & ``Callback''
- Request has maybe attribute, no out parameters
- Interactive client
- Client of Request, server of Callback
- Export Callback interface with object UUID
- Annotate server binding with
rpc_binding_set_object()
- Explicitly create thread for
rpc_server_listen()
- Assume previous
dce_login
- Daemon server
- Server of Request
- Issue Callback within remote procedure
- Standard daemon-style security with keytab
Fine Points
- Binding
- Client can use any binding method
- Server must get binding, call
rpc_binding_server_from_client()
- Client security
sec_login_get_current_context()
rpc_binding_set_auth_info()
- Prompt user for password (ugh)
rpc_server_register_auth_info()
- Design of callback handling
- Application dependent
- Beware of race conditions
- Consider using mutex or condition variable
Alternative Approaches
- Unauthenticated callback
- Daemon calls back as unauthenticated client
- Interactive program makes (2nd) RPC to obtain results
- Solves login problem
- Extra RPC required
- Nuisance attack is possible
- Make original RPC in separate thread
- Extemely simple to code
- Provides essentially the same functionality
- Small amount of additonal network overhead
- Single program
- Client and server of same interface
- Still looking for real-world use
General Technique
- Very similar to callback client
- Avoid multiply defined symbol conflict
- Suppress EPV generation by IDL
- Give remote procedure different name from that in
.idl
- Build your own EPV
- Create thread explicitly
- Security considerations
- Communication between client and server threads
Server Wants to Know Who Called
- Client calls may occur in any order
- Client calls may be assigned to any thread
- Other than by passing an explict parameter, how does the server find out which client called?
- Recipe 4 - Information associated with client
- Recipe 5 - Knowing which client is which
- For authorization, server can learn
- Principal identity (usually corresponds to human being)
- Group memberships
- Home cell
- For configuration purposes, server can learn
- Network address
- Endpoint (e.g. TCP port number)
- Transport protocol used
General Technique
- Security information
- Client must use PAC-based security
- Server obtains client binding handle
- Call
rpc_binding_inq_auth_info()
- Returns PAC containing Principal, Group, Cell
- Also returns security session attributes
- Configuration information
- Get binding handle
rpc_binding_to_string_binding()
rpc_string_binding_parse()
- Returns address, endpoint, protocol
- Client makes sequence of calls
- Server needs to associate state with client
- Client must call same server instance
- Server must clean up if client fails
General Technique
- Use a context handle
- In IDL:
typedef [context_handle] void *ctx_hdl;
- First operation:
[out] ctx_hdl *handle
- Server allocates data returns value (e.g. pointer)
- Client preserves value
- Next operations:
[in] ctx_hdl handle
- Client presents saved value
- Contains binding information
- Final operation:
[in, out] ctx_hdl *handle
- Server deallocates, sets handle to NULL
- Runtime deallocates at client
Fine Points
- Server provides context rundown procedure
ctx_hdl_rundown(ctx_hdl handle)
- Will be called if client fails
- Check for NULL pointer
- Deallocate data
- Will not be called until RPC completes
- If server fails and client continues
rpc_ss_destroy_client_context()
- Binding
- Context handle will override binding handle
- Multiple context handles, different server => error
- Need to pass dynamically created data
- Potentially complex data structures
- Lists
- Trees
- Arbitrary graphs, etc.
- Potential modification of structure by server
- Avoid manual packing and unpacking
- Avoid ``get next'' style interface
General Technique
- Pick the right kind of DCE pointer
- All types copy data, fix up pointer
- Reference pointer,
[ref]
- No allocation by stubs
- Cannot be NULL
- Unique pointer,
[unique]
- Can be NULL
- Stubs will allocate
- Cannot handle loops
- Full pointer,
[ptr]
- Full local pointer semantics
- Can detect aliases
- Bind to server based on unique id
- Many uses
- Identify server instances associated with unique resources
- Find the server instanciating a particular data object
- Distinguish each server instance from all others
General Technique
- Server creates UUID(s)
- Compile time:
uuidgen
- Runtime:
uuid_create()
- Export to namespace and endpoint manager
rpc_ns_binding_export()
rpc_ep_register()
- Client specifies UUID when binding
rpc_ns_binding_import_begin()
or
rpc_ns_binding_lookup_begin()
- NULL => match any object id
- One interface definition
- Need different behavior for different object types
- Must invoke different sets of procedures
- Potentially multiple objects of each type
General Technique
- Use object binding to export all object id's
- Suppress EPV generation by idl
- Build EPV for each object type
- Associate each object id with a type id
- Associate each type id with an EPV
rpc_server_register_if()
- NULL => unspecified or unmatched object id
- Server uses DCE security
- ACL checking
- Provides some services to unauthenticated clients
- Wants to treat clients not using security at all the same as unauthenticated
General Technique
- Get binding handle
- Call
rpc_server_inq_auth_client()
- If
rpc_s_ok
and rpc_c_authz_dce
, use PAC
- If
rpc_s_binding_has_no_auth
build dummy PAC
- Set
authenticated
to false
- Set all UUID fields to NULL
- Set all names to empty string
- Zero groups and foreign groups
- Need to run multiple instances of same server
- Mutiple nodes - availability and capacity
- Same node - parallelism with single threaded servers
- Combination
- Need to distributed client calls over multiple instances
- Assume stateless servers
General Technique
- Use object binding
- One object id per server instance
- Forces unique entry in namespace and endpoint map
- Client calls
rpc_ns_import_begin()
with nil object id
- Bindings returned in ``random'' order
- Alternatively, get all bindings and pick one at random
- Does not do true load balancing
- Complex to get right
- Could use namespace to store load information
- Two tier (node and server instance) distribution
- Can be done, but is more difficult
- Simpler approach - number instances proportional to capacity
- DCE client uses X-Windows
- X-Windows is not thread-aware or thread-safe
- Even single threaded DCE clients have additional runtime threads
General Technique
- Explicitly create a thread with all the X-WIndows code
- Design communication between X and DCE threads
- X-Windows ``add input source'' mechanism
- Use pipe to communicate between threads
- Suggest use of work queue - condition variable
- This is not guarenteed to work for every application
- X can block on input causing DCE to timeout
- Safest approach - two processes with IPC connection