wifidog源码分析 - 认证服务器心跳检测线程
来源:互联网 发布:淘宝8.8 编辑:程序博客网 时间:2024/06/05 18:31
引言
但wifidog启动时,会自动启动认证服务器心跳检测线程,此线程默认每隔60s与认证服务器交互一次,会将路由器的信息(系统启动时长,内存使用情况和系统平均负载)告知认证服务器,并通过一个"ping"字符串作为信号,而当认证服务器接收到此数据包后,会返回一个"pong"给路由器,具体我们看看代码。
代码片段1.1
此段代码很简单,就是调用ping函数,然后等待60s
1 void 2 thread_ping(void *arg) 3 { 4 pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 5 pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER; 6 struct timespec timeout; 7 8 while (1) { 9 /* 调用ping,具体代码看 代码片段1.2 */10 debug(LOG_DEBUG, "Running ping()");11 ping();12 13 /* 睡眠一个checkinterval,默认为60s */14 timeout.tv_sec = time(NULL) + config_get_config()->checkinterval;15 timeout.tv_nsec = 0;16 17 18 pthread_mutex_lock(&cond_mutex);19 20 pthread_cond_timedwait(&cond, &cond_mutex, &timeout);21 22 pthread_mutex_unlock(&cond_mutex);23 }
代码片段1.2
1 static void 2 ping(void) 3 { 4 ssize_t numbytes; 5 size_t totalbytes; 6 int sockfd, nfds, done; 7 char request[MAX_BUF]; 8 fd_set readfds; 9 struct timeval timeout; 10 FILE * fh; 11 unsigned long int sys_uptime = 0; 12 unsigned int sys_memfree = 0; 13 float sys_load = 0; 14 t_auth_serv *auth_server = NULL; 15 auth_server = get_auth_server(); 16 17 debug(LOG_DEBUG, "Entering ping()"); 18 19 /* 其实认证服务器就是一个web服务器,路由器跟他做通信行为就是通过发送http请求进行通信,首先先连接认证服务器的http端口,获取其socket */ 20 sockfd = connect_auth_server(); 21 if (sockfd == -1) { 22 /* 无法连接认证服务器,connect_auth_server分析见 代码片段1.3 */ 23 return; 24 } 25 26 /* 27 * 从/proc文件系统获取路由器信息 28 */ 29 if ((fh = fopen("/proc/uptime", "r"))) { 30 fscanf(fh, "%lu", &sys_uptime); 31 fclose(fh); 32 } 33 if ((fh = fopen("/proc/meminfo", "r"))) { 34 while (!feof(fh)) { 35 if (fscanf(fh, "MemFree: %u", &sys_memfree) == 0) { 36 while (!feof(fh) && fgetc(fh) != '\n'); 37 } 38 else { 39 break; 40 } 41 } 42 fclose(fh); 43 } 44 if ((fh = fopen("/proc/loadavg", "r"))) { 45 fscanf(fh, "%f", &sys_load); 46 fclose(fh); 47 } 48 49 /* 50 * 准备http请求包 51 */ 52 snprintf(request, sizeof(request) - 1, 53 "GET %s%sgw_id=%s&sys_uptime=%lu&sys_memfree=%u&sys_load=%.2f&wifidog_uptime=%lu HTTP/1.0\r\n" 54 "User-Agent: WiFiDog %s\r\n" 55 "Host: %s\r\n" 56 "\r\n", 57 auth_server->authserv_path, 58 auth_server->authserv_ping_script_path_fragment, 59 config_get_config()->gw_id, 60 sys_uptime, 61 sys_memfree, 62 sys_load, 63 (long unsigned int)((long unsigned int)time(NULL) - (long unsigned int)started_time), 64 VERSION, 65 auth_server->authserv_hostname); 66 67 debug(LOG_DEBUG, "HTTP Request to Server: [%s]", request); 68 /* 发送 */ 69 send(sockfd, request, strlen(request), 0); 70 71 debug(LOG_DEBUG, "Reading response"); 72 73 numbytes = totalbytes = 0; 74 done = 0; 75 do { 76 FD_ZERO(&readfds); 77 FD_SET(sockfd, &readfds); 78 /* 设置超时30s */ 79 timeout.tv_sec = 30; 80 timeout.tv_usec = 0; 81 nfds = sockfd + 1; 82 83 nfds = select(nfds, &readfds, NULL, NULL, &timeout); 84 85 if (nfds > 0) { 86 /* 多路复用 */ 87 numbytes = read(sockfd, request + totalbytes, MAX_BUF - (totalbytes + 1)); 88 if (numbytes < 0) { 89 debug(LOG_ERR, "An error occurred while reading from auth server: %s", strerror(errno)); 90 close(sockfd); 91 return; 92 } 93 else if (numbytes == 0) { 94 done = 1; 95 } 96 else { 97 totalbytes += numbytes; 98 debug(LOG_DEBUG, "Read %d bytes, total now %d", numbytes, totalbytes); 99 }100 }101 else if (nfds == 0) {102 debug(LOG_ERR, "Timed out reading data via select() from auth server");103 close(sockfd);104 return;105 }106 else if (nfds < 0) {107 debug(LOG_ERR, "Error reading data via select() from auth server: %s", strerror(errno));108 close(sockfd);109 return;110 }111 } while (!done);112 close(sockfd);113 114 debug(LOG_DEBUG, "Done reading reply, total %d bytes", totalbytes);115 116 request[totalbytes] = '\0';117 118 debug(LOG_DEBUG, "HTTP Response from Server: [%s]", request);119 /* 判断认证服务器返回包中有没有"Pong"字符串 */120 if (strstr(request, "Pong") == 0) {121 debug(LOG_WARNING, "Auth server did NOT say pong!");122 123 }124 else {125 debug(LOG_DEBUG, "Auth Server Says: Pong");126 }127 128 return; 129 }130
代码片段1.3
connect_auth_server函数用于连接认证服务器并返回socket套接字,其具体实现是通过_connect_auth_server实现的,而在_connect_auth_server中,递归认证服务器列表,每次递归中首先会根据认证服务器域名获取ip,如果失败,会通过公共网站判断是否为DNS问题,再判断是否为认证服务器问题,如果都失败,继续递归,否则返回认证服务器socket。
1 int connect_auth_server() { 2 int sockfd; 3 4 LOCK_CONFIG(); 5 /* 连接认证服务器 */ 6 sockfd = _connect_auth_server(0); 7 UNLOCK_CONFIG(); 8 9 if (sockfd == -1) { 10 debug(LOG_ERR, "Failed to connect to any of the auth servers"); 11 /* 标记认证服务器离线 */ 12 mark_auth_offline(); 13 } 14 else { 15 debug(LOG_DEBUG, "Connected to auth server"); 16 /* 标记认证服务器在线 */ 17 mark_auth_online(); 18 } 19 return (sockfd); 20 } 21 22 23 24 int _connect_auth_server(int level) { 25 s_config *config = config_get_config(); 26 t_auth_serv *auth_server = NULL; 27 struct in_addr *h_addr; 28 int num_servers = 0; 29 char * hostname = NULL; 30 /* 公共网站,用于判断DNS问题 */ 31 char * popular_servers[] = { 32 "www.google.com", 33 "www.yahoo.com", 34 NULL 35 }; 36 char ** popularserver; 37 char * ip; 38 struct sockaddr_in their_addr; 39 int sockfd; 40 41 /* 用于递归,因为可能会有多个认证服务器,如果第一个认证服务器无法连接,会递归尝试连接后面的认证服务器,此参数用于递归判断的,当成功连接任意一个认证服务器后停止 */ 42 level++; 43 44 /* 45 * 获取认证服务器数量 46 */ 47 for (auth_server = config->auth_servers; auth_server; auth_server = auth_server->next) { 48 num_servers++; 49 } 50 debug(LOG_DEBUG, "Level %d: Calculated %d auth servers in list", level, num_servers); 51 /* 已经尝试递归连接所有认证服务器,都不能连接 */ 52 if (level > num_servers) { 53 return (-1); 54 } 55 56 /* 57 * 获取认证服务器列表中的第一个认证服务器 58 */ 59 auth_server = config->auth_servers; 60 hostname = auth_server->authserv_hostname; 61 debug(LOG_DEBUG, "Level %d: Resolving auth server [%s]", level, hostname); 62 h_addr = wd_gethostbyname(hostname); 63 if (!h_addr) { 64 /* 65 * DNS解析错误,尝试解析公共网站判断是否为DNS错误 66 */ 67 debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] failed", level, hostname); 68 69 for (popularserver = popular_servers; *popularserver; popularserver++) { 70 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s]", level, *popularserver); 71 h_addr = wd_gethostbyname(*popularserver); 72 /* 公共网站DNS解析正确 */ 73 if (h_addr) { 74 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] succeeded = [%s]", level, *popularserver, inet_ntoa(*h_addr)); 75 break; 76 } 77 else { 78 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] failed", level, *popularserver); 79 } 80 } 81 82 if (h_addr) { 83 /* DNS正确,尝试递归下一个认证服务器 */ 84 free (h_addr); 85 86 debug(LOG_DEBUG, "Level %d: Marking auth server [%s] as bad and trying next if possible", level, hostname); 87 if (auth_server->last_ip) { 88 free(auth_server->last_ip); 89 auth_server->last_ip = NULL; 90 } 91 /* 将此认证服务器放入bad_server链表,并将config->auth_server指向认证服务器的下一个节点 */ 92 mark_auth_server_bad(auth_server); 93 /* 递归 */ 94 return _connect_auth_server(level); 95 } 96 else { 97 /* DNS问题,标记路由器离线 */ 98 mark_offline(); 99 debug(LOG_DEBUG, "Level %d: Failed to resolve auth server and all popular servers. "100 "The internet connection is probably down", level);101 return(-1);102 }103 }104 else {105 /* DNS解析成功 */106 ip = safe_strdup(inet_ntoa(*h_addr));107 debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] succeeded = [%s]", level, hostname, ip);108 109 if (!auth_server->last_ip || strcmp(auth_server->last_ip, ip) != 0) {110 /* DNS解析到的IP与我们上一次连接的IP不同,更新上一次连接的IP */111 debug(LOG_DEBUG, "Level %d: Updating last_ip IP of server [%s] to [%s]", level, hostname, ip);112 if (auth_server->last_ip) free(auth_server->last_ip);113 auth_server->last_ip = ip;114 115 /* 将此新的认证服务器IP添加到iptables中的可访问外网地址中 */116 fw_clear_authservers();117 fw_set_authservers();118 }119 else {120 /*121 * DNS解析到的IP与我们上一次连接的IP相同122 */123 free(ip);124 }125 126 /*127 * 连接128 */129 debug(LOG_DEBUG, "Level %d: Connecting to auth server %s:%d", level, hostname, auth_server->authserv_http_port);130 their_addr.sin_family = AF_INET;131 their_addr.sin_port = htons(auth_server->authserv_http_port);132 their_addr.sin_addr = *h_addr;133 memset(&(their_addr.sin_zero), '\0', sizeof(their_addr.sin_zero));134 free (h_addr);135 136 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {137 debug(LOG_ERR, "Level %d: Failed to create a new SOCK_STREAM socket: %s", strerror(errno));138 return(-1);139 }140 141 if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {142 /*143 * 连接失败144 * 将此认证服务器放入bad_server链表,并将config->auth_server指向认证服务器的下一个节点145 */146 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));147 close(sockfd);148 mark_auth_server_bad(auth_server);149 return _connect_auth_server(level); /* Yay recursion! */150 }151 else {152 /*153 * 连接成功154 */155 debug(LOG_DEBUG, "Level %d: Successfully connected to auth server %s:%d", level, hostname, auth_server->authserv_http_port);156 return sockfd;157 }158 }159 }
0 0
- wifidog源码分析 - 认证服务器心跳检测线程
- wifidog源码分析 - 客户端检测线程
- wifidog源码分析 - wifidog原理
- Wifidog分析wifidog认证网关协议v1
- wifidog 源码初分析
- wifidog 源码简要分析
- wifidog源码分析
- wifidog源码大概分析
- wifidog源码分析
- Hadoop-2.4.1源码分析--HDFS HeartBeat(心跳检测)之BPServiceActor工作线程运行流程(上)
- Hadoop-2.4.1源码分析--HDFS HeartBeat(心跳检测)之BPServiceActor工作线程运行流程(下)
- wifidog安装以及自写wifidog认证服务器
- 编写自己的WifiDog认证服务器
- wifidog源码分析 - 初始化阶段
- wifidog源码分析(一)
- wifidog 认证
- wifidog 认证
- Wifidog 认证
- wifidog源码分析 - 客户端检测线程
- Linux 安装Tomcat指定JDK
- linux2.6.32.71如何在lcd上显示内核启动信息
- Python Tornado 异步和非阻塞I/O
- 学习小组小记
- wifidog源码分析 - 认证服务器心跳检测线程
- selenium---sl4j日志输出
- 如何在安卓中把方形图变成圆形图
- 第十一周补充项目4-日期和时间类
- JavaScript事件委托的技术原理
- nyoj559 报数游戏
- kernel启动流程
- iframe跨域互相访问
- Asmack长链接框架使用