vtun 隧道建立分析

一、下面分析client端的认证函数(认证过程就是隧道建立过程)

函数client和server分别在文件client.c和server.c中,先分析client.

if( (s = socket(AF_INET,SOCK_STREAM,0))==-1 )。。。

隧道使用sock_STREAM建立的,但是隧道中数据的传输可用TCP也可用UDP。

if( bind(s,(struct sockaddr *)&my_addr,sizeof(my_addr)) )。。。

if( connect_t(s,(struct sockaddr *) &svr_addr, host->timeout) )。。。

看connect_t函数,在netlib.c函数中定义,

/* Connect with timeout */
int connect_t(int s, struct sockaddr *svr, time_t timeout)
{
#if defined(VTUN_SOCKS) && VTUN_SOCKS == 2
     /* Some SOCKS implementations don't support
      * non blocking connect */
     return connect(s,svr,sizeof(struct sockaddr));
#else
     int sock_flags;
     fd_set fdset;
     struct timeval tv;

     tv.tv_usec=0; tv.tv_sec=timeout;

     sock_flags=fcntl(s,F_GETFL);
     if( fcntl(s,F_SETFL,O_NONBLOCK) < 0 )
        return -1;

     if( connect(s,svr,sizeof(struct sockaddr)) < 0 && errno != EINPROGRESS)
        return -1;

     FD_ZERO(&fdset);
     FD_SET(s,&fdset);
     if( select(s+1,NULL,&fdset,NULL,timeout?&tv:NULL) > 0 ){
        int l=sizeof(errno);    
        errno=0;
        getsockopt(s,SOL_SOCKET,SO_ERROR,&errno,&l);
     } else
        errno=ETIMEDOUT;     

     fcntl(s,F_SETFL,sock_flags);

     if( errno )
        return -1;

     return 0;
#endif
}//end connect_t

 

看黑体字部分,fcntl函数。

fcntl功能是获取或设置文件描述符状态,这里讲socket设置为非阻塞,O_NONBLOCK.

 

上面是建立连接的过程,下面分析svr_addr是如何获得的,

回到client.c,

if( connect_t(s,(struct sockaddr *) &svr_addr, host->timeout) )…

找到下面代码,

/* Set server address */
        if( server_addr(&svr_addr, host) < 0 )
            continue;

server_addr在netlib.h中,

int server_addr(struct sockaddr_in *addr, struct vtun_host *host)
{
     struct hostent * hent;

     memset(addr,0,sizeof(struct sockaddr_in));
     addr->sin_family = AF_INET;
     addr->sin_port = htons(vtun.bind_addr.port);

     /* Lookup server's IP address.
      * We do it on every reconnect because server's IP
      * address can be dynamic.
      */
     if( !(hent = gethostbyname(vtun.svr_name)) ){
        vtun_syslog(LOG_ERR, "Can't resolv server address: %s", vtun.svr_name);
        return -1;
     }
     addr->sin_addr.s_addr = *(unsigned long *)hent->h_addr;

     host->sopt.raddr = strdup(inet_ntoa(addr->sin_addr));
     host->sopt.rport = vtun.bind_addr.port;

     return 0;
}

需找到vtun,在main.c中,全局变量,

/* Global options for the server and client */
struct vtun_opts vtun;

/*如果不是服务器端,再根据输入参数进行客户端配置*/
    if(!svr)
    {
        if( argc - optind < 2 ){
            usage();
            exit(1);
        }

        hst = argv[optind++];//vtund server [ip]的第二个参数给hst,这个参数是服务器给客户端定义的名字。

        if( !(host = find_host(hst)) )    //find_host就是从配置文件返回的host
        {
            vtun_syslog(LOG_ERR,"Host %s not found in %s", hst, vtun.cfg_file);
            exit(1);
        }

       vtun.svr_name = strdup(argv[optind]);//vtund server [ip]的ip给vtun.srv_name
    }

 

svr_addr就是客户端命令行参数中的那个IP!(也可是配置文件中的)

 

