码迷,mamicode.com
首页 > 其他好文 > 详细

Jump The Great Firewall【step15 支持DHCP】

时间:2015-05-21 12:25:03      阅读:150      评论:0      收藏:0      [点我收藏+]

标签:

一、修正错误

之前在测试过程中,经常性的出现Invalid msg错误,导致连接被重置。经过调查,发现原来是数据分片时最后一个分片的长度计算有误导致。下面我们来分析一下这个错误的代码:

int check_msg(client_t* client, msg_t* msg)
{
    size_t msg_data_len;
    if (msg->zone.clip)
    {
        if (msg->zone.last) msg_data_len = msg_data_length(msg) % client->max_length;
        else msg_data_len = client->max_length;
    }
    else msg_data_len = msg_data_length(msg);
    if (checksum(msg, sizeof(msg_t) + msg_data_len))
    {
        SYSLOG(LOG_ERR, "Invalid msg");
        return 0;
    }
    return 1;
}

由上面的代码可以看出,当到来的数据包为最后一个分片时,根据代码中的运算方法直接与对端的max_length取模,此时如果最后一个分片的长度正好能被max_length整除时。所计算出的长度就是错的,应此会平凡的遇到Invalid msg错误。与该错误类似的问题已全部修正,已修正的函数有:

network.c:
  process_clip_msg和check_msg函数
msg_group.c:
  parse_msg_group函数
server.c:
  tcp_process函数
client.c:
  client_process函数

二、支持DHCP

  1. 增加配置文件中的use_dhcp字段并增加lua接口
  2. 修改sys_login_msg_t结构
    typedef struct
    {
        unsigned short major_version;
        unsigned char  minor_version : 4;
        unsigned char  revision_version : 4;
        unsigned int   ip;
        unsigned int   gateway;
        unsigned char  mask;
        unsigned short internal_mtu;
        unsigned char  signature[31];
        unsigned char  dhcp;
    } sys_login_msg_t;
    
    将头3个字节修改为版本号的定义,在末尾增加31字节的signature和1字节的dhcp标志位,使用31字节作为signature的长度的意义在于与下一字节的dhcp标志位组合后使整个结构对齐到46字节。由于该校验工作只发生在连接建立后,因此出于安全和拓展因素考虑,在配置文件中只记录签名文件的路径,在每次进行校验时由lua脚本动态读出并做校验。这样做的好处是:(1) 安全,文件内容永远不记录在内存中 (2) 拓展性强,当服务器端添加新的客户端签名时无需重启服务器端程序。 需要注意的是:建议为每个单独的客户端配置不同的签名,这样做的好处是,每个客户端都独立持有各自的签名,更加安全。
  3. 修改server_process_login函数 1). 首先对客户端传来的版本号进行检查 2). 若不是DHCP模式则检查客户端传过来的内网IP是否在当前网段内 3). 检查客户端所传签名是否在当前签名文件中 4). 根据是否启用DHCP模式决定调用server_process_login_dhcp函数或server_process_login_no_dhcp函数,其中前者根据当前网段寻找一个合适的IP地址通知客户端,后者为原逻辑
  4. 修改connect_server函数 1). 检查服务器端传来的版本号与本地版本号是否相同 2). 检查服务器端传来的IP地址是否为0 3). 检查服务器端传来的signature是否与本地的相同 4). 检查服务器端传来的IP地址是否与本地的相同,若不同且服务器端为DHCP模式则使用服务器端给定的IP进行绑定 5). 进行其他初始化

三、更灵活的签名校验

为了兼容老版本,因此将sys_login_msg_t结构的头3字节用作版本号的定义。同时为了增强灵活性和安全性,在qtun程序中仅保存signature文件的路径,每次做校验时都动态的将其读出。

  1. 定义script_signature_verify和script_load_signature接口
    int script_signature_verify(lua_State* lua, const unsigned char signature[31]) {
        lua_getglobal(lua, "signature_verify");
        lua_pushlstring(lua, (char*)signature, 31);
        if (lua_pcall(lua, 1, 1, 0) != 0) {
            SYSLOG(LOG_ERR, "%s\n", lua_tostring(lua, -1));
            return 0;
        }
        return lua_toboolean(lua, -1);
    }
    
    int script_load_signature(lua_State* lua, unsigned char signature[31]) {
        const char* str;
        lua_getglobal(lua, "signature_load");
        if (lua_pcall(lua, 0, 1, 0) != 0) {
            SYSLOG(LOG_ERR, "%s\n", lua_tostring(lua, -1));
            return 0;
        }
        str = lua_tostring(lua, -1);
        memcpy(signature, str, 31);
        return 1;
    }
    
  2. 以上两个函数最终会调用qtun.lua中的signature_verify和signature_load接口
    function signature_load()
        local file = io.open(qtun.conf.signature_file)
        local ret = ‘‘
        assert(file)
        if qtun.state.is_server then
            ret = file:read(‘*a‘)
        else
            ret = file:read(31)
        end
        file:close()
        return ret
    end
    
    function signature_verify(signature)
        if qtun.state.is_server then
            local file = io.open(qtun.conf.signature_file)
            assert(file)
            while true do
                local line = file:read()
                if line == nil then break end
                if line == signature then return true end
            end
            file:close()
            return false
        else
            return signature == signature_load()
        end
    end
    
  3. 在connect_server函数中,创建login消息前先将签名读出来
    unsigned char signature[31] = {0};
    script_load_signature(qtun->lua, signature);
    msg = new_login_msg(qtun->localip, 0, 0, 1, 0, signature);
    
  4. 在server_process_login函数中,调用script_signature_verify检查客户端所传来的签名是否存在于签名文件中
    if (!script_signature_verify(qtun->lua, login->signature)) { // 签名检查失败
        msg_t* new_msg;
        unsigned char signature[sizeof(login->signature)];
        memset(signature, 0, sizeof(signature));
        SYSLOG(LOG_ERR, "unknown signature");
        pool_room_free(&qtun->pool, room_id);
        data = NULL;
        new_msg = new_login_msg(0, 0, 0, 0, 0, signature);
        if (new_msg) {
            write_c(client, new_msg, sizeof(msg_t) + msg_data_length(new_msg));
            pool_room_free(&qtun->pool, MSG_ROOM_IDX);
        }
        close_client(for_del, idx);
        goto end;
    }
    
  5. 在connect_server函数中,服务器返回数据时调用script_signature_verify接口进行校验
    if (!script_signature_verify(qtun->lua, login->signature)) {
        SYSLOG(LOG_ERR, "invalid signature");
        pool_room_free(&qtun->pool, room_id);
        goto end;
    }
    

四、完整代码

完整代码可到step15中查看

版本号:1.0.0

日期:2015-05-20

Jump The Great Firewall【step15 支持DHCP】

标签:

原文地址:http://www.cnblogs.com/lwch/p/4519258.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!