zoukankan      html  css  js  c++  java
  • 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字符串。

  • 相关阅读:
    0Day – 2011.01.26
    JQuery_PHP 开始新的旅途
    0Day – 2011.01.25
    0Day – 2011.02.04
    Delphi 必须的一致.
    0Day – 2011.01.28
    0Day – 2011.02.23[From B4A]
    足球 看球悲惨的回忆.
    Delphi – EurekaLog6.1.01Ent下载地址
    ubuntu 拨号
  • 原文地址:https://www.cnblogs.com/helloweworld/p/2699262.html
Copyright © 2011-2022 走看看