登录社区:用户名: 密码: 忘记密码 网页功能:加入收藏 设为首页 网站搜索  

文档

下载

图书

论坛

安全

源码

硬件

游戏
首页 信息 空间 VB VC Delphi Java Flash 补丁 控件 安全 黑客 电子书 笔记本 手机 MP3 杀毒 QQ群 产品库 分类信息 编程网站
 内容搜索 网页 下载 源代码
热点文章
  利用鼠标键盘钩子截获密码
  利用鼠标键盘钩子截获密码
  如何将多个文件捆绑成一个可..
  如何将多个文件捆绑成一个可..
  内核级HOOK的几种实现与应用
  内核级HOOK的几种实现与应用
  书写基于内核的linux键盘纪录..
  书写基于内核的linux键盘纪录..
  CIH病毒原码
  CIH病毒原码
  编写进程/线程监视器
  编写进程/线程监视器
本站原创
  利用鼠标键盘钩子截获密码
  利用鼠标键盘钩子截获密码
最新招聘信息

您现在的位置:立华软件园->安全防线->黑客编程
RPC/XDR/NFS系列之----RPC编程初战
发表日期:2003-08-27作者:[] 出处:  

原  作:Douglas E. Comer & David L. Stevens

     << Internetworking With TCP/IP Vol III >>

整理修改:scz < mailto: cloudsky@263.net >

概述:

  所有关于原理的部分以后再贴,这里直奔程序设

  计而去。文章中的程序就是书中的程序,但原文

  针对Xinu系统来的,要不就针对Sun RPC来的,

  我这里只有Redhat,改动是一定的,在后面的小

  节里我会指出改动过的地方。完整地演习过这个

  系列,你就不在畏惧RPC。计划在后面的灌水中

  讲述RPC远程过程调用发生缓冲区溢出的原理,

  不过仅仅是计划,要看时间是否允许。

  下面的程序完成一个字典的常规维护工作,代码

  很简单,让我们开始。

测试:

  RedHat6.0测试,如果在solaris下,应该更容易实现,

  因为Sun RPC是事实上的标准,rpcgen是Sun自己的工

  具嘛。

目录:

  ★ 构建一个解决问题的常规应用程序

  ★ 将该常规程序划分成两部分

  ★ 创建一个rpcgen规格说明

  ★ 运行rpcgen

  ★ rpcgen产生的.h文件

  ★ rpcgen产生的XDR转换文件

  ★ rpcgen产生的client代码

  ★ rpcgen产生的server代码

  ★ 编写stub接口过程

  ★ 编译链接client程序

  ★ 编译链接server程序

  ★ 启动服务器执行客户机

  ★ 分离服务器和客户机

  ★ rpcinfo的使用以及portmap原理简介(重要)

  ★ RPC程序编译开关

  ★ RPC编程小结

★ 构建一个解决问题的常规应用程序

下面这个程序很简单,实现一个字典的简单维护工作,不多解释了。

/* dict.c -- main, initw, nextin, insertw, deletew, lookupw */

#include

#include

#include

#include

#define MAXWORD 50  /* maximum length of a command or word */

#define DICTSIZ 100 /* maximum number of entries in dictionary. */

char dict[ DICTSIZ ][ MAXWORD + 1 ]; /* storage for a dictionary of words */

int nwords = 0;           /* number of words in the dictionary */

/* 函数原型 */

int nextin ( char * cmd, char * word );

int initw  ( void );

int insertw ( const char * word );

int deletew ( const char * word );

int lookupw ( const char * word );

/* ------------------------------------------------------------------

* main -- insert, delete, or lookup words in a dictionary as specified

* ------------------------------------------------------------------ */

int main ( int argc, char * argv[] )

