Using Broadcasting to Search for an RPC Service

It is possible for a user to send a message to all rpcbind daemons on a local network requesting information on a specific service. The request is generated using the clnt_broadcast network call. The broadcast requests are sent to all locally connected broadcast nets using connectionless UDP transport. When sent, multiple responses may be obtained from the same server and from multiple servers. As each response is obtained, the clnt_broadcast call automatically invokes a predefined routine. Table 9.8 provides the syntax details of the clnt_broadcast call.

Table 9.8. Summary of the clnt_broadcast Library Call.

Include File(s)

Manual Section

3N

Summary

[View full width]

enum clnt_stat clnt_broadcast( u_long prognum, u_long versnum, u_long procnum, xdrproc_t inproc, char *in, xdrproc_t outproc, char *out, resultproc_t eachresult );

Return

Success

Failure

Sets errno

An enumerated type value RPC_SUCCESS indicating the success of the broadcast call.

Use clnt_perrno for error message.

Yes

The clnt_broadcast call is similar in nature to the callrpc function (another call used to invoke a remote procedure). The first three arguments for clnt_broadcast are the program, version and procedure numbers of the service. The parameters inproc and in reference the encoding procedure and the address of its argument(s) while outproc and out reference the decoding procedure and the address of where to place the decoding output if it is successful. Every time the clnt_broadcast call receives a response, it calls the function referenced by the eachresult argument. The eachresult function has two arguments. The first is a char * that references the same value as the out argument used in the clnt_broadcast call. The second argument is a reference to a structure, struct sockaddr_in * , that has the address information from the host that responded to the broadcast request. Keep in mind that the system supplies these values when the function is invoked. Every time the eachresult referenced function returns a 0 (FALSE), the clnt_broadcast call continues to wait for additional replies. The clnt_broadcast call will eventually time out (the user has no control over the amount of time).

Program 9.8 demonstrates the use of the clnt_broadcast call.

Program 9.8 Program broad.c , sending a broadcast request.

File : broad.c #include #include #include // For resultproc_t cast + u_long program_number, version; // Note: These are global static bool_t who_responded(char *out, struct sockaddr_in *addr) { int my_port_T, my_port_U; my_port_T = pmap_getport(addr, program_number, version, IPPROTO_TCP); 10 my_port_U = pmap_getport(addr, program_number, version, IPPROTO_UDP); if ( my_port_T ) printf("host: %s TCP port: %d ",inet_ntoa(addr->sin_addr), my_port_T); if ( my_port_U ) + printf("host: %s UDP port: %d ",inet_ntoa(addr->sin_addr), my_port_U); return 0; } int 20 main(int argc, char *argv[]) { enum clnt_stat rpc_stat; struct rpcent *rpc_entry; if (argc < 2) { fprintf(stderr, "usage: %s RPC_service_[name #] version ", *argv); + return 1; } ++argv; // Step past your own prog name if (isdigit(**argv)) // Check to see if # was passed program_number = atoi(*argv); // If # passed use it otherwise 30 else { // obtain RPC entry information if ((rpc_entry = getrpcbyname(*argv)) == NULL) { fprintf(stderr, "Unknown service: %s ", *argv); return 2; } // Get the program number + program_number = rpc_entry->r_number; } ++argv; // Move to version # version = atoi(*argv); rpc_stat = clnt_broadcast(program_number, version, NULLPROC, 40 (xdrproc_t)xdr_void, (char *) NULL, (xdrproc_t)xdr_void, (char *) NULL, (resultproc_t) who_responded); if (rpc_stat != RPC_SUCCESS) if (rpc_stat != RPC_TIMEDOUT) { // If error is not a time out + fprintf(stderr, "Broadcast failure : %s ", clnt_sperrno(rpc_stat)); return 3; } return 0; }

The program checks the command line for the number of arguments. It expects to be passed the name (or number) of the service to check and its version number. The first character of the first argument is checked. If it is a digit, it is assumed that the number for the service was passed and the atoi function is used to convert the string representation of the number into an integer value. If the name of the service was passed, the getrpcbyname network call is used (line 31) to obtain details about the specified service. Table 9.9 summarizes the getrpcbyname network call.

