引言

  但wifidog启动时,会自动启动认证服务器心跳检测线程,此线程默认每隔60s与认证服务器交互一次,会将路由器的信息(系统启动时长,内存使用情况和系统平均负载)告知认证服务器,并通过一个"ping"字符串作为信号,而当认证服务器接收到此数据包后,会返回一个"pong"给路由器,具体我们看看代码。

代码片段1.1

此段代码很简单,就是调用ping函数,然后等待60s

 void
thread_ping(void *arg)
{
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER;
struct timespec timeout; while () {
/* 调用ping,具体代码看 代码片段1.2 */
debug(LOG_DEBUG, "Running ping()");
ping(); /* 睡眠一个checkinterval,默认为60s */
timeout.tv_sec = time(NULL) + config_get_config()->checkinterval;
timeout.tv_nsec = ; pthread_mutex_lock(&cond_mutex); pthread_cond_timedwait(&cond, &cond_mutex, &timeout); pthread_mutex_unlock(&cond_mutex);
}

代码片段1.2

 static void
ping(void)
{
ssize_t numbytes;
size_t totalbytes;
int sockfd, nfds, done;
char request[MAX_BUF];
fd_set readfds;
struct timeval timeout;
FILE * fh;
unsigned long int sys_uptime = ;
unsigned int sys_memfree = ;
float sys_load = ;
t_auth_serv *auth_server = NULL;
auth_server = get_auth_server(); debug(LOG_DEBUG, "Entering ping()"); /* 其实认证服务器就是一个web服务器,路由器跟他做通信行为就是通过发送http请求进行通信,首先先连接认证服务器的http端口,获取其socket */
sockfd = connect_auth_server();
if (sockfd == -) {
/* 无法连接认证服务器,connect_auth_server分析见 代码片段1.3 */
return;
} /*
* 从/proc文件系统获取路由器信息
*/
if ((fh = fopen("/proc/uptime", "r"))) {
fscanf(fh, "%lu", &sys_uptime);
fclose(fh);
}
if ((fh = fopen("/proc/meminfo", "r"))) {
while (!feof(fh)) {
if (fscanf(fh, "MemFree: %u", &sys_memfree) == ) {
while (!feof(fh) && fgetc(fh) != '\n');
}
else {
break;
}
}
fclose(fh);
}
if ((fh = fopen("/proc/loadavg", "r"))) {
fscanf(fh, "%f", &sys_load);
fclose(fh);
} /*
* 准备http请求包
*/
snprintf(request, sizeof(request) - ,
"GET %s%sgw_id=%s&sys_uptime=%lu&sys_memfree=%u&sys_load=%.2f&wifidog_uptime=%lu HTTP/1.0\r\n"
"User-Agent: WiFiDog %s\r\n"
"Host: %s\r\n"
"\r\n",
auth_server->authserv_path,
auth_server->authserv_ping_script_path_fragment,
config_get_config()->gw_id,
sys_uptime,
sys_memfree,
sys_load,
(long unsigned int)((long unsigned int)time(NULL) - (long unsigned int)started_time),
VERSION,
auth_server->authserv_hostname); debug(LOG_DEBUG, "HTTP Request to Server: [%s]", request);
/* 发送 */
send(sockfd, request, strlen(request), ); debug(LOG_DEBUG, "Reading response"); numbytes = totalbytes = ;
done = ;
do {
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
/* 设置超时30s */
timeout.tv_sec = ;
timeout.tv_usec = ;
nfds = sockfd + ; nfds = select(nfds, &readfds, NULL, NULL, &timeout); if (nfds > ) {
/* 多路复用 */
numbytes = read(sockfd, request + totalbytes, MAX_BUF - (totalbytes + ));
if (numbytes < ) {
debug(LOG_ERR, "An error occurred while reading from auth server: %s", strerror(errno));
close(sockfd);
return;
}
else if (numbytes == ) {
done = ;
}
else {
totalbytes += numbytes;
debug(LOG_DEBUG, "Read %d bytes, total now %d", numbytes, totalbytes);
}
}
else if (nfds == ) {
debug(LOG_ERR, "Timed out reading data via select() from auth server");
close(sockfd);
return;
}
else if (nfds < ) {
debug(LOG_ERR, "Error reading data via select() from auth server: %s", strerror(errno));
close(sockfd);
return;
}
} while (!done);
close(sockfd); debug(LOG_DEBUG, "Done reading reply, total %d bytes", totalbytes); request[totalbytes] = '\0'; debug(LOG_DEBUG, "HTTP Response from Server: [%s]", request);
/* 判断认证服务器返回包中有没有"Pong"字符串 */
if (strstr(request, "Pong") == ) {
debug(LOG_WARNING, "Auth server did NOT say pong!"); }
else {
debug(LOG_DEBUG, "Auth Server Says: Pong");
} return;
}

代码片段1.3

connect_auth_server函数用于连接认证服务器并返回socket套接字,其具体实现是通过_connect_auth_server实现的,而在_connect_auth_server中,递归认证服务器列表,每次递归中首先会根据认证服务器域名获取ip,如果失败,会通过公共网站判断是否为DNS问题,再判断是否为认证服务器问题,如果都失败,继续递归,否则返回认证服务器socket。

 int connect_auth_server() {
int sockfd; LOCK_CONFIG();
/* 连接认证服务器 */
sockfd = _connect_auth_server();
UNLOCK_CONFIG(); if (sockfd == -) {
debug(LOG_ERR, "Failed to connect to any of the auth servers");
/* 标记认证服务器离线 */
mark_auth_offline();
}
else {
debug(LOG_DEBUG, "Connected to auth server");
/* 标记认证服务器在线 */
mark_auth_online();
}
return (sockfd);
} int _connect_auth_server(int level) {
s_config *config = config_get_config();
t_auth_serv *auth_server = NULL;
struct in_addr *h_addr;
int num_servers = ;
char * hostname = NULL;
/* 公共网站,用于判断DNS问题 */
char * popular_servers[] = {
"www.google.com",
"www.yahoo.com",
NULL
};
char ** popularserver;
char * ip;
struct sockaddr_in their_addr;
int sockfd; /* 用于递归,因为可能会有多个认证服务器,如果第一个认证服务器无法连接,会递归尝试连接后面的认证服务器,此参数用于递归判断的,当成功连接任意一个认证服务器后停止 */
level++; /*
* 获取认证服务器数量
*/
for (auth_server = config->auth_servers; auth_server; auth_server = auth_server->next) {
num_servers++;
}
debug(LOG_DEBUG, "Level %d: Calculated %d auth servers in list", level, num_servers);
/* 已经尝试递归连接所有认证服务器,都不能连接 */
if (level > num_servers) {
return (-);
} /*
* 获取认证服务器列表中的第一个认证服务器
*/
auth_server = config->auth_servers;
hostname = auth_server->authserv_hostname;
debug(LOG_DEBUG, "Level %d: Resolving auth server [%s]", level, hostname);
h_addr = wd_gethostbyname(hostname);
if (!h_addr) {
/*
* DNS解析错误,尝试解析公共网站判断是否为DNS错误
*/
debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] failed", level, hostname); for (popularserver = popular_servers; *popularserver; popularserver++) {
debug(LOG_DEBUG, "Level %d: Resolving popular server [%s]", level, *popularserver);
h_addr = wd_gethostbyname(*popularserver);
/* 公共网站DNS解析正确 */
if (h_addr) {
debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] succeeded = [%s]", level, *popularserver, inet_ntoa(*h_addr));
break;
}
else {
debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] failed", level, *popularserver);
}
} if (h_addr) {
/* DNS正确,尝试递归下一个认证服务器 */
free (h_addr); debug(LOG_DEBUG, "Level %d: Marking auth server [%s] as bad and trying next if possible", level, hostname);
if (auth_server->last_ip) {
free(auth_server->last_ip);
auth_server->last_ip = NULL;
}
/* 将此认证服务器放入bad_server链表,并将config->auth_server指向认证服务器的下一个节点 */
mark_auth_server_bad(auth_server);
/* 递归 */
return _connect_auth_server(level);
}
else {
/* DNS问题,标记路由器离线 */
mark_offline();
debug(LOG_DEBUG, "Level %d: Failed to resolve auth server and all popular servers. "
"The internet connection is probably down", level);
return(-);
}
}
else {
/* DNS解析成功 */
ip = safe_strdup(inet_ntoa(*h_addr));
debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] succeeded = [%s]", level, hostname, ip); if (!auth_server->last_ip || strcmp(auth_server->last_ip, ip) != ) {
/* DNS解析到的IP与我们上一次连接的IP不同,更新上一次连接的IP */
debug(LOG_DEBUG, "Level %d: Updating last_ip IP of server [%s] to [%s]", level, hostname, ip);
if (auth_server->last_ip) free(auth_server->last_ip);
auth_server->last_ip = ip; /* 将此新的认证服务器IP添加到iptables中的可访问外网地址中 */
fw_clear_authservers();
fw_set_authservers();
}
else {
/*
* DNS解析到的IP与我们上一次连接的IP相同
*/
free(ip);
} /*
* 连接
*/
debug(LOG_DEBUG, "Level %d: Connecting to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(auth_server->authserv_http_port);
their_addr.sin_addr = *h_addr;
memset(&(their_addr.sin_zero), '\0', sizeof(their_addr.sin_zero));
free (h_addr); if ((sockfd = socket(AF_INET, SOCK_STREAM, )) == -) {
debug(LOG_ERR, "Level %d: Failed to create a new SOCK_STREAM socket: %s", strerror(errno));
return(-);
} if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -) {
/*
* 连接失败
* 将此认证服务器放入bad_server链表,并将config->auth_server指向认证服务器的下一个节点
*/
debug(LOG_DEBUG, "Level %d: Failed to connect to auth server %s:%d (%s). Marking it as bad and trying next if possible", level, hostname, auth_server->authserv_http_port, strerror(errno));
close(sockfd);
mark_auth_server_bad(auth_server);
return _connect_auth_server(level); /* Yay recursion! */
}
else {
/*
* 连接成功
*/
debug(LOG_DEBUG, "Level %d: Successfully connected to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
return sockfd;
}
}
}