{

  char word[ MAXWORD + 1 ]; /* space to hold word from input line */

  char cmd;

  int wordlen; /* length of input word */

  printf( "Please input:\n" );

  while ( 1 )

  {

    wordlen = nextin( &cmd, word );

    if ( wordlen < 0 )

    {

      exit( 0 );

    }

    switch ( cmd )

    {

    case 'I': /* 初始化 */

      initw();

      printf( "Dictionary initialized to empty.\n" );

      break;

    case 'i': /* 插入 */

      insertw( word );

      printf( "%s inserted.\n", word );

      break;

    case 'd': /* 删除 */

      if ( deletew( word ) )

      {

        printf( "%s deleted.\n", word );

      }

      else

      {

        printf( "%s not found.\n", word );

      }

      break;

    case 'l': /* 查询 */

      if ( lookupw( word ) )

      {

        printf( "%s was found.\n", word );

      }

      else

      {

        printf( "%s was not found.\n", word );

      }

      break;

    case 'q': /* 退出 */

      printf( "Program quits.\n" );

      exit( 0 );

      break;

    default: /* 非法输入 */

      printf( "command %c invalid.\n", cmd );

      break;

    } /* end of switch */

  } /* end of while */

  return 0;

} /* end of main */

/* ------------------------------------------------------------------

* nextin -- read a command and(possibly) a word from the next input line

* ------------------------------------------------------------------ */

int nextin ( char * cmd, char * word )

{

  int i, ch;

  ch = getc( stdin );

  while ( isspace( ch ) )

  {

    ch = getc( stdin );

  } /* end of while */

  if ( ch == EOF )

  {

    return( -1 );

  }

  *cmd = ( char )ch;

  ch = getc( stdin );

  while ( isspace( ch ) )

  {

    ch = getc( stdin );

  } /* end of while */

  if ( ch == EOF )

  {

    return( -1 );

  }

  if ( ch == '\n' )

  {

    return( 0 );

  }

  i = 0;

  while ( !isspace( ch ) )

  {

    if ( ++i > MAXWORD )

    {

      printf( "error: word too long.\n" );

      exit( 1 );

    }

    *word++ = ch;

    ch = getc( stdin );

  } /* end of while */

  *word = '\0'; /* 原来的代码这里有问题 */

  return i;

} /* end of nextin */

/* ------------------------------------------------------------------

* initw -- initialize the dictionary to contain no words at all

* ------------------------------------------------------------------ */

int initw ( void )

{

  nwords = 0;

  return 1;

} /* end of initw */

/* ------------------------------------------------------------------

* insertw -- insert a word in the dictionary

* ------------------------------------------------------------------ */

int insertw ( const char * word )

{

  strcpy( dict[nwords], word );

  nwords++;

  return( nwords );

} /* end of insertw */

/* ------------------------------------------------------------------

* deletew -- delete a word from the dictionary

* ------------------------------------------------------------------ */

int deletew ( const char * word )

{

  int i;

  for ( i = 0; i < nwords; i++ )

  {

    if ( strcmp( word, dict[i] ) == 0 )

    {

      nwords--;

      strcpy( dict[i], dict[nwords] );

      return( 1 );

    }

  } /* end of for */

  return( 0 );

} /* end of deletew */

/* ------------------------------------------------------------------

* lookupw -- look up a word in the dictionary

* ------------------------------------------------------------------ */

int lookupw ( const char * word )

{

  int i;

  for ( i = 0; i < nwords; i++ )

  {

    if ( strcmp( word, dict[i] ) == 0 )

    {

      return( 1 );

    }

  } /* end of for */

  return( 0 );

} /* end of lookupw */

[scz@ /home/scz/src]> cat > dict.c

[scz@ /home/scz/src]> gcc -Wall -O3 -o dict dict.c

[scz@ /home/scz/src]> strip dict

[scz@ /home/scz/src]> ./dict

Please input:

II < -- -- -- 原来的例子,怀疑作者并没有实际测试过,这里有点问题

Dictionary initialized to empty.

i word1

word1 inserted.

i word2

word2 inserted.

i word3

word3 inserted.

l word2

word2 was found.

d word2

word2 deleted.

l word2

word2 was not found.

qq < -- -- -- 问题同上,请仔细阅读nextin()函数的代码

Program quits.

[scz@ /home/scz/src]>

现在我们拥有了一个解决的的常规程序,该程序不是分布式的。

★ 将该常规程序划分成两部分

下图是常规程序的函数关系图。

