wifidog源码中的http.c文件,该文件是实现认证和页面跳转操作:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "httpd.h"
#include "safe.h"
#include "debug.h"
#include "conf.h"
#include "auth.h"
#include "firewall.h"
#include "http.h"
#include "client_list.h"
#include "common.h"
#include "centralserver.h"
#include "util.h"
#include "wd_util.h"
#include "../config.h"
/** The 404 handler is also responsible for redirecting to the auth server */
void //web页面404操作
http_callback_404(httpd * webserver, request * r, int error_code)
{
char tmp_url[MAX_BUF], *url, *mac;
s_config *config = config_get_config(); //获取配置文件中的数据
t_auth_serv *auth_server = get_auth_server(); //连接到服务器,
memset(tmp_url, 0, sizeof(tmp_url));
/*
* XXX Note the code below assumes that the client's request is a plain
* http request to a standard port. At any rate, this handler is called only
* if the internet/auth server is down so it's not a huge loss, but still.
*/
snprintf(tmp_url, (sizeof(tmp_url) - 1), "http://%s%s%s%s",
r->request.host, r->request.path, r->request.query[0] ? "?" : "", r->request.query);
url = httpdUrlEncode(tmp_url); //对url进程encode操作
if (!is_online()) { //连接不到服务器,服务器状态为down
/* The internet connection is down at the moment - apologize and do not redirect anywhere */
char *buf;
safe_asprintf(&buf,
"<p>We apologize, but it seems that the internet connection that powers this hotspot is temporarily unavailable.</p>"
"<p>If at all possible, please notify the owners of this hotspot that the internet connection is out of service.</p>"
"<p>The maintainers of this network are aware of this disruption. We hope that this situation will be resolved soon.</p>"
"<p>In a while please <a href='%s'>click here</a> to try your request again.</p>", tmp_url);
send_http_page(r, "Uh oh! Internet access unavailable!", buf); //发送状态页面及buf信息到客户端
free(buf);
debug(LOG_INFO, "Sent %s an apology since I am not online - no point sending them to auth server",
r->clientAddr);
} else if (!is_auth_online()) { //连接到服务器,但是服务器不提供重定向服务
/* The auth server is down at the moment - apologize and do not redirect anywhere */
char *buf;
safe_asprintf(&buf,
"<p>We apologize, but it seems that we are currently unable to re-direct you to the login screen.</p>"
"<p>The maintainers of this network are aware of this disruption. We hope that this situation will be resolved soon.</p>"
"<p>In a couple of minutes please <a href='%s'>click here</a> to try your request again.</p>",
tmp_url);
send_http_page(r, "Uh oh! Login screen unavailable!", buf);
free(buf);
debug(LOG_INFO, "Sent %s an apology since auth server not online - no point sending them to auth server",
r->clientAddr);
} else { //服务器端运行正常,提供重定向服务
/* Re-direct them to auth server */
char *urlFragment;
if (!(mac = arp_get(r->clientAddr))) { //获取客户端的mac地址
/* We could not get their MAC address */
debug(LOG_INFO, "Failed to retrieve MAC address for ip %s, so not putting in the login request",
r->clientAddr);
safe_asprintf(&urlFragment, "%sgw_address=%s&gw_port=%d&gw_id=%s&ip=%s&url=%s",
auth_server->authserv_login_script_path_fragment, config->gw_address, config->gw_port,
config->gw_id, r->clientAddr, url);
} else {
debug(LOG_INFO, "Got client MAC address for ip %s: %s", r->clientAddr, mac);
safe_asprintf(&urlFragment, "%sgw_address=%s&gw_port=%d&gw_id=%s&ip=%s&mac=%s&url=%s",
auth_server->authserv_login_script_path_fragment,
config->gw_address, config->gw_port, config->gw_id, r->clientAddr, mac, url);
free(mac);
}
// if host is not in whitelist, maybe not in conf or domain'IP changed, it will go to here. //如果客户端主机不再白名单中,则进行下面的操作
debug(LOG_INFO, "Check host %s is in whitelist or not", r->request.host); // e.g. www.example.com
t_firewall_rule *rule;
//e.g. example.com is in whitelist
// if request http://www.example.com/, it's not equal example.com.
for (rule = get_ruleset("global"); rule != NULL; rule = rule->next) {
debug(LOG_INFO, "rule mask %s", rule->mask);
if (strstr(r->request.host, rule->mask) == NULL) {
debug(LOG_INFO, "host %s is not in %s, continue", r->request.host, rule->mask);
continue;
} //分析客户端主机
int host_length = strlen(r->request.host);
int mask_length = strlen(rule->mask);
if (host_length != mask_length) {
char prefix[1024] = { 0 };
// must be *.example.com, if not have ".", maybe Phishing. e.g. phishingexample.com
strncpy(prefix, r->request.host, host_length - mask_length - 1); // e.g. www
strcat(prefix, "."); // www.
strcat(prefix, rule->mask); // www.example.com
if (strcasecmp(r->request.host, prefix) == 0) {
debug(LOG_INFO, "allow subdomain");
fw_allow_host(r->request.host);
http_send_redirect(r, tmp_url, "allow subdomain"); //执行重定向
free(url);
free(urlFragment);
return;
}
} else {
// e.g. "example.com" is in conf, so it had been parse to IP and added into "iptables allow" when wifidog start. but then its' A record(IP) changed, it will go to here.
debug(LOG_INFO, "allow domain again, because IP changed");
fw_allow_host(r->request.host);
http_send_redirect(r, tmp_url, "allow domain"); //执行重定向
free(url);
free(urlFragment);
return;
}
}
debug(LOG_INFO, "Captured %s requesting [%s] and re-directing them to login page", r->clientAddr, url);
http_send_redirect_to_auth(r, urlFragment, "Redirect to login page"); //向服务器端申请重定向的登录页面
free(urlFragment);
}
free(url);
}
void
http_callback_wifidog(httpd * webserver, request * r) // wifidog callback
{
send_http_page(r, "WiFiDog", "Please use the menu to navigate the features of this WiFiDog installation.");
}
void
http_callback_about(httpd * webserver, request * r) // wifidog about
{
send_http_page(r, "About WiFiDog", "This is WiFiDog version <strong>" VERSION "</strong>");
}
void
http_callback_status(httpd * webserver, request * r) //获取运行状态函数
{
const s_config *config = config_get_config();
char *status = NULL;
char *buf;
if (config->httpdusername && //验证用户,并决定是否授权
(strcmp(config->httpdusername, r->request.authUser) ||
strcmp(config->httpdpassword, r->request.authPassword))) {
debug(LOG_INFO, "Status page requested, forcing authentication");
httpdForceAuthenticate(r, config->httpdrealm);
return;
}
status = get_status_text(); //获取状态数据
safe_asprintf(&buf, "<pre>%s</pre>", status);
send_http_page(r, "WiFiDog Status", buf); //发送状态数据
free(buf);
free(status);
}
/** @brief Convenience function to redirect the web browser to the auth server
* @param r The request
* @param urlFragment The end of the auth server URL to redirect to (the part after path)
* @param text The text to include in the redirect header ant the mnual redirect title */
void //重定向web browser到服务器函数
http_send_redirect_to_auth(request * r, const char *urlFragment, const char *text)
{
char *protocol = NULL;
int port = 80;
t_auth_serv *auth_server = get_auth_server();
if (auth_server->authserv_use_ssl) {
protocol = "https";
port = auth_server->authserv_ssl_port;
} else {
protocol = "http";
port = auth_server->authserv_http_port;
}
char *url = NULL;
safe_asprintf(&url, "%s://%s:%d%s%s",
protocol, auth_server->authserv_hostname, port, auth_server->authserv_path, urlFragment);
http_send_redirect(r, url, text);
free(url);
}
/** @brief Sends a redirect to the web browser
* @param r The request
* @param url The url to redirect to
* @param text The text to include in the redirect header and the manual redirect link title. NULL is acceptable */
void //发送重定向信息到客户端
http_send_redirect(request * r, const char *url, const char *text)
{
char *message = NULL;
char *header = NULL;
char *response = NULL;
/* Re-direct them to auth server */
debug(LOG_DEBUG, "Redirecting client browser to %s", url);
safe_asprintf(&header, "Location: %s", url);
safe_asprintf(&response, "302 %s\n", text ? text : "Redirecting");
httpdSetResponse(r, response); //设置http请求的响应
httpdAddHeader(r, header); //添加http请求头
free(response);
free(header);
safe_asprintf(&message, "Please <a href='%s'>click here</a>.", url);
send_http_page(r, text ? text : "Redirection to message", message);
free(message);
}
void
http_callback_auth(httpd * webserver, request * r) //登录授权函数
{
t_client *client;
httpVar *token;
char *mac;
httpVar *logout = httpdGetVariableByName(r, "logout");
if ((token = httpdGetVariableByName(r, "token"))) {
/* They supplied variable "token" */
if (!(mac = arp_get(r->clientAddr))) {
/* We could not get their MAC address */
debug(LOG_ERR, "Failed to retrieve MAC address for ip %s", r->clientAddr);
send_http_page(r, "WiFiDog Error", "Failed to retrieve your MAC address");
} else {
/* We have their MAC address */ //mac和token对比,并增加到客户列表
LOCK_CLIENT_LIST();
if ((client = client_list_find(r->clientAddr, mac)) == NULL) {
debug(LOG_DEBUG, "New client for %s", r->clientAddr);
client_list_add(r->clientAddr, mac, token->value);
} else if (logout) {
logout_client(client);
} else {
debug(LOG_DEBUG, "Client for %s is already in the client list", client->ip);
}
UNLOCK_CLIENT_LIST();
if (!logout) { /* applies for case 1 and 3 from above if */
authenticate_client(r);
}
free(mac);
}
} else {
/* They did not supply variable "token" */
send_http_page(r, "WiFiDog error", "Invalid token");
}
}
void
http_callback_disconnect(httpd * webserver, request * r) //断开服务连接
{
const s_config *config = config_get_config();
/* XXX How do you change the status code for the response?? */
httpVar *token = httpdGetVariableByName(r, "token");
httpVar *mac = httpdGetVariableByName(r, "mac");
if (config->httpdusername &&
(strcmp(config->httpdusername, r->request.authUser) ||
strcmp(config->httpdpassword, r->request.authPassword))) {
debug(LOG_INFO, "Disconnect requested, forcing authentication");
httpdForceAuthenticate(r, config->httpdrealm);
return;
}
if (token && mac) {
t_client *client;
LOCK_CLIENT_LIST();
client = client_list_find_by_mac(mac->value);
if (!client || strcmp(client->token, token->value)) {
UNLOCK_CLIENT_LIST();
debug(LOG_INFO, "Disconnect %s with incorrect token %s", mac->value, token->value);
httpdOutput(r, "Invalid token for MAC");
return;
} //验证用户,查找用户列表,除名
/* TODO: get current firewall counters */
logout_client(client);
UNLOCK_CLIENT_LIST();
} else {
debug(LOG_INFO, "Disconnect called without both token and MAC given");
httpdOutput(r, "Both the token and MAC need to be specified");
return;
}
return;
}
void //对http信息反馈message信息
send_http_page(request * r, const char *title, const char *message)
{
s_config *config = config_get_config();
char *buffer;
struct stat stat_info;
int fd;
ssize_t written;
fd = open(config->htmlmsgfile, O_RDONLY);
if (fd == -1) {
debug(LOG_CRIT, "Failed to open HTML message file %s: %s", config->htmlmsgfile, strerror(errno));
return;
}
if (fstat(fd, &stat_info) == -1) {
debug(LOG_CRIT, "Failed to stat HTML message file: %s", strerror(errno));
close(fd);
return;
}
// Cast from long to unsigned int
buffer = (char *)safe_malloc((size_t) stat_info.st_size + 1);
written = read(fd, buffer, (size_t) stat_info.st_size);
if (written == -1) {
debug(LOG_CRIT, "Failed to read HTML message file: %s", strerror(errno));
free(buffer);
close(fd);
return;
}
close(fd);
buffer[written] = 0;
httpdAddVariable(r, "title", title);
httpdAddVariable(r, "message", message);
httpdAddVariable(r, "nodeID", config->gw_id);
httpdOutput(r, buffer); //输出buffer中的web信息到客户端
free(buffer);
}