wifidog源码分析 - 认证服务器心跳检测线程的更多相关文章

  1. wifidog源码分析 - 用户连接过程

    引言 之前的文章已经描述wifidog大概的一个工作流程,这里我们具体说说wifidog是怎么把一个新用户重定向到认证服务器中的,它又是怎么对一个已认证的用户实行放行操作的.我们已经知道wifidog ...

  2. wifidog源码分析 - wifidog原理

    wifidog是一个用于配合认证服务器实现无线网页认证功能的程序,常见的情景就是使用于公共场合的无线wifi接入点,首先移动设备会连接公共wifi接入点,之后会弹出网页要求输入用户名密码,认证过后才能 ...

  3. wifidog源码分析 - wifidog原理 tiger

    转:http://www.cnblogs.com/tolimit/p/4223644.html wifidog源码分析 - wifidog原理 wifidog是一个用于配合认证服务器实现无线网页认证功 ...

  4. Solr4.8.0源码分析(3)之index的线程池管理

    Solr4.8.0源码分析(3)之index的线程池管理 Solr建索引时候是有最大的线程数限制的,它由solrconfig.xml的<maxIndexingThreads>8</m ...

  5. HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

    HDFS源码分析数据块复制监控线程ReplicationMonitor(二)

  6. 【Zookeeper】源码分析之服务器(二)

    一.前言 前面阐述了服务器的总体框架,下面来分析服务器的所有父类ZooKeeperServer. 二.ZooKeeperServer源码分析 2.1 类的继承关系 public class ZooKe ...

  7. 【Zookeeper】源码分析之服务器(二)之ZooKeeperServer

    一.前言 前面阐述了服务器的总体框架,下面来分析服务器的所有父类ZooKeeperServer. 二.ZooKeeperServer源码分析 2.1 类的继承关系 public class ZooKe ...

  8. 【Zookeeper】源码分析之服务器(四)

    一.前言 前面分析了LeaderZooKeeperServer,接着分析FollowerZooKeeperServer. 二.FollowerZooKeeperServer源码分析 2.1 类的继承关 ...

  9. Django-rest-framework源码分析----认证

    一.前言 1.1.安装 两种方式: github pip直接安装 pip install django-rest-framework 1.2.需要先了解的一些知识 理解下面两个知识点非常重要,djan ...

