分析pptpd程序中关于执行pptpd和pppd程序的部分源代码

JavaGG 发布于 2009/05/05 18:04
阅读 1K+
收藏 0

最近使用pptpd部署vpn server,为了更好的了解pptpd程序,我特意看了它的源代码,并做了一些记录,这是其中一篇记录,有不正确的地方请各位指正,谢谢!
我对linux,对c语言的学习都还不够,写的东西里会有一些不清楚或者错误的地方,希望各位指点,也希望和大家一起讨论。



整个pptpd程序的源头应该是pptpd.c的main()函数。

在pptpd.c的main()函数的420行调用了pptp_manager()函数:

	/* manage connections until SIGTERM */


pptp_manager(argc, argv);

这个函数在pptpmanager.c中。

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

pptpmanager.c中第310行开始的代码,是为处理客户端连接而生成的子进程调用connectCall()函数:
#ifndef HAVE_FORK
switch (ctrl_pid = vfork()) {
#else
switch (ctrl_pid = fork()) {
#endif

                                ......

case 0: /* child */   /* 第310行 */
close(hostSocket);
if (pptp_debug)
syslog(LOG_DEBUG, "MGR: Launching " PPTP_CTRL_BIN " to handle client");
#if !defined(PPPD_IP_ALLOC)
connectCall(clientSocket, firstOpen);
#else
connectCall(clientSocket, 0);
#endif
_exit(1);



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

在pptpmanager.c的453行开始的代码,感觉这里是pptpd里真正为客户端分配ip地址的地方,这部分代码属于connectCall()函数:



#if !defined(PPPD_IP_ALLOC)
extern char localIP[MAX_CONNECTIONS][16];
        /* localIP数组应该存放了所有本地ip,每个数组元素中存放一个ip,
           总的元素个数就是允许的最大连接数 */
extern char remoteIP[MAX_CONNECTIONS][16];

/* for now, this contains all relavant info about a call
 * we are assuming that the remote and local IP's never change
 * and are set at startup.
 */
struct callArray {
        pid_t pid;
        char pppRemote[16];
        char pppLocal[16];
};

        ......

/* Author: Kevin Thayer
 * this routine sets up the arguments for the call handler and calls it. */

static void connectCall(int clientSocket, int clientNumber)
{

#define NUM2ARRAY(array, num) snprintf(array, sizeof(array), "%d", num)
        
        char *ctrl_argv[16];        /* arguments for launching 'pptpctrl' binary */

        int pptpctrl_argc = 0;        /* count the number of arguments sent to pptpctrl */

        ........

#if PPPD_IP_ALLOC    /* 第453行 */
        /* no local or remote address to specify */
        ctrl_argv[pptpctrl_argc++] = "0";
        ctrl_argv[pptpctrl_argc++] = "0";
#else
        /* specify local & remote addresses for this call */
        ctrl_argv[pptpctrl_argc++] = "1";
        ctrl_argv[pptpctrl_argc++] = localIP[clientNumber];  
        ctrl_argv[pptpctrl_argc++] = "1";
        ctrl_argv[pptpctrl_argc++] = remoteIP[clientNumber];

        ........

        /* ok, args are setup: invoke the call handler */
        execve(PPTP_CTRL_BIN, ctrl_argv, environ); 
              /*本函数最后,调用pptpd程序,把ctrl_argv和environ做为参数 */
              /* defaults.h中定义:#define PPTP_CTRL_BIN       SBINDIR "/pptpctrl" */
        syslog(LOG_ERR, "MGR: Failed to exec " PPTP_CTRL_BIN "!");
        _exit(1);
}
#endif


上面connectCall函数中的execve()实际调用了pptpctrl程序,这个程序的源代码应该是pptpctrl.c,看来又再次返回到pptpctrl。这个程序接收ctrl_argv和environ两个参数,其中ctrl_argv数组中包括了localIP和remoteIP两个变量,这正是我感兴趣的部分。
execve()调用成功后不会返回,所以该语句后面跟的是出错以后的处理。

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

pptp_handle_ctrl_connection()函数被pptpctrl.c的main()函数调用,相关代码在main()的尾部:



int main(int argc, char **argv)   /* main()一开始就定义了很多变量 */
{
        char pppLocal[16];                /* local IP to pass to pppd */
        char pppRemote[16];                /* remote IP address to pass to pppd */
        struct sockaddr_in addr;        /* client address */
        socklen_t addrlen;
        int arg = 1;
        int flags;
        struct in_addr inetaddrs[2];
        char *pppaddrs[2] = { pppLocal, pppRemote };

        ......

        /* be ready for a grisly death */
        sigpipe_create();
        sigpipe_assign(SIGTERM);
        NOTE_VALUE(PAC, call_id_pair, htons(-1));
        NOTE_VALUE(PNS, call_id_pair, htons(-1));

        syslog(LOG_INFO, "CTRL: Client %s control connection started", inet_ntoa(addr.sin_addr));
        pptp_handle_ctrl_connection(pppaddrs, inetaddrs);   /* 调用这个函数,处理来自客户端的连接 */
             /* 上面这个函数里就调用了pppaddrs数组和inetaddrs结构,说明在此以前就有赋值 */

        syslog(LOG_DEBUG, "CTRL: Reaping child PPP[%i]", pppfork);
        if (pppfork > 0)  /* 结束用于处理客户端连接的子进程 */
                waitpid(pppfork, NULL, 0);
        syslog(LOG_INFO, "CTRL: Client %s control connection finished", inet_ntoa(addr.sin_addr));

        bail(0);                /* NORETURN */
        return 1;                /* make gcc happy */
}


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

在pptpctrl.c的383行,是pptp_handle_ctrl_connection()函数,它调用了startCall()函数,相关代码:
/*


 * pptp_handle_ctrl_connection
 *
 * 1. read a packet (should be start_ctrl_conn_rqst)
 * 2. reply to packet (send a start_ctrl_conn_rply)
 * 3. proceed with GRE and CTRL connections
 *
 * args: pppaddrs - ppp local and remote addresses (strings)
 *       inetaddrs - local and client socket address
 * retn: 0 success, -1 failure
 */
static void pptp_handle_ctrl_connection(char **pppaddrs, struct in_addr *inetaddrs)
{
    ......

    /* start the call, by launching pppd */
    syslog(LOG_INFO, "CTRL: Starting call (launching pppd, opening GRE)");
    pty_fd = startCall(pppaddrs, inetaddrs);


    ......

}

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

同一文件下的startCall()函数,pptpd为客户端连接建立一个子进程,在这个子进程里调用pppd程序(第634行):

/*


 * startCall
 *
 * Launches PPPD for the call.
 *
 * args:        pppaddrs - local/remote IPs or "" for either/both if none
 * retn:        pty file descriptor
 *
 */
static int startCall(char **pppaddrs, struct in_addr *inetaddrs)
{

        ......

        /* Launch the PPPD  */
#ifndef HAVE_FORK
        switch(pppfork=vfork()){
#else
        switch(pppfork=fork()){
#endif
        case -1:        /* fork() error */
                syslog(LOG_ERR, "CTRL: Error forking to exec pppd");
                _exit(1);

        case 0:                /* child */
                /* 子进程中调用pppd */

                ......

                launch_pppd(pppaddrs, inetaddrs);  /* launch_pppd函数用于调用pppd */
                syslog(LOG_ERR, "CTRL: PPPD launch failed! (launch_pppd did not fork)");
                _exit(1);
        }
        
        close(tty_fd);
        return pty_fd;
}


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

同一文件655行是launch_pppd函数:
/*


 * launch_pppd
 *
 * Launches the PPP daemon. The PPP daemon is responsible for assigning the
 * PPTP client its IP address.. These values are assigned via the command
 * line.
 *
 * Add return of connected ppp interface
 *
 * retn: 0 on success, -1 on failure.
 *
 */
static void launch_pppd(char **pppaddrs, struct in_addr *inetaddrs)
{

        ......

#else

        /* 以下这部分应该是pptpd调用pppd的时候执行的代码 */
/* options for 'normal' pppd */
        
pppd_argv[an++] = "local";

/* If a pppd option file is specified, use it
 * if not, pppd will default to /etc/ppp/options
 */
if (*pppdxfig) {
pppd_argv[an++] = "file";
pppd_argv[an++] = pppdxfig;
}

/* If a speed has been specified, use it
 * if not, use "smart" default (defaults.h)
 */
if (*speed) {
pppd_argv[an++] = speed;
} else {
pppd_argv[an++] = PPP_SPEED_DEFAULT;
}

if (pptpctrl_debug) {
                /* 以下两个if语句在系统日志文件里输出localip和remoteip,
                   在/var/log/messages里可以查到这些输出 */
if (*pppaddrs[0])
syslog(LOG_DEBUG, "CTRL (PPPD Launcher): local address = %s", pppaddrs[0]);
if (*pppaddrs[1])
syslog(LOG_DEBUG, "CTRL (PPPD Launcher): remote address = %s", pppaddrs[1]);
}

if (*pppaddrs[0] || *pppaddrs[1]) {
char pppInterfaceIPs[33];
sprintf(pppInterfaceIPs, "%s:%s", pppaddrs[0], pppaddrs[1]);
pppd_argv[an++] = pppInterfaceIPs;
}
#endif

        ......

        if (pptp_logwtmp) {                    /* logwtmp相关的代码 */
                 pppd_argv[an++] = "plugin";
                 pppd_argv[an++] = "/usr/lib/pptpd/pptpd-logwtmp.so";
                 pppd_argv[an++] = "pptpd-original-ip";
                 pppd_argv[an++] = inet_ntoa(inetaddrs[1]);
        }

/* argv arrays must always be NULL terminated */
pppd_argv[an++] = NULL;
/* make sure SIGCHLD is unblocked, pppd does not expect it */
sigfillset(&sigs);
sigprocmask(SIG_UNBLOCK, &sigs, NULL);
/* run pppd now */
execvp(pppd_argv[0], pppd_argv);   /* 就是这一句调用了pppd程序 */
/* execvp() failed */
syslog(LOG_ERR, 
       "CTRL (PPPD Launcher): Failed to launch PPP daemon. %s",
       strerror(errno));
}


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

关于上面函数中尾部的execvp(pppd_argv[0], pppd_argv)语句:
launch_pppd()函数里定义:
char *pppd_argv[14];


然后函数中对14个数组元素进行定义,其中第0个元素存放了pppd的执行程序:
pppd_argv[an++] = ppp_binary;

从程序代码中还可以看到这个数组的其他元素都被赋了值,这些值应该都是pppd程序的参数。

这里的ppp_binary在本文的头部定义:
static char *ppp_binary = PPP_BINARY;


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

PPP_BINARY变量在configure.in里有定义:
if test "$BSDUSER_PPP" = "yes"; then


  AC_DEFINE(PPP_BINARY, "/usr/sbin/ppp")
else
  if test "$SLIRP" = "yes"; then
    AC_DEFINE(PPP_BINARY, "/bin/slirp")
  else
    AC_DEFINE(PPP_BINARY, "/usr/sbin/pppd")
  fi
fi


[  本帖最后由 sailer_sh 于 2006-2-10 21:51 编辑  ]



加载中
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部