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. Well 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 well 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, thats 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 havent wroten any line for that.

In order to test his strength, well 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

pierre.derrier@laposte.net