wifidog源码分析 - 初始化阶段
Wifidog是一个linux下开源的认证网关软件,它主要用于配合认证服务器实现无线路由器的认证放行功能。
wifidog是一个后台的服务程序,可以通过wdctrl命令对wifidog主程序进行控制。
本文解释wifidog在启动阶段所做的初始化主要工作(代码片段1.1)
- 初始化配置(先将配置结构体初始化为默认值,在读取配置文件修改配置结构体)
- 初始化已连接客户端列表(如果是通过wdctrl重启wifidog,将会读取之前wifidog的已连接客户端列表 代码片段1.2 代码片段1.3)
- 如无特殊情况,分离进程,建立守护进程 (代码片段1.1)
- 添加多个http请求回调函数(包括404错误回调函数) (见之后章节)
- 摧毁删除现有的iptables路由表规则 (见之后章节)
- 建立新的iptables路由表规则 (见之后章节)
- 启动多个功能线程 (见之后章节)
- 循环等待客户端连接 (见之后章节)
代码片段1.1
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, ) != -) { //kill已运行的wifidog
debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid);
sleep();
} 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 : /* child */
setsid(); //创建新会话,脱离此终端,实现守护进程
append_x_restartargv();
main_loop(); //进入主循环(核心代码在此)。
break; default: /* parent */
exit();
break;
}
}
else {
append_x_restartargv();
main_loop();
} return(); /* never reached */
}
代码片段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 = ;
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, );
memset(&sa_un, , sizeof(sa_un));
sa_un.sun_family = AF_UNIX;
strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - )); //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, , sizeof(linebuffer));
len = ;
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\n */
while (read(sock, &onechar, ) == ) {
if (onechar == '\n') {
/* 如果接收到末尾('\n'),则转为'\0' */
onechar = '\0';
}
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") == ) {
/* 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, , sizeof(t_client));
}
} if (key && value) {
if (strcmp(command, "CLIENT") == ) {
/* Assign the key into the appropriate slot in the connection structure */
if (strcmp(key, "ip") == ) {
client->ip = safe_strdup(value);
}
else if (strcmp(key, "mac") == ) {
client->mac = safe_strdup(value);
}
else if (strcmp(key, "token") == ) {
client->token = safe_strdup(value);
}
else if (strcmp(key, "fw_connection_state") == ) {
client->fw_connection_state = atoi(value);
}
else if (strcmp(key, "fd") == ) {
client->fd = atoi(value);
}
else if (strcmp(key, "counters_incoming") == ) {
client->counters.incoming_history = atoll(value);
client->counters.incoming = client->counters.incoming_history;
}
else if (strcmp(key, "counters_outgoing") == ) {
client->counters.outgoing_history = atoll(value);
client->counters.outgoing = client->counters.outgoing_history;
}
else if (strcmp(key, "counters_last_updated") == ) {
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, , sizeof(linebuffer));
len = ;
client = NULL;
}
} UNLOCK_CLIENT_LIST();
debug(LOG_INFO, "Client list downloaded successfully from parent"); close(sock);
}
代码片段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 = ;
done = ;
memset(request, , sizeof(request)); /* 读取命令 */
while (!done && read_bytes < (sizeof(request) - )) {
len = read(fd, request + read_bytes,
sizeof(request) - read_bytes); //读取wdctrl发送的命令 /* 判断命令正确性 */
for (i = read_bytes; i < (read_bytes + len); i++) {
if (request[i] == '\r' || request[i] == '\n') {
request[i] = '\0';
done = ;
}
} /* Increment position */
read_bytes += len;
} //判断命令
if (strncmp(request, "status", ) == ) {
wdctl_status(fd);
} else if (strncmp(request, "stop", ) == ) {
wdctl_stop(fd);
} else if (strncmp(request, "reset", ) == ) {
wdctl_reset(fd, (request + ));
} else if (strncmp(request, "restart", ) == ) {
wdctl_restart(fd); //执行wdctl_restart(int afd)函数
} if (!done) {
debug(LOG_ERR, "Invalid wdctl request.");
//关闭套接字
shutdown(fd, );
close(fd);
pthread_exit(NULL);
} debug(LOG_DEBUG, "Request received: [%s]", request); //关闭套接字
shutdown(fd, );
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, , 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) - )) { debug(LOG_ERR, "INTERNAL socket name too long");
return;
} debug(LOG_DEBUG, "Creating socket");
sock = socket(PF_UNIX, SOCK_STREAM, ); //建立内部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, )) {
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 > ) {
/* 父进程 */ /* 等待子进程连接此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)) == -){ //接受连接
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\n", 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 = ;
while (len != strlen(tempstring)) {
written = write(fd, (tempstring + len), strlen(tempstring) - len); //发送给子进程
if (written == -) {
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, );
close(afd); wdctl_stop(afd);
}
else {
/* 子进程,先关闭资源 */
close(wdctl_socket_server);
close(icmp_fd);
close(sock);
shutdown(afd, );
close(afd);
debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[]); setsid();
execvp(restartargv[], restartargv); //执行外部命令,这里重新启动wifidog debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno));
debug(LOG_ERR, "Exiting without cleanup");
exit();
}
}
小结
客户端列表只有在restart命令中才会执行,实际上流程就是
- 父wifidog准备socket
- 父wifidog启动子wifidog
- 子wifidog连接父wifidog
- 客户端列表传递
- 子wifidog终止父wifidog
wifidog源码分析 - 初始化阶段的更多相关文章
- wifidog源码分析 - 用户连接过程
引言 之前的文章已经描述wifidog大概的一个工作流程,这里我们具体说说wifidog是怎么把一个新用户重定向到认证服务器中的,它又是怎么对一个已认证的用户实行放行操作的.我们已经知道wifidog ...
- wifidog源码分析 - wifidog原理 tiger
转:http://www.cnblogs.com/tolimit/p/4223644.html wifidog源码分析 - wifidog原理 wifidog是一个用于配合认证服务器实现无线网页认证功 ...
- linux调度器源码分析 - 初始化(二)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 上期文章linux调度器源码分析 - 概述(一)已经把调度器相关的数据结构介绍了一遍,本篇着重通过代码说明 ...
- Linux 内核调度器源码分析 - 初始化
导语 上篇系列文 混部之殇-论云原生资源隔离技术之CPU隔离(一) 介绍了云原生混部场景中CPU资源隔离核心技术:内核调度器,本系列文章<Linux内核调度器源码分析>将从源码的角度剖析内 ...
- junit源码解析--初始化阶段
OK,我们接着上篇整理.上篇博客中已经列出的junit的几个核心的类,这里我们开始整理junit完整的生命周期. JUnit 的完整生命周期分为 3 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...
- linux中断源码分析 - 初始化(二)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的 ...
- wifidog源码分析 - wifidog原理
wifidog是一个用于配合认证服务器实现无线网页认证功能的程序,常见的情景就是使用于公共场合的无线wifi接入点,首先移动设备会连接公共wifi接入点,之后会弹出网页要求输入用户名密码,认证过后才能 ...
- struts2源码分析-初始化流程
这一篇文章主要是记录struts.xml的初始化,还原struts2.xml的初始化流程.源码依据struts2-2.3.16.3版本. struts2初始化入口,位于web.xml中: <fi ...
- Shiro源码分析-初始化-Realm
在上一篇介绍SecurityManager的初始化过程中,也有realm的粗略介绍. realm的概念在安全领域随处可见: 各种中间件的realm.spring security的realm.shir ...
随机推荐
- Castle 集锦
Castle Windsor http://www.cnblogs.com/RicCC/archive/2010/03/30/castle-windsor-ioc-di.html 官方配置说明(反正我 ...
- 6.python字符串-内置方法列举
所谓内置方法,就是凡是字符串都能用的方法,这个方法在创建字符串的类中,下面是总结: 首先,我们要学习一个获取帮助的内置函数 help(对象) ,对象可以是一个我们创建出来的,也可以是创建对象的那个类, ...
- python 数据类型(sequence 序列、dictionary 词典、动态类型)
文章内容摘自:http://www.cnblogs.com/vamei 1.sequence 序列 sequence(序列)是一组有顺序的元素的集合 (严格的说,是对象的集合,但鉴于我们还没有引入“对 ...
- hdu 4631Sad Love Story<计算几何>
链接:http://acm.hdu.edu.cn/showproblem.php?pid=4631 题意:依次给你n个点,每次求出当前点中的最近点对,输出所有最近点对的和: 思路:按照x排序,然后用s ...
- Newtonsoft.Json版本冲突时参考解决方案
如果同一项目中不同第三方类库分别使用了不同版本的Newtonsoft.Json的情况下,可以在配置文件中添加以下节点,将0.0.0.0-9.0.0.0此区间的Newtonsoft.Json使用全部强制 ...
- Golang之ring.Ring的Link操作
ring.Ring 是一个环链. 其Link操作规则是: r.Link(s) => t = r.Next r.Next.Prev = s.Prev s.Prev.Next = r.Next r. ...
- Mysql海量数据存储和解决方案之一—分布式DB方案
1) 分布式DB水平切分中用到的主要关键技术:分库,分表,M-S,集群,负载均衡 2) 需求分析:一个大型互联网应用每天几十亿的PV对DB造成了相当高的负载,对系统的稳定性的扩展性带来极大挑战. 3 ...
- Socket与TcpClient的区别(转载)
Socket和TcpClient有什么区别 原文:http://wxwinter.spaces.live.com/blog/cns!C36588978AFC344A!322.entry 回答: &qu ...
- Python学习——struct模块的pack、unpack示例
he struct module includes functions for converting between strings of bytes and native Python data t ...
- iOSReachability判断网络连接状态
// // NetStateManage.h // // Created by miniu on 15/11/24. // Copyright © 2015年 mini. All rights ...