zoukankan      html  css  js  c++  java
  • wifidog源码分析

    Wifidog是一个linux下开源的认证网关软件,它主要用于配合认证服务器实现无线路由器的认证放行功能。

    wifidog是一个后台的服务程序,可以通过wdctrl命令对wifidog主程序进行控制。

    本文解释wifidog在启动阶段所做的初始化主要工作(代码片段1.1

    • 初始化配置(先将配置结构体初始化为默认值,在读取配置文件修改配置结构体)
    • 初始化已连接客户端列表(如果是通过wdctrl重启wifidog,将会读取之前wifidog的已连接客户端列表 代码片段1.2 代码片段1.3
    • 如无特殊情况,分离进程,建立守护进程 (代码片段1.1
    • 添加多个http请求回调函数(包括404错误回调函数) (见之后章节)
    • 摧毁删除现有的iptables路由表规则 (见之后章节)
    • 建立新的iptables路由表规则 (见之后章节)
    • 启动多个功能线程 (见之后章节)
    • 循环等待客户端连接 (见之后章节)

    代码片段1.1

     1 int main(int argc, char **argv) {
     2 
     3     s_config *config = config_get_config();  //就是返回全局变量config结构体的地址
     4     config_init();    //初始化全局变量config结构体为默认值
     5 
     6     parse_commandline(argc, argv);    //根据传入参数执行操作(如果参数有-x则会设置restart_orig_pid为已运行的wifidog的pid)
     7 
     8     /* Initialize the config */
     9     config_read(config->configfile);    //根据配置文件设置全局变量config结构体
    10     config_validate();    //判断GatewayInterface和AuthServer是否为空,空则无效退出程序。
    11 
    12     /* Initializes the linked list of connected clients */
    13     client_list_init();    //将已连接客户端链表置空。
    14 
    15     /* Init the signals to catch chld/quit/etc */
    16     init_signals();    //初始化一些信号
    17 
    18     if (restart_orig_pid) {    //用于restart,如果有已运行的wifidog,先会kill它
    19         /*
    20          * We were restarted and our parent is waiting for us to talk to it over the socket
    21          */
    22         get_clients_from_parent();    //从已运行的wifidog中获取客户端列表,详见 代码片段1.2
    23 
    24         /*
    25          * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue
    26          */
    27 
    28         while (kill(restart_orig_pid, 0) != -1) {    //kill已运行的wifidog
    29             debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
    30             sleep(1);
    31         }
    32 
    33         debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");
    34     }
    35 
    36     if (config->daemon) {    //创建为守护进程,config->daemon默认值为-1
    37 
    38         debug(LOG_INFO, "Forking into background");
    39 
    40         switch(safe_fork()) {
    41             case 0: /* child */
    42                 setsid();    //创建新会话,脱离此终端,实现守护进程
    43                 append_x_restartargv();
    44                 main_loop();    //进入主循环(核心代码在此)。
    45                 break;
    46 
    47             default: /* parent */
    48                 exit(0);
    49                 break;
    50         }
    51     }
    52     else {
    53         append_x_restartargv();
    54         main_loop();
    55     }
    56 
    57     return(0); /* never reached */
    58 }

    代码片段1.2(获取已启动的wifidog的客户端列表):

    此段代表描述了新启动的wifidog如何从已启动的wifidog程序中获取已连接的客户端列表。发送端见 代码片段1.3

      1 void get_clients_from_parent(void) {
      2     int sock;
      3     struct sockaddr_un    sa_un;
      4     s_config * config = NULL;
      5     char linebuffer[MAX_BUF];
      6     int len = 0;
      7     char *running1 = NULL;
      8     char *running2 = NULL;
      9     char *token1 = NULL;
     10     char *token2 = NULL;
     11     char onechar;
     12     char *command = NULL;
     13     char *key = NULL;
     14     char *value = NULL;
     15     t_client * client = NULL;
     16     t_client * lastclient = NULL;
     17 
     18     config = config_get_config();
     19     
     20     debug(LOG_INFO, "Connecting to parent to download clients");
     21 
     22     /* 连接socket */
     23     sock = socket(AF_UNIX, SOCK_STREAM, 0);
     24     memset(&sa_un, 0, sizeof(sa_un));
     25     sa_un.sun_family = AF_UNIX;
     26     strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1));    //config->internal_sock的值为"/tmp/wifidog.sock"
     27 
     28     /* 连接已启动的wifidog */
     29     if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) {
     30         debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno));
     31         return;
     32     }
     33 
     34     debug(LOG_INFO, "Connected to parent.  Downloading clients");
     35 
     36     LOCK_CLIENT_LIST();
     37 
     38     command = NULL;
     39     memset(linebuffer, 0, sizeof(linebuffer));
     40     len = 0;
     41     client = NULL;
     42     /* 接收数据,逐个字符接收 */
     43     /* 数据包格式为 CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu
     */
     44     while (read(sock, &onechar, 1) == 1) {
     45         if (onechar == '
    ') {
     46             /* 如果接收到末尾('
    '),则转为'' */
     47             onechar = '';
     48         }
     49         linebuffer[len++] = onechar;
     50         
     51         if (!onechar) {
     52             /* 以下将数据转化为t_client结构体添加到客户端列表 */
     53             debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer);
     54             running1 = linebuffer;
     55             while ((token1 = strsep(&running1, "|")) != NULL) {
     56                 if (!command) {
     57                     /* The first token is the command */
     58                     command = token1;
     59                 }
     60                 else {
     61                 /* Token1 has something like "foo=bar" */
     62                     running2 = token1;
     63                     key = value = NULL;
     64                     while ((token2 = strsep(&running2, "=")) != NULL) {
     65                         if (!key) {
     66                             key = token2;
     67                         }
     68                         else if (!value) {
     69                             value = token2;
     70                         }
     71                     }
     72                 }
     73 
     74                 if (strcmp(command, "CLIENT") == 0) {
     75                     /* This line has info about a client in the client list */
     76                     if (!client) {
     77                         /* Create a new client struct */
     78                         client = safe_malloc(sizeof(t_client));
     79                         memset(client, 0, sizeof(t_client));
     80                     }
     81                 }
     82 
     83                 if (key && value) {
     84                     if (strcmp(command, "CLIENT") == 0) {
     85                         /* Assign the key into the appropriate slot in the connection structure */
     86                         if (strcmp(key, "ip") == 0) {
     87                             client->ip = safe_strdup(value);
     88                         }
     89                         else if (strcmp(key, "mac") == 0) {
     90                             client->mac = safe_strdup(value);
     91                         }
     92                         else if (strcmp(key, "token") == 0) {
     93                             client->token = safe_strdup(value);
     94                         }
     95                         else if (strcmp(key, "fw_connection_state") == 0) {
     96                             client->fw_connection_state = atoi(value);
     97                         }
     98                         else if (strcmp(key, "fd") == 0) {
     99                             client->fd = atoi(value);
    100                         }
    101                         else if (strcmp(key, "counters_incoming") == 0) {
    102                             client->counters.incoming_history = atoll(value);
    103                             client->counters.incoming = client->counters.incoming_history;
    104                         }
    105                         else if (strcmp(key, "counters_outgoing") == 0) {
    106                             client->counters.outgoing_history = atoll(value);
    107                             client->counters.outgoing = client->counters.outgoing_history;
    108                         }
    109                         else if (strcmp(key, "counters_last_updated") == 0) {
    110                             client->counters.last_updated = atol(value);
    111                         }
    112                         else {
    113                             debug(LOG_NOTICE, "I don't know how to inherit key [%s] value [%s] from parent", key, value);
    114                         }
    115                     }
    116                 }
    117             }
    118 
    119             /* End of parsing this command */
    120             if (client) {
    121                 /* Add this client to the client list */
    122                 if (!firstclient) {
    123                     firstclient = client;
    124                     lastclient = firstclient;
    125                 }
    126                 else {
    127                     lastclient->next = client;
    128                     lastclient = client;
    129                 }
    130             }
    131 
    132             /* Clean up */
    133             command = NULL;
    134             memset(linebuffer, 0, sizeof(linebuffer));
    135             len = 0;
    136             client = NULL;
    137         }
    138     }
    139 
    140     UNLOCK_CLIENT_LIST();
    141     debug(LOG_INFO, "Client list downloaded successfully from parent");
    142 
    143     close(sock);
    144 }

    代码片段1.3(已启动的wifidog发送客户端列表到新启动的wifidog):

      1 //thread_wdctl_handler(void *arg)函数是wifidog启动后自动创建的控制线程,主要用于与wdctrl进行socket通信,根据wdctrl命令执行不同的操作。这里我们着重讲解的是wdctrl发送restart后wifidog的执行逻辑。
      2 static void *
      3 thread_wdctl_handler(void *arg)
      4 {
      5     int    fd,
      6         done,
      7         i;
      8     char    request[MAX_BUF];
      9     ssize_t    read_bytes,
     10         len;
     11 
     12     debug(LOG_DEBUG, "Entering thread_wdctl_handler....");
     13 
     14     fd = (int)arg;
     15     
     16     debug(LOG_DEBUG, "Read bytes and stuff from %d", fd);
     17 
     18     /* 初始化变量 */
     19     read_bytes = 0;
     20     done = 0;
     21     memset(request, 0, sizeof(request));
     22     
     23     /* 读取命令 */
     24     while (!done && read_bytes < (sizeof(request) - 1)) {
     25         len = read(fd, request + read_bytes,
     26                 sizeof(request) - read_bytes);    //读取wdctrl发送的命令
     27 
     28         /* 判断命令正确性 */
     29         for (i = read_bytes; i < (read_bytes + len); i++) {
     30             if (request[i] == '
    ' || request[i] == '
    ') {
     31                 request[i] = '';
     32                 done = 1;
     33             }
     34         }
     35         
     36         /* Increment position */
     37         read_bytes += len;
     38     }
     39 
     40         //判断命令
     41     if (strncmp(request, "status", 6) == 0) {
     42         wdctl_status(fd);
     43     } else if (strncmp(request, "stop", 4) == 0) {
     44         wdctl_stop(fd);
     45     } else if (strncmp(request, "reset", 5) == 0) {
     46         wdctl_reset(fd, (request + 6));
     47     } else if (strncmp(request, "restart", 7) == 0) {
     48         wdctl_restart(fd);    //执行wdctl_restart(int afd)函数
     49     }
     50 
     51     if (!done) {
     52         debug(LOG_ERR, "Invalid wdctl request.");
     53                 //关闭套接字
     54         shutdown(fd, 2);
     55         close(fd);
     56         pthread_exit(NULL);
     57     }
     58 
     59     debug(LOG_DEBUG, "Request received: [%s]", request);
     60     
     61         //关闭套接字
     62     shutdown(fd, 2);
     63     close(fd);
     64     debug(LOG_DEBUG, "Exiting thread_wdctl_handler....");
     65 
     66     return NULL;
     67 }
     68 
     69 
     70 //wdctl_restart(int afd)函数详解
     71 static void
     72 wdctl_restart(int afd)
     73 {
     74     int    sock,
     75         fd;
     76     char    *sock_name;
     77     struct     sockaddr_un    sa_un;
     78     s_config * conf = NULL;
     79     t_client * client = NULL;
     80     char * tempstring = NULL;
     81     pid_t pid;
     82     ssize_t written;
     83     socklen_t len;
     84 
     85     conf = config_get_config();
     86 
     87     debug(LOG_NOTICE, "Will restart myself");
     88 
     89     /*
     90      * 准备内部连接socket
     91      */
     92     memset(&sa_un, 0, sizeof(sa_un));
     93     sock_name = conf->internal_sock;    //conf->internal_sock值为"/tmp/wifidog.sock"
     94     debug(LOG_DEBUG, "Socket name: %s", sock_name);
     95 
     96     if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
     97        
     98         debug(LOG_ERR, "INTERNAL socket name too long");
     99         return;
    100     }
    101 
    102     debug(LOG_DEBUG, "Creating socket");
    103     sock = socket(PF_UNIX, SOCK_STREAM, 0);    //建立内部socket套接字
    104 
    105     debug(LOG_DEBUG, "Got internal socket %d", sock);
    106 
    107     /* 如果sock_name文件存在,则删除*/
    108     unlink(sock_name);
    109 
    110     debug(LOG_DEBUG, "Filling sockaddr_un");
    111     strcpy(sa_un.sun_path, sock_name); 
    112     sa_un.sun_family = AF_UNIX;
    113     
    114     debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name));
    115     
    116    
    117     if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) {
    118         debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno));
    119         return;
    120     }
    121 
    122     if (listen(sock, 5)) {
    123         debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno));
    124         return;
    125     }
    126     
    127     /*
    128      * socket建立完成,创建子进程
    129      */
    130     debug(LOG_DEBUG, "Forking in preparation for exec()...");
    131     pid = safe_fork();
    132     if (pid > 0) {
    133         /* 父进程 */
    134 
    135         /* 等待子进程连接此socket :*/
    136         debug(LOG_DEBUG, "Waiting for child to connect on internal socket");
    137         len = sizeof(sa_un);
    138         if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){    //接受连接
    139             debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno));
    140             close(sock);
    141             return;
    142         }
    143 
    144         close(sock);
    145 
    146         debug(LOG_DEBUG, "Received connection from child.  Sending them all existing clients");
    147 
    148         /*子进程已经完成连接,发送客户端列表 */
    149         LOCK_CLIENT_LIST();
    150         client = client_get_first_client();    //获取第一个客户端
    151         while (client) {
    152             /* Send this client */
    153             safe_asprintf(&tempstring, "CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu
    ", client->ip, client->mac, client->token, client->fw_connection_state, client->fd, client->counters.incoming, client->counters.outgoing, client->counters.last_updated);
    154             debug(LOG_DEBUG, "Sending to child client data: %s", tempstring);
    155             len = 0;
    156             while (len != strlen(tempstring)) {
    157                 written = write(fd, (tempstring + len), strlen(tempstring) - len);    //发送给子进程
    158                 if (written == -1) {
    159                     debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno));
    160                     free(tempstring);
    161                     break;
    162                 }
    163                 else {
    164                     len += written;
    165                 }
    166             }
    167             free(tempstring);
    168             client = client->next;
    169         }
    170         UNLOCK_CLIENT_LIST();
    171 
    172         close(fd);
    173 
    174         debug(LOG_INFO, "Sent all existing clients to child.  Committing suicide!");
    175 
    176         shutdown(afd, 2);
    177         close(afd);
    178 
    179         
    180         wdctl_stop(afd);
    181     }
    182     else {
    183         /* 子进程,先关闭资源 */
    184         close(wdctl_socket_server);
    185         close(icmp_fd);
    186         close(sock);
    187         shutdown(afd, 2);
    188         close(afd);
    189         debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]);
    190 
    191         setsid();
    192         execvp(restartargv[0], restartargv);    //执行外部命令,这里重新启动wifidog
    193         
    194         debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));
    195         debug(LOG_ERR, "Exiting without cleanup");
    196         exit(1);
    197     }
    198 }

    小结

      客户端列表只有在restart命令中才会执行,实际上流程就是

    • 父wifidog准备socket
    • 父wifidog启动子wifidog
    • 子wifidog连接父wifidog
    • 客户端列表传递
    • 子wifidog终止父wifidog
  • 相关阅读:
    jsp完成页面自动刷新
    ssm整合案例
    MySql 里的IFNULL、NULLIF和ISNULL用法
    数据库优化
    数据库读写并发控制
    solr入门
    AngularJS入门(一)
    jsp和html的的区别
    Docker 网络(十一)
    Docker 容器资源隔离 namespace(十)
  • 原文地址:https://www.cnblogs.com/tolimit/p/4221719.html
Copyright © 2011-2022 走看看