随机推荐

  1. Ashampoo Driver Updater - 阿香婆驱动安装

    Ashampoo Driver Updater 让系统更完美 – 永远有最新的驱动,出错或旧的驱动是每个电脑系统的恶梦.时不时,驱动会丢失或不可避免的过时.Ashampoo Driver Update ...

  2. canvas实现验证码

    在通常的登录界面我们都可以看到验证码,验证码的作用是检测是不是人在操作,防止机器等非人操作,防止数据库被轻而易举的攻破. 验证码一般用PHP和java等后端语言编写. 但是在前端,用canva或者SV ...

  3. 安全测试 一次关于WEB的URL安全测试

    一次关于WEB的URL安全测试 by:授客 QQ:1033553122     测试思路: 时间精力问题,对web安全这块也没咋深入研究,但因为某个小插曲,公司要求先做个简单的安全测试,主要是针对UR ...

  4. loadrunner 运行场景-命令行运行场景

    运行场景-命令行运行场景 by:授客 QQ:1033553122 1 相对路径与绝对路径 在场景中为脚本指定一个相对位置,可以是相对于当前场景目录或lr安装目录. 当你运行一个场景,场景自动从这个相对 ...

  5. 安装Django(1)

    安装Django 注意:因为这是web项目将来要部署在Linux上,所以使用centos/ubuntu:因为Python3是将来的趋势,所以使用Python3做为开发语言.本人使用的开发模式操作系统: ...

  6. nginx 配置简单网站项目(linux下)

    1.新建html2与html3两个网站项目测试,而html是本身就有,记得到/etc/hosts 添加dns记录 2.修改nginx.conf文件 3.测试访问 中间用到一些nginx的命令,就不截图 ...

  7. Apache POI导出excel表格

    项目中我们经常用到导出功能,将数据导出以便于审查和统计等.本文主要使用Apache POI实现导出数据. POI中文文档 简介 ApachePOI是Apache软件基金会的开放源码函式库,POI提供A ...

  8. 洗礼灵魂,修炼python(30)--装饰器(2)—>装饰器总结+进阶使用

    在上一篇博文的经典案例中,我想你应该对装饰器有很好的了解了,不过光有那些还不够真的,还需要总结和进阶一下,所以本篇博文解析装饰器进阶. 装饰器 1.什么是装饰器? 个人理解:装饰器又叫语法糖,指的是对 ...

  9. python中remove的一些坑

    前几天,使用python时遇到这么一个需求,删除一个列表中值为1的元素.我寻思着使用remove方法,但是remove方法只会删除第一个,于是我使用for循环去删除.代码和运行结果如下: 当时这个结果 ...

  10. CRM项目之stark组件(1)

    admin组件 admin组件的简单使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你可以在项目的 settings.py ...