建立连接之后,是通过auth.c文件进行最后隧道的建立,再次说明,隧道建立的过程使用的是tcp,并没有封装解封操作。

主要涉及两个函数:

struct vtun_host * auth_server(int fd)

int auth_client(int fd, struct vtun_host *host)

我们来看一下,客户端认证时干了什么,分析auth_client,

/* Authentication (Client side) */
int auth_client(int fd, struct vtun_host *host)
{
    char buf[VTUN_MESG_SIZE], chal[VTUN_CHAL_SIZE];
    int stage, success=0 ;
    stage = ST_INIT;

    while( readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0 )
    {
        buf[sizeof(buf)-1]='\0';
        switch( stage )
        {
            case ST_INIT:
                if( !strncmp(buf,"VTUN",4) )
                {
                   stage = ST_HOST;
                    print_p(fd,"HOST: %s\n",host->host);
                    continue;
                }
                break;
            case ST_HOST:
                if( !strncmp(buf,"OK",2) && cs2cl(buf,chal))
                {
                    stage = ST_CHAL;

                   encrypt_chal(chal,host->passwd);
                   print_p(fd,"CHAL: %s\n", cl2cs(chal));
                    continue;
                }
                break;
            case ST_CHAL:
                if( !strncmp(buf,"OK",2) && cf2bf(buf,host) )
                    success = 1;
                break;
        }
        break;
    }//end while
    return success;
}

auth_client的参数fd是什么,就是客户端认证套接字描述符。

print_p就是向server发送认证消息——host和passwd.

 

int print_p(int fd,const char *fmt, ...)
{
    char buf[VTUN_MESG_SIZE];
    va_list ap;

    memset(buf,0,sizeof(buf));

    /* print the argument string */
    va_start(ap, fmt);
    vsnprintf(buf,sizeof(buf)-1, fmt, ap);
    va_end(ap);
    return write_n(fd, buf, sizeof(buf));
}

 

二、下面分析server端的认证函数

if( (host=auth_server(sock)) )

/* Authentication (Server side) */
struct vtun_host * auth_server(int fd)
{
        char chal_req[VTUN_CHAL_SIZE], chal_res[VTUN_CHAL_SIZE];   
    char buf[VTUN_MESG_SIZE], *str1, *str2;
        struct vtun_host *h = NULL;
    char *host = NULL;
    int  stage;

        set_title("authentication");

    print_p(fd,"VTUN server ver %s\n",VTUN_VER);

    stage = ST_HOST;

    while( readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0 ){
       buf[sizeof(buf)-1]='\0';
       strtok(buf,"\r\n");

       if( !(str1=strtok(buf," :")) )
          break;
       if( !(str2=strtok(NULL," :")) )
          break;

       switch( stage ){
         case ST_HOST:
            if( !strcmp(str1,"HOST") ){
           host = strdup(str2);

           gen_chal(chal_req);
           print_p(fd,"OK CHAL: %s\n", cl2cs(chal_req));

           stage = ST_CHAL;
           continue;
            }
        break;
         case ST_CHAL:
            if( !strcmp(str1,"CHAL") ){
           if( !cs2cl(str2,chal_res) )
              break;
           if( !(h = find_host(host)) )
              break;

           decrypt_chal(chal_res, h->passwd);          
           if( !memcmp(chal_req, chal_res, VTUN_CHAL_SIZE) ){
              /* Auth successeful. */

              /* Lock host */   
              if( lock_host(h) < 0 ){
                 /* Multiple connections are denied */
                 h = NULL;
                 break;
              }   
              print_p(fd,"OK FLAGS: %s\n", bf2cf(h));
            } else
              h = NULL;
            }
        break;
        }
       break;
    }

    if( host )
       free(host);

    if( !h )
       print_p(fd,"ERR\n");   

    return h;
}

 

认证成功后用print_p想client发送认证成功信息即——ok字符串。

posted @ 2012-09-23 22:03  helloweworld  阅读(1112)  评论(0编辑  收藏  举报