main ---- nextin

   |

   |

   ---- insertw

   |

   |

   ---- initw

   |

   |

   ---- deletew

   |

   |

   ---- lookupw

nextin用于读取下一个输入行,需要访问标准输入stdin,应该和main函数放在一起。

  原则:执行I/O或者访问了文件句柄的过程不能轻易转移到远程主机上。

lookupw需要访问全部单词数据库,如果执行lookupw的主机和字典所在主机不是同一主机,

则对lookupw的RPC调用就必须将整个字典作为参数传递,这是不可取的。

  原则:执行过程的主机应该和过程执行中需访问数据所在主机一致。

于是可以按照如下图示划分远程过程:

client端          server端

发起RPC远程过程调用端    响应RPC远程过程调用端

------------        -------------------------------------

|     |  RPC调用   |                  |

|  main -|----------------|  initw  lookupw        |

|     |        |           字典数据结构 |

|  nextin |        |  insertw deletew        |

|     |        |                  |

------------        -------------------------------------

/* dict1.c -- main, nextin */

#include

#include

#define MAXWORD 50  /* maximum length of a command or word */

/* ------------------------------------------------------------------

* main -- insert, delete, or lookup words in a dictionary as specified

* ------------------------------------------------------------------ */

int main ( int argc, char * argv[] )

{

  char word[ MAXWORD + 1 ]; /* space to hold word from input line */

  char cmd;

  int wordlen; /* length of input word */

  printf( "Please input:\n" );

  while ( 1 )

  {

    wordlen = nextin( &cmd, word );

    if ( wordlen < 0 )

    {

      exit( 0 );

    }

    switch ( cmd )

    {

    case 'I': /* 初始化 */

      initw();

      printf( "Dictionary initialized to empty.\n" );

      break;

    case 'i': /* 插入 */

      insertw( word );

      printf( "%s inserted.\n", word );

      break;

    case 'd': /* 删除 */

      if ( deletew( word ) )

      {

        printf( "%s deleted.\n", word );

      }

      else

      {

        printf( "%s not found.\n", word );

      }

      break;

    case 'l': /* 查询 */

      if ( lookupw( word ) )

      {

        printf( "%s was found.\n", word );

      }

      else

      {

        printf( "%s was not found.\n", word );

      }

      break;

    case 'q': /* 退出 */

      printf( "Program quits.\n" );

      exit( 0 );

      break;

    default: /* 非法输入 */

      printf( "command %c invalid.\n", cmd );

      break;

    } /* end of switch */

  } /* end of while */

  return 0;

} /* end of main */

/* ------------------------------------------------------------------

* nextin -- read a command and(possibly) a word from the next input line

* ------------------------------------------------------------------ */

int nextin ( char * cmd, char * word )

{

  int i, ch;

  ch = getc( stdin );

  while ( isspace( ch ) )

  {

    ch = getc( stdin );

  } /* end of while */

  if ( ch == EOF )

  {

    return( -1 );

  }

  *cmd = ( char )ch;

  ch = getc( stdin );

  while ( isspace( ch ) )

  {

    ch = getc( stdin );

  } /* end of while */

  if ( ch == EOF )

  {

    return( -1 );

  }

  if ( ch == '\n' )

  {

    return( 0 );

  }

  i = 0;

  while ( !isspace( ch ) )

  {

    if ( ++i > MAXWORD )

    {

      printf( "error: word too long.\n" );

      exit( 1 );

    }

    *word++ = ch;

    ch = getc( stdin );

  } /* end of while */

  *word = '\0';

  return i;

} /* end of nextin */

*******************************************************************************

/* dict2.c -- initw, insertw, deletew, lookupw */

#define MAXWORD 50  /* maximum length of a command or word */

#define DICTSIZ 100 /* maximum number of entries in dictionary. */

char dict[ DICTSIZ ][ MAXWORD + 1 ]; /* storage for a dictionary of words */

int nwords = 0;           /* number of words in the dictionary */

/* ------------------------------------------------------------------

* initw -- initialize the dictionary to contain no words at all

* ------------------------------------------------------------------ */

int initw ( void )

{

  nwords = 0;

  return 1;

} /* end of initw */

