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

2120阅读 0评论2015-03-22 Garfield_Trump
分类:嵌入式

引言

  但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.         pthread_mutex_lock(&cond_mutex);
  17.         
  18.         pthread_cond_timedwait(&cond, &cond_mutex, &timeout);

  19.         pthread_mutex_unlock(&cond_mutex);
  20.     }

代码片段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.      */proc文件系统获取路由器信息
  27.      */
  28.     if ((fh = fopen("/proc/uptime", "r"))) {
  29.         fscanf(fh, "%lu", &sys_uptime);
  30.         fclose(fh);
  31.     }
  32.     if ((fh = fopen("/proc/meminfo", "r"))) {
  33.         while (!feof(fh)) {
  34.             if (fscanf(fh, "MemFree: %u", &sys_memfree) == 0) {
  35.                 while (!feof(fh) && fgetc(fh) != '\n');
  36.             }
  37.             else {
  38.                 break;
  39.             }
  40.         }
  41.         fclose(fh);
  42.     }
  43.     if ((fh = fopen("/proc/loadavg", "r"))) {
  44.         fscanf(fh, "%f", &sys_load);
  45.         fclose(fh);
  46.     }

  47.     /*
  48.      * 准备http请求包
  49.      */
  50.     snprintf(request, sizeof(request) - 1,
  51.             "GET %s%sgw_id=%s&sys_uptime=%lu&sys_memfree=%u&sys_load=%.2f&wifidog_uptime=%lu HTTP/1.0\r\n"
  52.             "User-Agent: WiFiDog %s\r\n"
  53.             "Host: %s\r\n"
  54.             "\r\n",
  55.             auth_server->authserv_path,
  56.             auth_server->authserv_ping_script_path_fragment,
  57.             config_get_config()->gw_id,
  58.             sys_uptime,
  59.             sys_memfree,
  60.             sys_load,
  61.             (long unsigned int)((long unsigned int)time(NULL) - (long unsigned int)started_time),
  62.             VERSION,
  63.             auth_server->authserv_hostname);

  64.     debug(LOG_DEBUG, "HTTP Request to Server: [%s]", request);
  65.     /* 发送 */
  66.     send(sockfd, request, strlen(request), 0);

  67.     debug(LOG_DEBUG, "Reading response");
  68.     
  69.     numbytes = totalbytes = 0;
  70.     done = 0;
  71.     do {
  72.         FD_ZERO(&readfds);
  73.         FD_SET(sockfd, &readfds);
  74.         /* 设置超时30s */
  75.         timeout.tv_sec = 30;
  76.         timeout.tv_usec = 0;
  77.         nfds = sockfd + 1;

  78.         nfds = select(nfds, &readfds, NULL, NULL, &timeout);

  79.         if (nfds > 0) {
  80.             /* 多路复用 */
  81.             numbytes = read(sockfd, request + totalbytes, MAX_BUF - (totalbytes + 1));
  82.             if (numbytes < 0) {
  83.                 debug(LOG_ERR, "An error occurred while reading from auth server: %s", strerror(errno));
  84.                 close(sockfd);
  85.                 return;
  86.             }
  87.             else if (numbytes == 0) {
  88.                 done = 1;
  89.             }
  90.             else {
  91.                 totalbytes += numbytes;
  92.                 debug(LOG_DEBUG, "Read %d bytes, total now %d", numbytes, totalbytes);
  93.             }
  94.         }
  95.         else if (nfds == 0) {
  96.             debug(LOG_ERR, "Timed out reading data via select() from auth server");
  97.             close(sockfd);
  98.             return;
  99.         }
  100.         else if (nfds < 0) {
  101.             debug(LOG_ERR, "Error reading data via select() from auth server: %s", strerror(errno));
  102.             close(sockfd);
  103.             return;
  104.         }
  105.     } while (!done);
  106.     close(sockfd);

  107.     debug(LOG_DEBUG, "Done reading reply, total %d bytes", totalbytes);

  108.     request[totalbytes] = '\0';

  109.     debug(LOG_DEBUG, "HTTP Response from Server: [%s]", request);
  110.     /* 判断认证服务器返回包中有没有"Pong"字符串 */
  111.     if (strstr(request, "Pong") == 0) {
  112.         debug(LOG_WARNING, "Auth server did NOT say pong!");
  113.        
  114.     }
  115.     else {
  116.         debug(LOG_DEBUG, "Auth Server Says: Pong");
  117.     }

  118.     return;
  119. }

