|
|
UNIVERSITAD
DE LAS AMERICAS Cholula,
Puebla MEXICO |
SISTEMAS DISTRIBUIDOS - IS 417
PRÁCTICA
II
(RPC)
The
aim of this practice is to test the mecanism of RPC. We’ll do it in reverse engineering.
At
the beginning, we have these files :
$ ll
-rwxrwxrwx 1 root
root 490 sep 22 13:42 adaptador_clnt.c
-rwxrwxrwx 1 root
root 322 sep 22 22:09
adaptador_svc.c
-rwxrwxrwx 1 root
root 1009 sep 22 22:10
cliente.c
-rwxrwxrwx 1 root
root 178 sep 22 13:42
mensaje.x
-rwxrwxrwx 1
root root 188 sep 22 15:33 servidor.c
SERVIDOR.C
Contains the services provided by the server. In this practice we use
only a simple function “printmessage”
/* servidor.c: Codigo para el servidor */
#include <stdio.h>
/* Muestra un mensaje en la pantalla */
int printmessage(char *mensaje)
{
printf("%s\n", mensaje);
return 1;
}
CLIENTE.C
Contains the code of the client process. We
modified it to call repetitively the service “printmessage”,
in order to test the server in critical case. (The modified parts are in red)
/* cliente.c: Este programa solo muestra un mensaje en
la pantalla */
#include <stdio.h>
#include "mensaje.h"
/* prototipo de funciones */
int printmessage(char *mensaje);
CLIENT *ref; /* Referencia al servidor (conexion) */
char *servidor; /* Nombre del servidor */
main(int argc, char *argv[])
{
char *mensaje;
char
*numclient;
if (argc != 4) {
fprintf(stderr, "Utilizacion: %s <host> <mensaje> <numclient>\n",argv[0]);
exit(1);
}
servidor =
argv[1];
mensaje =
argv[2];
numclient = argv[3];
/* Iniciar la
conexion */
ref =
clnt_create(servidor, MESSAGEPROG, PRINTMESSAGEVERS, "tcp");
if (ref ==
(CLIENT *)NULL) {
/* No se
pudo establecer conexion con el servidor! */
clnt_pcreateerror(servidor);
exit(1);
}
printf("## Conexion OK!\n");
while(1)
{
printmessage(mensaje);
printf("## CLIENT %s : El mensaje fue
entregado\n",numclient);
}
/* Cerrar la
conexion */
clnt_destroy( ref
);
}
MENSAJE.X
Contains the definitions of the interface for the service(s) provided by
the server. It uses the specific syntax of the unix tool rpcgen.
rpcgen : permits to
generate automatically the stub and the skeleton.
stub : low-level interface
who provide simple function to manage the remote communication : connexion, sending requests to the server
host, recept responses from the server.
skeleton : same as stub,
but for the server side.
Thanks to these interfaces, the complexities of the remote communication
are hidden for the programmer.
/* mensaje.x: Interfaz para la aplicacion distribuida
*/
program MESSAGEPROG { /* name
of the program for the portmapper of the server host */
version
PRINTMESSAGEVERS { /* version del programa */
int
PRINTMESSAGE(string) = 1; /* function of the service */
} = 1;
} = 0x20000001; /*
identificator of the port for the portmapper */
We call rpcgen with this file :
$ rpcgen mensaje.x
$ ll
-rwxrwxrwx 1 root
root 490 sep 22 13:42
adaptador_clnt.c
-rwxrwxrwx 1 root
root 322 sep 22 22:09
adaptador_svc.c
-rwxrwxrwx 1 root
root 1009 sep 22 22:10
cliente.c
-rwxrwxrwx 1 root
root 556 sep 22 22:19
mensaje_clnt.c
-rwxrwxrwx 1
root root 730 sep 22 22:19 mensaje.h
-rwxrwxrwx 1 root
root 2214 sep 22 22:19
mensaje_svc.c
-rwxrwxrwx 1 root
root 178 sep 22 13:42
mensaje.x
-rwxrwxrwx 1 root
root 188 sep 22 15:33
servidor.c
We have now three new files :
MENSAJE.H
Contains the constants and functions used by the both sides, client and
server.
#ifndef _MENSAJE_H_RPCGEN
#define _MENSAJE_H_RPCGEN
#include <rpc/rpc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MESSAGEPROG 0x20000001
#define PRINTMESSAGEVERS 1
#if defined(__STDC__) || defined(__cplusplus)
#define PRINTMESSAGE 1
extern int *
printmessage_1(char **, CLIENT *);
extern int *
printmessage_1_svc(char **, struct svc_req *);
extern int messageprog_1_freeresult (SVCXPRT *,
xdrproc_t, caddr_t);
#else /* K&R C */
#define PRINTMESSAGE 1
extern int *
printmessage_1();
extern int *
printmessage_1_svc();
extern int messageprog_1_freeresult ();
#endif /* K&R C */
#ifdef __cplusplus
}
#endif
#endif /* !_MENSAJE_H_RPCGEN */
MENSAGE_CLNT.C
Contains the stub (client side). The names are generated automatically.
#include <memory.h> /* for memset */
#include "mensaje.h"
/* Default timeout can be changed using clnt_control()
*/
static struct timeval TIMEOUT = { 25, 0 };
int *
printmessage_1(char **argp, CLIENT *clnt)
{
static int
clnt_res;
memset((char
*)&clnt_res, 0, sizeof(clnt_res));
if
(clnt_call (clnt, PRINTMESSAGE,
(xdrproc_t)
xdr_wrapstring, (caddr_t) argp,
(xdrproc_t) xdr_int,
(caddr_t) &clnt_res,
TIMEOUT) != RPC_SUCCESS) {
return
(NULL);
}
return
(&clnt_res);
}
MENSAJE_SRV.C
Contains the skeleton (server side).
#include "mensaje.h"
#include <stdio.h>
#include <stdlib.h>
#include <rpc/pmap_clnt.h>
#include <string.h>
#include <memory.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifndef SIG_PF
#define SIG_PF void(*)(int)
#endif
static void
messageprog_1(struct svc_req *rqstp, register SVCXPRT
*transp)
{
union {
char
*printmessage_1_arg;
}
argument;
char *result;
xdrproc_t
_xdr_argument, _xdr_result;
char *(*local)(char *, struct svc_req *);
switch
(rqstp->rq_proc) {
case
NULLPROC:
(void)
svc_sendreply (transp, (xdrproc_t) xdr_void, (char *)NULL);
return;
case
PRINTMESSAGE:
_xdr_argument
= (xdrproc_t) xdr_wrapstring;
_xdr_result
= (xdrproc_t) xdr_int;
local
= (char *(*)(char *, struct svc_req *)) printmessage_1_svc;
break;
default:
svcerr_noproc
(transp);
return;
}
memset
((char *)&argument, 0, sizeof (argument));
if (!svc_getargs
(transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
svcerr_decode
(transp);
return;
}
result =
(*local)((char *)&argument, rqstp);
if (result
!= NULL && !svc_sendreply(transp, (xdrproc_t) _xdr_result, result)) {
svcerr_systemerr
(transp);
}
if
(!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) {
fprintf
(stderr, "%s", "unable to free arguments");
exit
(1);
}
return;
}
int
main (int argc, char **argv)
{
register
SVCXPRT *transp;
pmap_unset
(MESSAGEPROG, PRINTMESSAGEVERS);
transp =
svcudp_create(RPC_ANYSOCK);
if (transp
== NULL) {
fprintf
(stderr, "%s", "cannot create udp service.");
exit(1);
}
if
(!svc_register(transp, MESSAGEPROG, PRINTMESSAGEVERS, messageprog_1,
IPPROTO_UDP)) {
fprintf
(stderr, "%s", "unable to register (MESSAGEPROG,
PRINTMESSAGEVERS, udp).");
exit(1);
}
transp =
svctcp_create(RPC_ANYSOCK, 0, 0);
if (transp
== NULL) {
fprintf
(stderr, "%s", "cannot create tcp service.");
exit(1);
}
if
(!svc_register(transp, MESSAGEPROG, PRINTMESSAGEVERS, messageprog_1,
IPPROTO_TCP)) {
fprintf
(stderr, "%s", "unable to register (MESSAGEPROG,
PRINTMESSAGEVERS, tcp).");
exit(1);
}
svc_run
();
fprintf
(stderr, "%s", "svc_run returned");
exit (1);
/*
NOTREACHED */
}
As we can see, rpcgen is a very
nice tool, but the syntax and the names are a bit complex.
We are lazy programmers, so we’ll create
two more modules in order to save a lot of useless complexities.
ADAPTADOR_CLNT.C
Simple module for helping the programmer. It permits to translate local
calls in remote calls (because the arguments of the remote call are a bit more
complex, that’s only for the visibility of the code)
The names and the syntax used for the remote functions are the ones used
in the low-level modules, the stub and the skeleton.
/* adaptador_clnt.c: Adaptador de llamadas en la parte
cliente */
#include <stdio.h>
#include "mensaje.h"
extern CLIENT *ref;
extern char *servidor;
/* Esta funcion adapta la llamada local en una remota
*/
int printmessage(char *mensaje) {
int
*resultado;
resultado =
printmessage_1(&mensaje, ref);
if (resultado
== (int *)NULL) {
/* Oops ... Hubo un error (se esperaba un 1)!*/
clnt_perror(ref, servidor);
exit(1);
}
return
*resultado;
}
ADAPTADOR_SVC.C
Same tip but for the server side, translate remote calls to local calls
with a more visible syntax
/* adaptador_svc.c: Adaptador de llamadas en la parte
servidor */
#include "mensaje.h"
extern int printmessage(char *mensaje);
int *printmessage_1_svc(char **mensaje, struct svc_req
*info_llamada) {
static int
resultado; /* Debe ser "static" */
resultado = printmessage(*mensaje);
return
&resultado;
}
Now
we are ready to compile the whole thing.
$cc cliente.c
mensaje_clnt.c adaptador_clnt.c -o cliente -lnsl
$cc -DRPC_SVC_FG
servidor.c mensaje_svc.c adaptador_svc.c -o servidor -lnsl
$ ll
-rwxrwxrwx 1 root
root 490 sep 22 13:42
adaptador_clnt.c
-rwxrwxrwx 1 root
root 322 sep 22 22:09
adaptador_svc.c
-rwxrwxrwx 1 root
root 13142 sep 22 22:21
cliente
-rwxrwxrwx 1 root
root 1009 sep 22 22:10
cliente.c
-rwxrwxrwx 1 root
root 556 sep 22 22:21
mensaje_clnt.c
-rwxrwxrwx 1 root
root 730 sep 22 22:21
mensaje.h
-rwxrwxrwx 1 root
root 2214 sep 22 22:21
mensaje_svc.c
-rwxrwxrwx 1 root
root 178 sep 22 13:42
mensaje.x
-rwxrwxrwx 1 root
root 14302 sep 22 22:21 servidor
-rwxrwxrwx 1 root
root 188 sep 22 15:33
servidor.c
$ ./servidor
Now the server is waiting for the calls.
And we haven’t wroten any line for that.
In order to test his strength, we’ll launch 3
clients at the same time.
$ ./cliente 127.0.0.1 « MESSAGE
DU CLIENT 1 » 1
$ ./cliente 127.0.0.1 « MESSAGE
DU CLIENT 2 » 2
$ ./cliente 127.0.0.1 « MESSAGE
DU CLIENT 3 » 3

The three processes on the top are the 3
clients. The server in on the bottom.
We can see that he receives all the
requests and replies to all, in the right order.
To complete this practice, we should
test the same thing with a real network between the clients and the server, to
see if the latency create problems.
Pierre Derrier #203293