/* ------------------------------------------------------------------

* insertw -- insert a word in the dictionary

* ------------------------------------------------------------------ */

int insertw ( const char * word )

{

  strcpy( dict[nwords], word );

  nwords++;

  return( nwords );

} /* end of insertw */

/* ------------------------------------------------------------------

* deletew -- delete a word from the dictionary

* ------------------------------------------------------------------ */

int deletew ( const char * word )

{

  int i;

  for ( i = 0; i < nwords; i++ )

  {

    if ( strcmp( word, dict[i] ) == 0 )

    {

      nwords--;

      strcpy( dict[i], dict[nwords] );

      return( 1 );

    }

  } /* end of for */

  return( 0 );

} /* end of deletew */

/* ------------------------------------------------------------------

* lookupw -- look up a word in the dictionary

* ------------------------------------------------------------------ */

int lookupw ( const char * word )

{

  int i;

  for ( i = 0; i < nwords; i++ )

  {

    if ( strcmp( word, dict[i] ) == 0 )

    {

      return( 1 );

    }

  } /* end of for */

  return( 0 );

} /* end of lookupw */

注意,对于符号常量MAXWORD的定义在两边都出现了。

[scz@ /home/scz/src]> cat > dict1.c

[scz@ /home/scz/src]> cat > dict2.c

[scz@ /home/scz/src]> gcc -O3 -o dict1.o -c dict1.c

[scz@ /home/scz/src]> gcc -O3 -o dict2.o -c dict2.c

此时进行部分编译(-c选项),可以提前修正很多语法错误,避免程序员的注意

力从RPC上移开。这里的两部分代码不构成完整的应用,剩下的代码以后增加。

★ 创建一个rpcgen规格说明

这个规格说明文件包括:

  . 声明在client或者(这更常见)server(远程程序)中所使用的常量

  . 声明所使用的数据类型(特别是对远程过程的参数)

  . 声明远程程序、每个程序中所包含的过程、以及它们的参数类型

RPC使用一些数字来标识远程程序以及在这些程序中的远程过程。在规格说明

文件中的程序声明定义了诸如程序的RPC号、版本号、以及分配给程序中的过

程的编号等等。所有这些声明都必须用RPC编程语言给出,而不是用C。在RPC

中string代表以null结束的字符串,而C用char *表示,必须注意这些细微的

差别。

文件rdict.x给出了一个rpcgen规格说明,包含了字典程序之RPC版的声明。

/* rdict.x */

/* RPC declarations for dictionary program */

const MAXWORD = 50;  /* maximum length of a command or word */

const DICTSIZ = 100; /* number of entries in dictionary */

struct example   /* unused structure declared here to */

{

  int exfield1; /* illustrate how rpcgen builds XDR */

  char exfield2; /* routines to convert structures */

};

/* ------------------------------------------------------------------

* RDICTPROG -- remote program that provides insert, delete, and lookup

* ------------------------------------------------------------------ */

program RDICTPROG /* name of remote program ( not used ) */

{

  version RDICTVERS /* declaration of version ( see below ) */

  {

    int INITW ( void )   = 1; /* first procedure in this program */

    int INSERTW ( string ) = 2; /* second procedure in this program */

    int DELETEW ( string ) = 3; /* third procedure in this program */

    int LOOKUPW ( string ) = 4; /* fourth procedure in this program */

  } = 1; /* definition of the program version */

} = 0x30090949; /* remote program number ( must be unique ) */

一个rpcgen规格说明文件并没有囊括在最初的程序中的所能找到的所有声明,仅仅

定义了那些在client和server之间要共享的常量和数据类型,或者是那些需要指明

的参数。

按照约定,规格说明文件使用大写名字定义过程和程序,并不绝对要求使用大写,

但这样做有助于避免冲突。

★ 运行rpcgen

[scz@ /home/scz/src]> cat > rdict.x

[scz@ /home/scz/src]> rpcgen rdict.x

[scz@ /home/scz/src]> ls -l rdict* 

-rw-r--r--  1 scz   users    1559 Feb 17 17:18 rdict.h

-rw-r--r--  1 scz   users    1138 Feb 17 17:18 rdict.x

