wifidog源码分析 - 认证服务器心跳检测线程

引言

  但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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值