• wifidog源码


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

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

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

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

    int main(int argc, char **argv) {
    
        s_config *config = config_get_config(); //就是返回全局变量config结构体的地址
        config_init(); //初始化全局变量config结构体为默认值
    
        parse_commandline(argc, argv); //根据传入参数执行操作(如果参数有-x则会设置restart_orig_pid为已运行的wifidog的pid)
    
        /* Initialize the config */
        config_read(config->configfile); //根据配置文件设置全局变量config结构体
        config_validate(); //判断GatewayInterface和AuthServer是否为空,空则无效退出程序。
    
        /* Initializes the linked list of connected clients */
        client_list_init(); //将已连接客户端链表置空。
    
        /* Init the signals to catch chld/quit/etc */
        init_signals(); //初始化一些信号
    
        if (restart_orig_pid) { //用于restart,如果有已运行的wifidog,先会kill它
            /*
             * We were restarted and our parent is waiting for us to talk to it over the socket
             */
            get_clients_from_parent(); //从已运行的wifidog中获取客户端列表,详见 代码片段1.2
    
            /*
             * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue
             */
    
            while (kill(restart_orig_pid, 0) != -1) { //kill已运行的wifidog
                debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
                sleep(1);
            }
    
            debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading.");
        }
    
        if (config->daemon) { //创建为守护进程,config->daemon默认值为-1
    
            debug(LOG_INFO, "Forking into background");
    
            switch(safe_fork()) {
                case 0: /* child */
                    setsid(); //创建新会话,脱离此终端,实现守护进程
                    append_x_restartargv();
                    main_loop(); //进入主循环(核心代码在此)。
                    break;
    
                default: /* parent */
                    exit(0);
                    break;
            }
        }
        else {
            append_x_restartargv();
            main_loop();
        }
    
        return(0); /* never reached */
    }
    View Code

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

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

    void get_clients_from_parent(void) {
        int sock;
        struct sockaddr_un sa_un;
        s_config * config = NULL;
        char linebuffer[MAX_BUF];
        int len = 0;
        char *running1 = NULL;
        char *running2 = NULL;
        char *token1 = NULL;
        char *token2 = NULL;
        char onechar;
        char *command = NULL;
        char *key = NULL;
        char *value = NULL;
        t_client * client = NULL;
        t_client * lastclient = NULL;
    
        config = config_get_config();
    
        debug(LOG_INFO, "Connecting to parent to download clients");
    
        /* 连接socket */
        sock = socket(AF_UNIX, SOCK_STREAM, 0);
        memset(&sa_un, 0, sizeof(sa_un));
        sa_un.sun_family = AF_UNIX;
        strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1)); //config->internal_sock的值为"/tmp/wifidog.sock"
    
        /* 连接已启动的wifidog */
        if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) {
            debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno));
            return;
        }
    
        debug(LOG_INFO, "Connected to parent. Downloading clients");
    
        LOCK_CLIENT_LIST();
    
        command = NULL;
        memset(linebuffer, 0, sizeof(linebuffer));
        len = 0;
        client = NULL;
        /* 接收数据,逐个字符接收 */
        /* 数据包格式为 CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu
     */
        while (read(sock, &onechar, 1) == 1) {
            if (onechar == '
    ') {
                /* 如果接收到末尾('
    '),则转为'' */
                onechar = '';
            }
            linebuffer[len++] = onechar;
    
            if (!onechar) {
                /* 以下将数据转化为t_client结构体添加到客户端列表 */
                debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer);
                running1 = linebuffer;
                while ((token1 = strsep(&running1, "|")) != NULL) {
                    if (!command) {
                        /* The first token is the command */
                        command = token1;
                    }
                    else {
                    /* Token1 has something like "foo=bar" */
                        running2 = token1;
                        key = value = NULL;
                        while ((token2 = strsep(&running2, "=")) != NULL) {
                            if (!key) {
                                key = token2;
                            }
                            else if (!value) {
                                value = token2;
                            }
                        }
                    }
    
                    if (strcmp(command, "CLIENT") == 0) {
                        /* This line has info about a client in the client list */
                        if (!client) {
                            /* Create a new client struct */
                            client = safe_malloc(sizeof(t_client));
                            memset(client, 0, sizeof(t_client));
                        }
                    }
    
                    if (key && value) {
                        if (strcmp(command, "CLIENT") == 0) {
                            /* Assign the key into the appropriate slot in the connection structure */
                            if (strcmp(key, "ip") == 0) {
                                client->ip = safe_strdup(value);
                            }
                            else if (strcmp(key, "mac") == 0) {
                                client->mac = safe_strdup(value);
                            }
                            else if (strcmp(key, "token") == 0) {
                                client->token = safe_strdup(value);
                            }
                            else if (strcmp(key, "fw_connection_state") == 0) {
                                client->fw_connection_state = atoi(value);
                            }
                            else if (strcmp(key, "fd") == 0) {
                                client->fd = atoi(value);
                            }
                            else if (strcmp(key, "counters_incoming") == 0) {
                                client->counters.incoming_history = atoll(value);
                                client->counters.incoming = client->counters.incoming_history;
                            }
                            else if (strcmp(key, "counters_outgoing") == 0) {
                                client->counters.outgoing_history = atoll(value);
                                client->counters.outgoing = client->counters.outgoing_history;
                            }
                            else if (strcmp(key, "counters_last_updated") == 0) {
                                client->counters.last_updated = atol(value);
                            }
                            else {
                                debug(LOG_NOTICE, "I don't know how to inherit key [%s] value [%s] from parent", key, value);
                            }
                        }
                    }
                }
    
                /* End of parsing this command */
                if (client) {
                    /* Add this client to the client list */
                    if (!firstclient) {
                        firstclient = client;
                        lastclient = firstclient;
                    }
                    else {
                        lastclient->next = client;
                        lastclient = client;
                    }
                }
    
                /* Clean up */
                command = NULL;
                memset(linebuffer, 0, sizeof(linebuffer));
                len = 0;
                client = NULL;
            }
        }
    
        UNLOCK_CLIENT_LIST();
        debug(LOG_INFO, "Client list downloaded successfully from parent");
    
        close(sock);
    }
    View Code

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

    //thread_wdctl_handler(void *arg)函数是wifidog启动后自动创建的控制线程,主要用于与wdctrl进行socket通信,根据wdctrl命令执行不同的操作。这里我们着重讲解的是wdctrl发送restart后wifidog的执行逻辑。
    static void *
    thread_wdctl_handler(void *arg)
    {
        int fd,
            done,
            i;
        char request[MAX_BUF];
        ssize_t read_bytes,
            len;
    
        debug(LOG_DEBUG, "Entering thread_wdctl_handler....");
    
        fd = (int)arg;
    
        debug(LOG_DEBUG, "Read bytes and stuff from %d", fd);
    
        /* 初始化变量 */
        read_bytes = 0;
        done = 0;
        memset(request, 0, sizeof(request));
    
        /* 读取命令 */
        while (!done && read_bytes < (sizeof(request) - 1)) {
            len = read(fd, request + read_bytes,
                    sizeof(request) - read_bytes); //读取wdctrl发送的命令
    
            /* 判断命令正确性 */
            for (i = read_bytes; i < (read_bytes + len); i++) {
                if (request[i] == '
    ' || request[i] == '
    ') {
                    request[i] = '';
                    done = 1;
                }
            }
    
            /* Increment position */
            read_bytes += len;
        }
    
            //判断命令
        if (strncmp(request, "status", 6) == 0) {
            wdctl_status(fd);
        } else if (strncmp(request, "stop", 4) == 0) {
            wdctl_stop(fd);
        } else if (strncmp(request, "reset", 5) == 0) {
            wdctl_reset(fd, (request + 6));
        } else if (strncmp(request, "restart", 7) == 0) {
            wdctl_restart(fd); //执行wdctl_restart(int afd)函数
        }
    
        if (!done) {
            debug(LOG_ERR, "Invalid wdctl request.");
                    //关闭套接字
            shutdown(fd, 2);
            close(fd);
            pthread_exit(NULL);
        }
    
        debug(LOG_DEBUG, "Request received: [%s]", request);
    
            //关闭套接字
        shutdown(fd, 2);
        close(fd);
        debug(LOG_DEBUG, "Exiting thread_wdctl_handler....");
    
        return NULL;
    }
    
    
    //wdctl_restart(int afd)函数详解
    static void
    wdctl_restart(int afd)
    {
        int sock,
            fd;
        char *sock_name;
        struct sockaddr_un sa_un;
        s_config * conf = NULL;
        t_client * client = NULL;
        char * tempstring = NULL;
        pid_t pid;
        ssize_t written;
        socklen_t len;
    
        conf = config_get_config();
    
        debug(LOG_NOTICE, "Will restart myself");
    
        /*
         * 准备内部连接socket
         */
        memset(&sa_un, 0, sizeof(sa_un));
        sock_name = conf->internal_sock; //conf->internal_sock值为"/tmp/wifidog.sock"
        debug(LOG_DEBUG, "Socket name: %s", sock_name);
    
        if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) {
    
            debug(LOG_ERR, "INTERNAL socket name too long");
            return;
        }
    
        debug(LOG_DEBUG, "Creating socket");
        sock = socket(PF_UNIX, SOCK_STREAM, 0); //建立内部socket套接字
    
        debug(LOG_DEBUG, "Got internal socket %d", sock);
    
        /* 如果sock_name文件存在,则删除*/
        unlink(sock_name);
    
        debug(LOG_DEBUG, "Filling sockaddr_un");
        strcpy(sa_un.sun_path, sock_name); 
        sa_un.sun_family = AF_UNIX;
    
        debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name));
    
    
        if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) {
            debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno));
            return;
        }
    
        if (listen(sock, 5)) {
            debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno));
            return;
        }
    
        /*
         * socket建立完成,创建子进程
         */
        debug(LOG_DEBUG, "Forking in preparation for exec()...");
        pid = safe_fork();
        if (pid > 0) {
            /* 父进程 */
    
            /* 等待子进程连接此socket :*/
            debug(LOG_DEBUG, "Waiting for child to connect on internal socket");
            len = sizeof(sa_un);
            if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){ //接受连接
                debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno));
                close(sock);
                return;
            }
    
            close(sock);
    
            debug(LOG_DEBUG, "Received connection from child. Sending them all existing clients");
    
            /*子进程已经完成连接,发送客户端列表 */
            LOCK_CLIENT_LIST();
            client = client_get_first_client(); //获取第一个客户端
            while (client) {
                /* Send this client */
                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);
                debug(LOG_DEBUG, "Sending to child client data: %s", tempstring);
                len = 0;
                while (len != strlen(tempstring)) {
                    written = write(fd, (tempstring + len), strlen(tempstring) - len); //发送给子进程
                    if (written == -1) {
                        debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno));
                        free(tempstring);
                        break;
                    }
                    else {
                        len += written;
                    }
                }
                free(tempstring);
                client = client->next;
            }
            UNLOCK_CLIENT_LIST();
    
            close(fd);
    
            debug(LOG_INFO, "Sent all existing clients to child. Committing suicide!");
    
            shutdown(afd, 2);
            close(afd);
    
    
            wdctl_stop(afd);
        }
        else {
            /* 子进程,先关闭资源 */
            close(wdctl_socket_server);
            close(icmp_fd);
            close(sock);
            shutdown(afd, 2);
            close(afd);
            debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]);
    
            setsid();
            execvp(restartargv[0], restartargv); //执行外部命令,这里重新启动wifidog
    
            debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));
            debug(LOG_ERR, "Exiting without cleanup");
            exit(1);
        }
    }
    View Code

    本文章由http://www.wifidog.pro/2015/04/02/wifidog%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E5%88%9D%E5%A7%8B%E5%8C%96.html 整理编辑,转载请注明出处

  • 相关阅读:
    一个bug肝一周...忍不住提了issue
    git上传本地项目
    simsun.ttc is not a valid ttf file(linux OS/windows OS)
    mysql端口
    基于Docker的WebApi接口服务
    基于Docker的WebApi接口服务
    服务容器化Docker部署访问
    Docker部署vue项目
    C#限制字符串的长度
    python多进程
  • 原文地址:https://www.cnblogs.com/wifidog/p/4386445.html
Copyright © 2020-2023  润新知