-rw-r--r--  1 scz   users    1466 Feb 17 17:18 rdict_clnt.c

-rw-r--r--  1 scz   users    2623 Feb 17 17:18 rdict_svc.c

-rw-r--r--  1 scz   users     297 Feb 17 17:18 rdict_xdr.c

[scz@ /home/scz/src]>

rpcgen将生成四个文件,分别是rdict.h, rdict_clnt.c, rdict_svc.c和rdict_xdr.c。

注意生成的四个文件的名字与rpcgen的规格说明文件名相关。

★ rpcgen产生的.h文件

[scz@ /home/scz/src]> cat rdict.h

/*

* Please do not edit this file.

* It was generated using rpcgen.

*/

#ifndef _RDICT_H_RPCGEN

#define _RDICT_H_RPCGEN

#include

#ifdef __cplusplus

extern "C" {

#endif

#define MAXWORD 50

#define DICTSIZ 100

struct example {

    int exfield1;

    char exfield2;

};

typedef struct example example;

#define RDICTPROG 0x30090949

#define RDICTVERS 1

#if defined(__STDC__) || defined(__cplusplus)

#define INITW 1

extern int * initw_1(void *, CLIENT *);

extern int * initw_1_svc(void *, struct svc_req *);

#define INSERTW 2

extern int * insertw_1(char **, CLIENT *);

extern int * insertw_1_svc(char **, struct svc_req *);

#define DELETEW 3

extern int * deletew_1(char **, CLIENT *);

extern int * deletew_1_svc(char **, struct svc_req *);

#define LOOKUPW 4

extern int * lookupw_1(char **, CLIENT *);

extern int * lookupw_1_svc(char **, struct svc_req *);

extern int rdictprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);

#else /* K&R C */

#define INITW 1

extern int * initw_1();

extern int * initw_1_svc();

#define INSERTW 2

extern int * insertw_1();

extern int * insertw_1_svc();

#define DELETEW 3

extern int * deletew_1();

extern int * deletew_1_svc();

#define LOOKUPW 4

extern int * lookupw_1();

extern int * lookupw_1_svc();

extern int rdictprog_1_freeresult ();

#endif /* K&R C */

/* the xdr functions */

#if defined(__STDC__) || defined(__cplusplus)

extern bool_t xdr_example (XDR *, example*);

#else /* K&R C */

extern bool_t xdr_example ();

#endif /* K&R C */

#ifdef __cplusplus

}

#endif

#endif /* !_RDICT_H_RPCGEN */

[scz@ /home/scz/src]>

该文件包含了在规格说明文件中所声明的所有常量和数据类型的C的合法声明。

此外rpcgen增加了对远程过程的定义。

#define INSERTW 2

extern int * insertw_1(char **, CLIENT *);

extern int * insertw_1_svc(char **, struct svc_req *);

过程名insertw_1取自业已声明过的过程名INSERTW,只是被转换成小写,并附

加了一个下划线和程序的版本号1。insertw_1对应client端的stub通信例程,

client端的stub接口例程需要程序员自己编写。insertw_1_svc对应server端的

stub接口例程,需要程序员自己编写。server端的stub通信例程已经由rpcgen

产生的代码提供了。

scz注:这里与原书中有重要区别,请仔细对比P234(第2版 vol III)开始的

    章节。如果照搬,会失败。

server端的stub通信例程调用名为insertw_1_svc的stub接口例程,该调用使

用rpcgen所选择的参数。允许程序员适当设计insertw_1_svc以便能用正确的

参数调用原来常规的insertw。

★ rpcgen产生的XDR转换文件

[scz@ /home/scz/src]> cat rdict_xdr.c 

/*

* Please do not edit this file.

* It was generated using rpcgen.

*/

#include "rdict.h"

bool_t

xdr_example (XDR *xdrs, example *objp)

{

     register long *buf;

     if (!xdr_int (xdrs, &objp->exfield1))

         return FALSE;

     if (!xdr_char (xdrs, &objp->exfield2))

         return FALSE;

    return TRUE;

}

[scz@ /home/scz/src]>

rpcgen产生了一个含有对一些例程调用的文件,这些例程执行XDR转换,

