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 }

 

你可能感兴趣的:(源码分析)