分类 其他 下的文章

Nginx模块开发中使用PCRE正则表达式匹配

Nginx内部对pcre库的常用操作进行了封装. 封装的源码位于nginx/src/core/ngx_regex.c, 同时将pcre内使用的内存池更变为了Nginx的内存池.

  • pcre_compile:

    Nginx封装了pcre_compile方法. 方法名为ngx_regex_compile.

    ngx_regex_compile方法的参数需要传入一个ngx_regex_compile_t来进行编译正则等操作.

    ngx_regex_compile_t结构如下:

    typedef struct {
        ngx_str_t     pattern; // 正则
        ngx_pool_t    *pool;   // 每个request分配的内存池
        ngx_int_t     options; // pcre options
    
        ngx_regex_t   *regex;  // 编译完毕后的pcre实例
        int           captures;
        int           named_captures;
        int           name_size;
        u_char       *names;
        ngx_str_t     err;     // 错误信息
    } ngx_regex_compile_t;`
    

    当编译正则成功时会返回NGX_OK并且会在内部调用pcre_study来进一步提高正则匹配性能. 失败时会返回NGX_ERROR. 同时失败的错误信息会保存在err成员变量中.

  • pcre_exec

    Nginx同样封装了pcre_exec封装后的方法名为:ngx_regex_exec 源码位于nginx/src/core/ngx_regex.h文件中:

    #define ngx_regex_exec(re, s, captures, size)                                \
        pcre_exec(re->code, re->extra, (const char *) (s)->data, (s)->len, 0, 0, \
                  captures, size)
    

    可以看到参数re要求的是ngx_regex_compile_t中的regex成员变量. 而被搜索的字符串被替换成了Nginx内部的字符串类型ngx_str_t. 同时也将Nginx内部不常用的搜索偏移以及选项设置为成0. 如果仍需要使用偏移以及选项的话可以直接使用pcre_exec来跳过Nginx的封装.

Refs:

SS通讯步骤

目前在写一个Socks5转SS的工具(某些奇怪需求需要), 在互联网上翻了几个网站并没有详细说明SS通讯步骤. 所以在这记录下.

密码初始化

本文章aes-256-cfb加密来说明, 当使用AES时需要提供KeyIV(初始化向量). 其中IV类似于给hash加盐, 不过这个盐是基于上一个数据块加密后的出来的盐. 那为啥还需要提供IV呢?因为第一次用的时候没上一块数据只能自己指定一个.

SS生成KeyIV是基于用户指定的密码来生成的, 以下为TypeScript基于密码生成KeyIV代码:

function generateKeyIVByPassword(passwordString: string, keyLength: number, ivLength: number): any {
    var password = new Buffer(passwordString, "binary");
    var hashBuffers: Array<Buffer> = [];
    for (var dataCount = 0, loopCount = 0; dataCount < keyLength + ivLength; loopCount++) {
        var data: any = password;
        if (loopCount > 0) {
            data = Buffer.concat([hashBuffers[loopCount - 1], password]);
        }
        var md5 = crypto.createHash("md5");
        var md5Buffer = md5.update(data).digest();
        hashBuffers.push(md5Buffer);
        dataCount += md5Buffer.length;
    }
    var hashBuffer: Buffer = Buffer.concat(hashBuffers);
    return {
        key: hashBuffer.slice(0, keyLength),
        iv: hashBuffer.slice(keyLength, keyLength + ivLength)
    };
}

从上述代码中可以得知SS作者先将用户指定的密码迭代MD5多次; 其结束条件是迭代多次的MD5结果长度相加, 如果长度大于加密算法所需Key长度 + IV长度则退出循环. 最后将MD5 Hash结果截断来获得KeyIV.

通讯过程(TCP过程)

当客户端连接至服务端后会直接发送 客户端IV + 已被加密的Socks5头 + TCP数据.

IV交换

Client连接后, Client发送的第一个数据前N个字节(取决于加密算法)是AESIV用于来解密来自Client的数据.
然后Server发送Client的第一个数据也是类似. 其结构图如下:
ShadowsocksIVED.png

注意!SS中这样交换IV是不安全做法.

通讯

SS省略了Socks5协商过程直接进入通讯过程. Socks5通讯需要客户端提供 目标IP或域名 + 端口. 当客户端提供完成之后服务端会连接至目标IP并返回是否连接成功; 如果连接成功就能够直接通讯了.

格式如下:
027071B4-415D-4ACE-BF39-EFABB2F7B64A.png

上述说过SS省略了协商过程, 所以这里Socks5请求也是缩水的. 其把前3个字节砍掉了, 也就是说Client只会发来ATYP, DST ADDR, DST PROT. 当连接至DST ADDRDST ADDR发送的数据加密发送给Client, Client发送的数据解密发送给DST ADDR. 这样SS就完成了通讯.

参考资料

批量将MyISAM引擎转换为InnoDB引擎

<?php
header('Content-type: text/html;charset=utf-8');
@error_reporting(E_ALL | E_STRICT);
@ini_set('display_errors', '1');

//填写数据库IP地址, 账号, 密码
$con = mysql_connect('127.0.0.1', 'root', 'root');
//填写需要转换的数据库名字
$dbName = 'minecraft_status';

$sql = "SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '$dbName' AND ENGINE = 'MyISAM';";
$rs = mysql_query($sql, $con);

$count = 0;
$ok = 0;
while($row = mysql_fetch_array($rs))
{
    $count ++;
    $tbl = $row[0];
    $sql = "ALTER TABLE $dbName.$tbl ENGINE = INNODB;";
    $resultado = mysql_query($sql);
    if ($resultado){
        $ok ++;
        echo $sql."<hr/>";
    }
}
if ($count == $ok)
{
    echo '<div style="color: green"><b>全部表转换成功</b></div>';
}else{
    echo "<div style=\"color: red\"><b>出现错误</b>表的数量: $count, 成功转换的数量: $ok</div>";
}

MWC飞控通讯

前几个星期入手了一款基于MWC飞控的WIFI小四轴,缺点就是只有手机APP。操作起来没有手感。寻思着用Ardiuno+C#做一个实体遥控器。首先是封装MWC通讯格式,下面就是封装的代码。最后结局就是手滑没调好撞废一个硬盘

/// <summary>
/// 获取发送Byte
/// </summary>
/// <param name="command">操作</param>
/// <param name="data">数据</param>
/// <returns></returns>
public byte[] GetFlyByte(int command, int[] data)
{
    byte[] FlyByte = new byte[data.Length + 6];
    // $
    FlyByte[0] = (byte)Convert.ToInt32("24", 16);
    // M
    FlyByte[1] = (byte)Convert.ToInt32("4D", 16);
    // <
    FlyByte[2] = (byte)Convert.ToInt32("3C", 16);
    //数据长度
    FlyByte[3] = (byte)Convert.ToInt32(data.Length.ToString("X"), 16);
    //操作
    FlyByte[4] = (byte)Convert.ToInt32(command.ToString("X"), 16);
    
    byte[] ConvertData = new byte[data.Length];
    //发送数据赋值 与 校验数据赋值
    for (int i = 5, j = 0; i < data.Length + 5; i++, j++ )
    { 
        FlyByte[i] = (byte)data[j];
        ConvertData[j] = (byte)data[j];
    }

    FlyByte[FlyByte.Length-1] = CheckNumber((byte)command, ConvertData);
    return FlyByte;
}

/// <summary>
/// 获取校验码
/// </summary>
/// <param name="command">操作</param>
/// <param name="data">数据</param>
/// <returns>校验码</returns>
private byte CheckNumber(byte command, byte[] data)
{
    byte rdata = 0;

    rdata ^= (byte)(data.Length & 0xFF);
    rdata ^= (byte)(command & 0xFF);

    for (int i = 0; i < data.Length; i++)
        rdata ^= (byte)(data[i] & 0xFF);

    return rdata;
}

flyc.jpg
MWC通讯格式官方文档:http://www.multiwii.com/wiki/index.php?title=Multiwii_Serial_Protocol