Table 9.9. Summary of the getrpcbyname Network Call.

Include File(s)

Manual Section

3N

Summary

struct rpcent *getrpcbyname(char *name);

Return

Success

Failure

Sets errno

A reference to the rpcent structure for the service.

NULL

 

The getrpcbyname call has one parameter, a reference to a character array containing the service name. If successful, the call returns a pointer to the rpcent structure for the service (as found in RPC program number database stored in the file /etc/rpc ). The rpcent structure is defined as

struct rpcent { char *r_name; /* name of this rpc service */ char **r_aliases; /* zero-terminated list of alternate names */ long r_number; /* rpc program number */ };

In line 38 the program then converts the second command-line argument into a version number. The clnt_broadcast call is used to garner responses. Each time a server responds to a broadcast request, the user-defined function who_responded is automatically invoked.

The who_responded function contains two other function calls, pmap_getport and inet_ntoa . The pmap_getport library function is used to obtain the port associated with the service. Table 9.10 provides the syntax specifics for the pmap_getport library function.

Table 9.10. Summary of the pmap_getport Library Function.

Include File(s)

Manual Section

3N

Summary

u_short pmap_getport(struct sockaddr_in *addr, u_long prognum, u_long versnum, u_long protocol);

Return

Success

Failure

Sets errno

The associated port number.

No, it sets rpc_createerr , query with clnt_pcreateerror( )

The first argument for this call is a reference to an address structure. This structure is as follows : [14]

[14] In the gdb debugger the command ptype TYPE can be used to display definition of the type of the value for TYPE ( assuming , of course, the type is referenced in the current code).

struct sockaddr_in { sa_family_t sin_family; // address family in_port_t sin_port; // port struct in_addr sin_addr; // reference to the address structure unsigned char sin_zero[8]; // unused };

The prognum and versnum arguments are the program and version number of the service. The last argument, protocol, should be set to either IPPROTO_TCP for TCP or IPPROTO_UDP for UPD. If the call is successful, it returns the port number; otherwise, it sets the variable rpc_createerr to indicate the error. If an error occurs, the library function clnt_pcreateerror should be used to retrieve the associated error message.

At this point some should be asking, why use pmap_getport at all? Couldn't we just call htons ( addr->sin_port ) in the who_responded function to get the port number? The answer is, we could if we wanted only the UDP-associated port for the service.

The second function used in who_responded is the network function inet_ntoa . This function takes an encoded four-byte network address and converts it to its dotted notation counterpart . A sample run of the program requesting information about the status service, version 1, is shown in Figure 9.35.

Figure 9.35 Output of the broad.c program showing servers providing status service.

medusa$ broad status 1 host: 137.49.6.1 TCP port: 32768 host: 137.49.6.1 UDP port: 32768 <-- 1 host: 137.49.52.2 TCP port: 32782 host: 137.49.52.2 UDP port: 32791 host: 137.49.9.27 TCP port: 751 host: 137.49.9.27 UDP port: 749 <-- 2 host: 137.49.52.152 TCP port: 984 host: 137.49.52.152 UDP port: 982 host: 137.49.240.157 TCP port: 1024 host: 137.49.240.157 UDP port: 1025 host: 137.49.6.1 TCP port: 32768 host: 137.49.6.1 UDP port: 32768 <-- 3 host: 137.49.52.152 TCP port: 984 host: 137.49.52.152 UDP port: 982 host: 137.49.240.157 TCP port: 1024 host: 137.49.240.157 UDP port: 1025 host: 137.49.52.2 TCP port: 32782 host: 137.49.52.2 UDP port: 32791 . . .

(1) Same host, same ports.

(2) Different host, different ports.

(3) Hosts continue to respond.

Notice that before the broadcast call timed out, some servers responded more than once. Also note that the service can be associated with different ports on different hosts. This output should be somewhat similar to the output produced by the rpcinfo command when called as

medusa$ rpcinfo -b status 1

EXERCISE

Modify the broad.c program to display the name (versus the IP address) of the host that responds to the broadcast. Hint : Check into the gethostbyaddr network function that, when passed an address, will return a structure that contains a reference to the host name.

Категории