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