而这种调用是针对远程程序中所声明的所有数据类型的。

我们的例子中唯一的类型声明被取名为example,它定义了一个结构,

文件rdict_xdr.c含有将结构example在本地数据表示和外部数据表示

之间进行转换所需要的代码,这些代码是由rpcgen自动生成的,它为

结构中的每个字段调用XDR库例程。一旦给出了一个声明,这个被声明

的数据类型就可以用做远程过程的参数。如果某个远程过程确实使用

结构example作为参数,rpcgen将在client和server中生成代码,以便

调用过程xdr_example对数据表示进行转换。

★ rpcgen产生的client代码

[scz@ /home/scz/src]> cat rdict_clnt.c

/*

* Please do not edit this file.

* It was generated using rpcgen.

*/

#include /* for memset */

#include "rdict.h"

/* Default timeout can be changed using clnt_control() */

static struct timeval TIMEOUT = { 25, 0 };

int *

initw_1(void *argp, CLIENT *clnt)

{

    static int clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));

    if (clnt_call (clnt, INITW,

        (xdrproc_t) xdr_void, (caddr_t) argp,

        (xdrproc_t) xdr_int, (caddr_t) &clnt_res,

        TIMEOUT) != RPC_SUCCESS) {

        return (NULL);

    }

    return (&clnt_res);

}

int *

insertw_1(char **argp, CLIENT *clnt)

{

    static int clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));

    if (clnt_call (clnt, INSERTW,

        (xdrproc_t) xdr_wrapstring, (caddr_t) argp,

        (xdrproc_t) xdr_int, (caddr_t) &clnt_res,

        TIMEOUT) != RPC_SUCCESS) {

        return (NULL);

    }

    return (&clnt_res);

}

int *

deletew_1(char **argp, CLIENT *clnt)

{

    static int clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));

    if (clnt_call (clnt, DELETEW,

        (xdrproc_t) xdr_wrapstring, (caddr_t) argp,

        (xdrproc_t) xdr_int, (caddr_t) &clnt_res,

        TIMEOUT) != RPC_SUCCESS) {

        return (NULL);

    }

    return (&clnt_res);

}

int *

lookupw_1(char **argp, CLIENT *clnt)

{

    static int clnt_res;

    memset((char *)&clnt_res, 0, sizeof(clnt_res));

    if (clnt_call (clnt, LOOKUPW,

        (xdrproc_t) xdr_wrapstring, (caddr_t) argp,

        (xdrproc_t) xdr_int, (caddr_t) &clnt_res,

        TIMEOUT) != RPC_SUCCESS) {

        return (NULL);

    }

    return (&clnt_res);

}

[scz@ /home/scz/src]>

rdict_clnt.c是个源程序,它将成为本程序分布式版中client端的tub通信例程。

该文件为调用远程程序中的每个远程过程准备好了一个client端的stub通信例程。

这个文件中的代码很有意思,在系列文章的后续部分我们会回头来研究它们,

尤其是clnt_call()函数的使用。现在暂且就这样放到一边去。

★ rpcgen产生的server代码

[scz@ /home/scz/src]> cat rdict_svc.c

/*

* Please do not edit this file.

* It was generated using rpcgen.

*/

#include "rdict.h"

#include

#include

#include

#include

#include

#include

#include

#ifndef SIG_PF

#define SIG_PF void(*)(int)

#endif

static void

rdictprog_1(struct svc_req *rqstp, register SVCXPRT *transp)

{

    union {

        char *insertw_1_arg;

        char *deletew_1_arg;

        char *lookupw_1_arg;

    } argument;

    char *result;

    xdrproc_t _xdr_argument, _xdr_result;

    char *(*local)(char *, struct svc_req *);

    switch (rqstp->rq_proc) {

    case NULLPROC:

       

我来说两句】 【发送给朋友】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 RPC/XDR/NFS系列之----RPC编程初战
关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放 / 友情链接

Copyright ©2001-2003 Allrights reserved
e_mail:站长:webmaster(at)lihuasoft.net
网站编程QQ群  
京ICP备05001064号

页面生成时间:0.00519