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字符串。