代码片段1.3

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


  1. int connect_auth_server() {
  2.     int sockfd;

  3.     LOCK_CONFIG();
  4.     /* 连接认证服务器 */
  5.     sockfd = _connect_auth_server(0);
  6.     UNLOCK_CONFIG();

  7.     if (sockfd == -1) {
  8.         debug(LOG_ERR, "Failed to connect to any of the auth servers");
  9.         /* 标记认证服务器离线 */
  10.         mark_auth_offline();
  11.     }
  12.     else {
  13.         debug(LOG_DEBUG, "Connected to auth server");
  14.         /* 标记认证服务器在线 */
  15.         mark_auth_online();
  16.     }
  17.     return (sockfd);
  18. }



  19. int _connect_auth_server(int level) {
  20.     s_config *config = config_get_config();
  21.     t_auth_serv *auth_server = NULL;
  22.     struct in_addr *h_addr;
  23.     int num_servers = 0;
  24.     char * hostname = NULL;
  25.     /* 公共网站,用于判断DNS问题 */
  26.     char * popular_servers[] = {
  27.           "",
  28.           "",
  29.           NULL
  30.     };
  31.     char ** popularserver;
  32.     char * ip;
  33.     struct sockaddr_in their_addr;
  34.     int sockfd;

  35.     /* 用于递归,因为可能会有多个认证服务器,如果第一个认证服务器无法连接,会递归尝试连接后面的认证服务器,此参数用于递归判断的,当成功连接任意一个认证服务器后停止 */
  36.     level++;

  37.     /*
  38.      * 获取认证服务器数量
  39.      */
  40.     for (auth_server = config->auth_servers; auth_server; auth_server = auth_server->next) {
  41.         num_servers++;
  42.     }
  43.     debug(LOG_DEBUG, "Level %d: Calculated %d auth servers in list", level, num_servers);
  44.         /* 已经尝试递归连接所有认证服务器,都不能连接 */
  45.     if (level > num_servers) {
  46.         return (-1);
  47.     }

  48.     /*
  49.      * 获取认证服务器列表中的第一个认证服务器
  50.      */
  51.     auth_server = config->auth_servers;
  52.     hostname = auth_server->authserv_hostname;
  53.     debug(LOG_DEBUG, "Level %d: Resolving auth server [%s]", level, hostname);
  54.     h_addr = wd_gethostbyname(hostname);
  55.     if (!h_addr) {
  56.         /*
  57.          * DNS解析错误,尝试解析公共网站判断是否为DNS错误
  58.          */
  59.         debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] failed", level, hostname);

  60.         for (popularserver = popular_servers; *popularserver; popularserver++) {
  61.             debug(LOG_DEBUG, "Level %d: Resolving popular server [%s]", level, *popularserver);
  62.             h_addr = wd_gethostbyname(*popularserver);
  63.             /* 公共网站DNS解析正确 */
  64.             if (h_addr) {
  65.                 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] succeeded = [%s]", level, *popularserver, inet_ntoa(*h_addr));
  66.                 break;
  67.             }
  68.             else {
  69.                 debug(LOG_DEBUG, "Level %d: Resolving popular server [%s] failed", level, *popularserver);
  70.             }
  71.         }

  72.         if (h_addr) {
  73.             /* DNS正确,尝试递归下一个认证服务器 */
  74.             free (h_addr);

  75.             debug(LOG_DEBUG, "Level %d: Marking auth server [%s] as bad and trying next if possible", level, hostname);
  76.             if (auth_server->last_ip) {
  77.                 free(auth_server->last_ip);
  78.                 auth_server->last_ip = NULL;
  79.             }
  80.             /* 将此认证服务器放入bad_server链表,并将config->auth_server指向认证服务器的下一个节点 */
  81.             mark_auth_server_bad(auth_server);
  82.             /* 递归 */
  83.             return _connect_auth_server(level);
  84.         }
  85.         else {
  86.             /* DNS问题,标记路由器离线 */
  87.             mark_offline();
  88.             debug(LOG_DEBUG, "Level %d: Failed to resolve auth server and all popular servers. "
  89.                     "The internet connection is probably down", level);
  90.             return(-1);
  91.         }
  92.     }
  93.     else {
  94.         /* DNS解析成功 */
  95.         ip = safe_strdup(inet_ntoa(*h_addr));
  96.         debug(LOG_DEBUG, "Level %d: Resolving auth server [%s] succeeded = [%s]", level, hostname, ip);

  97.         if (!auth_server->last_ip || strcmp(auth_server->last_ip, ip) != 0) {
  98.             /* DNS解析到的IP与我们上一次连接的IP不同,更新上一次连接的IP */
  99.             debug(LOG_DEBUG, "Level %d: Updating last_ip IP of server [%s] to [%s]", level, hostname, ip);
  100.             if (auth_server->last_ip) free(auth_server->last_ip);
  101.             auth_server->last_ip = ip;

  102.             /* 将此新的认证服务器IP添加到iptables中的可访问外网地址中 */
  103.             fw_clear_authservers();
  104.             fw_set_authservers();
  105.         }
  106.         else {
  107.             /*
  108.              * DNS解析到的IP与我们上一次连接的IP相同
  109.              */
  110.             free(ip);
  111.         }

  112.         /*
  113.          * 连接
  114.          */
  115.         debug(LOG_DEBUG, "Level %d: Connecting to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
  116.         their_addr.sin_family = AF_INET;
  117.         their_addr.sin_port = htons(auth_server->authserv_http_port);
  118.         their_addr.sin_addr = *h_addr;
  119.         memset(&(their_addr.sin_zero), '\0', sizeof(their_addr.sin_zero));
  120.         free (h_addr);

  121.         if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  122.             debug(LOG_ERR, "Level %d: Failed to create a new SOCK_STREAM socket: %s", strerror(errno));
  123.             return(-1);
  124.         }

  125.         if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
  126.             /*
  127.              * 连接失败
  128.              * 将此认证服务器放入bad_server链表,并将config->auth_server指向认证服务器的下一个节点
  129.              */
  130.             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));
  131.             close(sockfd);
  132.             mark_auth_server_bad(auth_server);
  133.             return _connect_auth_server(level); /* Yay */
  134.         }
  135.         else {
  136.             /*
  137.              * 连接成功
  138.              */
  139.             debug(LOG_DEBUG, "Level %d: Successfully connected to auth server %s:%d", level, hostname, auth_server->authserv_http_port);
  140.             return sockfd;
  141.         }
  142.     }
  143. }

上一篇:wifidog源码分析 - 客户端检测线程
下一篇:2个分析input event事件的开源工具evtest,getevent