Smiley face

我的征尘是星辰大海。。。

The dirt and dust from my pilgrimage forms oceans of stars...

-------当记忆的篇章变得零碎,当追忆的图片变得模糊,我们只能求助于数字存储的永恒的回忆

作者:黄教授

二〇一八


一月一日等待变化等待机会

Learning Linux Binary Analysis是一部无上至宝,我决定使用我的信用卡点数买正版,一方面向大师”elfmaster”, Ryan O'Neill
致敬,另一方面也是经典应当收藏的意思。在编译以下这段测试程序时候,32位和64位是有很大的不同的。

Int foo()
{
}
_start()
{
        foo();
        bar();
        __asm__("leave");
}
首先编译32位:gcc -m32 -c -nostdlib nomain.c -o nomain.o 查看汇编码:

nick@ubuntu-14:~/Documents/staroceans/practice$ objdump -S  nomain.o
nomain.o:     file format elf32-i386
Disassembly of section .text:
00000000 <foo>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   5d                      pop    %ebp
   4:   c3                      ret    
00000005 <_start>:
   5:   55                      push   %ebp
   6:   89 e5                   mov    %esp,%ebp
   8:   53                      push   %ebx
   9:   83 ec 04                sub    $0x4,%esp
   c:   e8 fc ff ff ff          call   d
<foo+0xd>
  11:   81 c3 02 00 00 00       add    $0x2,%ebx
  17:   e8 fc ff ff ff          call   18
<foo+0x18>
  1c:   c9                      leave  
  1d:   83 c4 04                add    $0x4,%esp
  20:   5b                      pop    %ebx
  21:   5d                      pop    %ebp
  22:   c3                      ret    
Disassembly of section
.text.__x86.get_pc_thunk.bx:
00000023 <__x86.get_pc_thunk.bx>:
  23:   8b 1c 24                mov  (%esp),%ebx
  26:   c3                      ret    
注意高亮部分。不过我遇到了一些困惑,准备买正本的书来作实验。

一月二日 等待变化等待机会

回到了圣何塞,头脑还不是很清楚,我开始困惑为什么我在/etc/cron.xxx/下看不到我的设定的任务,后来才明白你不是直接编辑这些文件的,你使用crontab这个命令来修改添加任务。比如,我使用strace crontab -l发现他是在/var/spool/cron/crontabs/xxx
作为一个月的回忆,这里证明我还是读了不少的东西
这个也许是一个微不足道的东西,就是在html里嵌入代码,我长期依赖wysiwyg的html编辑器,但是遭遇过kompozer的灾难性错误后我决定使用最最白痴的libreoffice里的writer来编辑html文件,因为我仅仅编辑一些文本日记,我的amazon的S3的静态网站不支持任何的脚本,就是一个纯粹的html文本服务器,或者说就是文件服务器,那么对于脚本深恶痛绝也不擅长的我何必要去折腾那些强大但不一定免费的编辑器呢?最要命的是在不同的linux版本下编辑器居然行为不一致,至少我认为是这样子的。所以,这个问题最简单的解决办法是使用这个万能的tag: <pre><code>now starts my original text code with various tab etc.</code></pre>可惜我的writer不支持html代码编辑,我只能打开文本编辑器直接编辑,这个辛苦的工作还是值得的。

一月四日 等待变化等待机会

U-boot的代码还看不懂,这位大侠的学习笔记我现在看还有些困难,我买的葵花宝典《Learning Linux Binary Analysis》,怀着对于作者Ryan “elfmaster” O'Neill 的无限尊敬我购买了原版书达到了,准备认认真真一步一步看,这个现在比较适合我的程度,很多不一定是完全的陌生,但是一定是有很大的提高。这里是大师的网站,还没有拜读。 一月五日 等待变化等待机会 下载了大师的作品text-infector。究竟怎么使用呢?代码尽管不多可是怎么平移添加蠕虫还是要花时间才能看得懂。这里是大师的论文,还没有看就发现大师还有自己的大师,这篇祖师的论文更加的难懂。下载一个版本吧。

一月六日 等待变化等待机会

对于PIC我的头脑并没有很清楚的概念,至少怎么实现的是一窍不通,甚至现在才意识到它和loader relocation是两种不同的方式,这篇文章值得学习,我还没有看它的前传,直接读PIC似乎有些断层?这一段要如何理解呢?:
There's no instruction to obtain the value of the instruction pointer on x86, but we can use a simple trick to get it. Here's some assembly pseudo-code that demonstrates it:
    call TMPLABEL
TMPLABEL:
    pop ebx
什么叫做不知道instruction pointer value?难道instruction pointer和programcounter概念上有不同?难道你不能读取PC寄存器的值?作者的所谓trick让我难以理解。呃,我唯一能够解释的就是在x86架构中PC寄存器的值你无法直接获得,这个倒是对的,我的误解,所以你才只好调用一个假函数call,这个指令会把PC存在stack里,因此pop ebx获得pc在ebx寄存器了。(看这篇文章我睡着了三次)

一月七日 等待变化等待机会

昨晚看不明白早上继续看,下载如下。关于作者验证全局变量在got中地址一段很精彩,需要特别仔细阅读:
  1. 使用一个所谓的thunk调用得到当前“下一条”指令的地址是编译器固定的做法,函数名字__i686.get_pc_thunk.cx本身就言明了作用,在文章中这个是下一条指令,也就是调用这个小函数的下一条,代码里是0x444,gdb运行中实际地址是0x00131444,也许是segment基地址是0x00131000??
  2. GOT是使用固定常量0x1bb0偏移,这一点还不知道出处
  3. 使用gdb的能力还是要加强:info registers查看所有寄存器内容这个不必说了。set disassembly-flavor intel设置汇编风格为Intel目标参数看起来习惯吗?然后disas加函数名字查看函数汇编码。x加地址是查看地址的内容,这个我没有用过值得学习。
作者提到的loadtime relocation缺点就是如果代码段被修改了地址那么这个段就不能真正成为"shared"这里是否也意味着代码段不能是只读?,也就失去了sharedlibrary的本来目的,至于数据段原本就是每个进程里自己维护的不能共享。所以这个也就是PIC的优点。关于这一点要回头阅读作者引用的,下载一个拷贝。可是刚看了一个开头我就困惑,因为executable的entry point似乎是random,至少x86_64是这样的,也许x86,即所谓32位是fixed?作者给了另一个引用的文章,学习就是这样子的,我想起以前的导师的关于读论文的话,一篇好的论文的作用在于它给你指引到一系列的好的论文。一个很小的概念要提醒一下就是link time relocation和loadtime relocation是两个不同的过程与算法,我想这个概念是任何一个学过计算机的人都明白的,我不应该不明白,但是,具体的内涵却不一定能说出来,只不过我曾经比较过.o文件里的函数汇编和最后的可执行文件里的函数汇编代码,其中的差别是显而易见的,但是怎么做到的就不甚了了了。
总之前者比后者复杂的多。

    ; add xx + yy + zz and store it in sum
    ;
    mov     eax, DWORD PTR [ebp-8]
    mov     edx, DWORD PTR [ebp-4]
    lea     eax, [edx+eax]
    add     eax, DWORD PTR [ebp-12]
    mov     DWORD PTR [ebp-16],eax
以上的汇编我迷惑了好久因为我不熟悉lea指令,我还以为真的去取值,实际上是一种特殊的地址计算指令,而独特的地方是计算不使用alu,就是算术逻辑单元,因此这个操作可以和普通的加减运算并行。当然这里并不是在计算地址,只不过是省的去争抢alu,我看了好一会儿才明白因为一开始先入为主以为一看到[]就以为是内存取值。

一月八日 等待变化等待机会

如果我在x86_64ubuntu下编译32位程序,比如gcc -m32,然后查看readelf -h我看到的entry point是0x8048440,这个和64位程序很不同。

一月十日 等待变化等待机会

安装wireshark默认不能enable网卡的访问,需要配置: sudo dpkg-reconfigure wireshark-common 当然还要把用户添加到wireshark的组里:
sudo usermod -a -G wireshark $USER
另一个好东西是怎样在desktop环境下logout,因为你实际上是在gnome的session里,所以这个命令很有用:
gnome-session-quit --logout –no-prompt
整天被文档的格式折磨。。。
为什么显示和编辑的时候不一样呢?这里说不应当直接使用default style,可是text body总是改变每次我打开文档的时候?

一月十一日 等待变化等待机会

经过了太多的忍耐,我的耐心到了极点,决定使用笔记本来编写html日记,反正我也没有什么花哨的背景和字体,只是记录原始的思想而已。
尝试一个相对链接,我的同事在讨论流行的spectre。我实际上没有兴趣,至少是没有能力看这篇论文,我知道他是利用prediction的算法,这个学习计算机架构几乎人人都知道,至少我这么认为,可是除此之外一般人又明白多少呢?

一月十二日 等待变化等待机会

学习PIC是一件很难的事情如果不结合代码可能更难?请看这幅画里面有多少文字来解释:
Explanation:
position independence code calling flow
这个还没有完仅仅是开始,大师接着解释在resolve之后发生了什么?
After the first call, the diagram looks a bit differently:
position independence code calling flow

一月十三日 等待变化等待机会

看汇编有多难呢?总之一个人的记忆力丧失了至少还能写一些C/C++的代码前提是利用一些好的开发工具,但是汇编码靠的很多是记忆力,脑子记不过来,当然熟能生巧是不变的,我几乎没有怎么写过,除了刚上大学的第一门课的第一篇作业和第一个project,那时候很自豪以为汇编写了几千行就是很牛逼的事情。其实机器产生的汇编码更多你怎么不和机器比?
早上起来废话一大堆,看懂的部分大概就是一两行。前两天关于获取当前代码地址后加了一个神奇的常数跳转的疑惑明白了,这个是编译器根据每个动态库具体计算出来的并非常数,每个文件都是不一样的,应该是全部汇编指令都产生以后再计算来修改得到的。比如以下是我自己实际在我的64位ubuntu下编译的32位的动态库,注意对于32位单纯的-shared是可以编译成动态库但是并非PIC代码,一定要加上-fPIC,这个和64位动态库不同因为64位是不支持简单的loadtime relocation一定要PIC代码才行,很简单的判断就是查看汇编是否有__x86.get_pc_thunk.bx或者__i686.get_pc_thunk.bx实际上两者都有这些thunk函数,如何判断两种文件还需要学习),因为在我的机器上是前者,在大师的例子里是后者,看来我的电脑使用amd的cpu而大师可能是Intel的CPU吧,虽然看汇编码两者没有区别都是把栈寄存器地址放在返回的寄存器里作为返回值,也许将来预留的吧。
以下是我自己的汇编码:

00000556 <ml_func>:
 556:   55                      push   ebp
 557:   89 e5                   mov    ebp,esp
 559:   53                      push   ebx
 55a:   83 ec 24                sub    esp,0x24
 55d:   e8 be fe ff ff          call   420 <__x86.get_pc_thunk.bx>
 562:   81 c3 9e 1a 00 00       add    ebx,0x1a9e
 568:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 56b:   89 04 24                mov    DWORD PTR [esp],eax
 56e:   e8 9d fe ff ff          call   410 <ml_util_func@plt>
请注意我的0x562地址的指令里的常量是0x1a9e,请对照大师的例子,他的0x483地址的指令常量是0x1b71,我本来一直以为是什么魔术数字:

00000477 <ml_func>:
 477:   55                      push   ebp
 478:   89 e5                   mov    ebp,esp
 47a:   53                      push   ebx
 47b:   83 ec 24                sub    esp,0x24
 47e:   e8 e4 ff ff ff          call   467 <__i686.get_pc_thunk.bx>
 483:   81 c3 71 1b 00 00       add    ebx,0x1b71
 489:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 48c:   89 04 24                mov    DWORD PTR [esp],eax
 48f:   e8 0c ff ff ff          call   3a0 <ml_util_func@plt>
其实很简单的,这个是当前代码地址和.GOT.PLT段的偏移量。请看我的:

nick@ubuntu-14:/tmp$ readelf -x .got.plt libsample.so 

Hex dump of section '.got.plt':
  0x00002000 081f0000 00000000 00000000 f6030000 ................
  0x00002010 06040000 16040000                   ........
在我的__x86.get_pc_thunk.bx使用ebx返回的函数本身的“下一条”指令地址也就是说0x562,那么加上0x1a9e就是0x562+0x1a9e=0x2000,这个正是.got.plt的section地址。所以,这个小细节是明白了是编译器计算出来的。
既然ebx是.GOT.plt的地址,再来看我们的ml_util_func@plt的汇编码,他就是简单的跳转到所谓的.GOT.PLT的地址加上0x14的偏移。也就是说地址0x2014的地方,看我的.got.plt我们知道是0x1604,不过根据little-endian,地址应该是0x0416。请看我的0x0416的地址码正是大师所说的ml_util_func@plt的push的语句。

00000410 <ml_util_func@plt>:
 410:   ff a3 14 00 00 00       jmp    DWORD PTR [ebx+0x14]
 416:   68 10 00 00 00          push   0x10
 41b:   e9 c0 ff ff ff          jmp    3e0 <_init+0x28>
 
大师接着解说relocation的部分,其实我还没有理解,就是.rel.plt

Contents of section .rel.plt:
 03a0 0c200000 07020000 10200000 07030000  . ....... ......
 03b0 14200000 07090000                    . ......        
以上是我用objdump -s得到的,似乎驴唇不对马嘴,于是我尝试使用readelf -r结果似乎有些对头实际上你仔细观察以上只是没有翻译而已,不过我可以看得出来地址是0x200c是从little-endian的0x0c20转过来的offset,但是最后一个怎么变出的sym.value的0x054b怎么来的呢?不过这个超出了分析的意义使用objdump没毛病啊

Relocation section '.rel.plt' at offset 0x3a0 contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000200c  00000207 R_386_JUMP_SLOT   00000000   __cxa_finalize
00002010  00000307 R_386_JUMP_SLOT   00000000   __gmon_start__
00002014  00000907 R_386_JUMP_SLOT   0000054b   ml_util_func
这里按照大师的解说就是说0x2014的地方需要修改为ml_util_func的地址,这个0x54b是什么呢?会是ml_util_func吗?

0000054b <ml_util_func>:
 54b:   55                      push   ebp
 54c:   89 e5                   mov    ebp,esp
 54e:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 551:   83 c0 01                add    eax,0x1
 554:   5d                      pop    ebp
 555:   c3                      ret
看到这里我还有什么不信服的呢?只不过整个过程非常的令人头晕目眩,仿佛高难度的体操令人眼花缭乱完全没有头绪。

一月十五日 等待变化等待机会

大师说看PIC之前应该先读懂loadtime relocation,所以,应该先读这一篇,实际上这个应该更容易。
现在我按照大师的指引一步一步复述如下:
所以,总结一下就是说: 我觉得这个是所有程序员的最直接的想法似乎也没有什么错,那么引入PIC的原因肯定是为了减少这类修改的次数,想想看对于一个全局变量的每一次引用都要去修改重复了多少次呢?实际上用多余的代码来实现PIC可以避免多次的修改,这一点看一下PIC应该是对的,因为在GOT里实际上是一个地址改动吧?(这一点才是大师希望你能够体会到的:为什么现在PIC胜过loadtime relocation成为主流的做法,要是我一开始就读这一篇带着这个问题去看PIC就直接有了答案了。顺序啊)我的记忆力太差了,前两天看到的PIC的优点就是loadtime relocation的缺点是这样子的,提醒一下防止脑子发昏,因为我就是这样子的,物理地址肯定是不变的否则怎么share,那么就是每一个进程内部映射成自己的虚拟地址不同,换句话说同样的shared library的segement会被load到不同的虚拟地址。

一月十六日 等待变化等待机会

loadtime relocation对于函数的算法非常的难懂,对比全局变量复杂度增加了一个数量级,我看了一晚上也没有看懂,看来只有实际动手一步一步推演才行。当然大师也说他的解说加快了步骤是假定读者已经明白了要义,我感到比较惭愧,似乎很不得法悟性很糟。现在跳过详细的解说,如果你直接去读大师所谓的bonus的部分给你的一些真知灼见的分析为何算法如此复杂的原因,我才有些恍然,因为不要以为凡是global就一定是在我们当前代码段的相对固定的偏移,如果是这样的话,global就沦为了static,这个正是大师的实验所证实的。因为也许这个global本身就在另一个动态库里单靠相对偏移不可能解决的。

一月十七日 等待变化等待机会

我一开始始终不明白大师的输出有所有的程序的header的地址和名字,我还一直以为是大师的gdb的版本不同的特性。今天看了大师的driver才恍然大悟,其实我早就注意到了dl_iterate_phdr,可是后来就忘记了。原来是一个很有用的小trick,就是说我一直期待寻找的方法,我之前曾经费尽心机的要去debug linker/loader的运行期加载的模块,只能使用LD_DEBUG的环境变量输出到文件,虽然也不错可是毕竟有时候不太好控制,那么这个回调函数注册的办法提供了可编程的方法。
我在大师程序的基础上加了几行代码输出type,flag的可读名字。不过有些flag和type似乎是多余的,应该是永远不可能的,不过基本是harmless。

#define _GNU_SOURCE
#include <link.h>
#include <stdlib.h>
#include <stdio.h>

char* typeName[]=
{
"unused",       /* Program header table entry unused */
"Loadable program segment",       /* Loadable program segment */
"Dynamic linking information",       /* Dynamic linking information */
"Program interpreter",       /* Program interpreter */
"Auxiliary information",       /* Auxiliary information */
"Reserved",       /* Reserved */
"Entry for header table itself",       /* Entry for header table itself */
"Thread-local storage segment",       /* Thread-local storage segment */
"Number of defined types",       /* Number of defined types */
"Start of OS-specific",  /* Start of OS-specific */
"GCC .eh_frame_hdr segment",  /* GCC .eh_frame_hdr segment */
"Indicates stack executability",  /* Indicates stack executability */
"Read-only after relocation",  /* Read-only after relocation */
"unknown"
};

int type2Number(int in)
{
    if (in < 8)
    {
        return in;
    }
    switch (in)
    {
        case 0x60000000:
            return 9;
        case 0x6474e550:
            return 10;
        case 0x6474e551:
            return 11;
        case 0x6474e552:
            return 12;
    }
    return 13;
}

char*flagName[] =
{
"executable",    /* Segment is executable */
"writable",    /* Segment is writable */
"readable",    /* Segment is readable */
"OS-specific",  /* OS-specific */
"Processor-specific",  /* Processor-specific */
"PF_PARISC_SBP(PF_HP_SBP)",
"PF_HP_PAGE_SIZE",
"PF_HP_FAR_SHARED",
"PF_HP_NEAR_SHARED",
"PF_HP_CODE",
"PF_HP_MODIFY",
"PF_HP_LAZYSWAP",
"executable|writable",
"executable|readable",
"readable|writable",
"readable|writable|executable",
"unknown"
};
int flag2Number(int in)
{
    switch (in)
    {
        case PF_X:
            return 0;
        case PF_W:
            return 1;
        case PF_R:
            return 2;
        case 0x0ff00000:
            return 3;
        case 0xf0000000:
            return 4;
        case 0x08000000:
            return 5;
        case 0x00100000:
            return 6;
        case 0x00200000:
            return 7;
        case 0x00400000:
            return 8;
        case 0x01000000:
            return 9;
        case 0x02000000:
            return 10;
        case 0x04000000:
            return 11;
        case (PF_X|PF_W):
            return 12;
        case (PF_X|PF_R):
            return 13;
        case (PF_W|PF_R):
            return 14;
        case (PF_X|PF_W|PF_R):
            return 15;
    }
    return 12;
}

static int header_handler(struct dl_phdr_info* info, size_t size, void* data)
{
    printf("name=%s (%d segments) address=%p\n",
            info->dlpi_name, info->dlpi_phnum, (void*)info->dlpi_addr);
    for (int j = 0; j < info->dlpi_phnum; j++) {
         printf("\t\t header %2d: address=%10p\n", j,
             (void*) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr));
         int typeNumber=type2Number(info->dlpi_phdr[j].p_type);
         int flagNumber=flag2Number(info->dlpi_phdr[j].p_flags);
         printf("\t\t\t type=%s, flags=%s\n",
                 typeName[typeNumber], flagName[flagNumber]);
    }
    printf("\n");
    return 0;
}
extern int ml_func(int, int);
int main(int argc, const char* argv[])
{
    dl_iterate_phdr(header_handler, NULL);

    int t = ml_func(argc, argc);
    return t;
}
编译方法自然很简单的:gcc driver.c -m32 -std=c99 -L/tmp -lfunc -o driver
运行的输出结果是这样子的:

name= (9 segments) address=(nil)
		 header  0: address= 0x8048034
			 type=Entry for header table itself, flags=executable|readable
		 header  1: address= 0x8048154
			 type=Program interpreter, flags=readable
		 header  2: address= 0x8048000
			 type=Loadable program segment, flags=executable|readable
		 header  3: address= 0x8049f00
			 type=Loadable program segment, flags=readable|writable
		 header  4: address= 0x8049f0c
			 type=Dynamic linking information, flags=readable|writable
		 header  5: address= 0x8048168
			 type=Auxiliary information, flags=readable
		 header  6: address= 0x8048bd0
			 type=GCC .eh_frame_hdr segment, flags=readable
		 header  7: address=     (nil)
			 type=Indicates stack executability, flags=readable|writable
		 header  8: address= 0x8049f00
			 type=Read-only after relocation, flags=readable

name= (4 segments) address=0xf776e000
		 header  0: address=0xf776e000
			 type=Loadable program segment, flags=executable|readable
		 header  1: address=0xf776e2bc
			 type=Dynamic linking information, flags=readable
		 header  2: address=0xf776e558
			 type=Auxiliary information, flags=readable
		 header  3: address=0xf776e5b8
			 type=GCC .eh_frame_hdr segment, flags=readable

name=/tmp/libfunc.so (7 segments) address=0xf7767000
		 header  0: address=0xf7767000
			 type=Loadable program segment, flags=executable|readable
		 header  1: address=0xf7768ef8
			 type=Loadable program segment, flags=readable|writable
		 header  2: address=0xf7768f04
			 type=Dynamic linking information, flags=readable|writable
		 header  3: address=0xf7767114
			 type=Auxiliary information, flags=readable
		 header  4: address=0xf77675a0
			 type=GCC .eh_frame_hdr segment, flags=readable
		 header  5: address=0xf7767000
			 type=Indicates stack executability, flags=readable|writable
		 header  6: address=0xf7768ef8
			 type=Read-only after relocation, flags=readable

name=/lib32/libc.so.6 (10 segments) address=0xf759a000
		 header  0: address=0xf759a034
			 type=Entry for header table itself, flags=executable|readable
		 header  1: address=0xf7704cb4
			 type=Program interpreter, flags=readable
		 header  2: address=0xf759a000
			 type=Loadable program segment, flags=executable|readable
		 header  3: address=0xf77421dc
			 type=Loadable program segment, flags=readable|writable
		 header  4: address=0xf7743da8
			 type=Dynamic linking information, flags=readable|writable
		 header  5: address=0xf759a174
			 type=Auxiliary information, flags=readable
		 header  6: address=0xf77421dc
			 type=Thread-local storage segment, flags=readable
		 header  7: address=0xf7704cc8
			 type=GCC .eh_frame_hdr segment, flags=readable
		 header  8: address=0xf759a000
			 type=Indicates stack executability, flags=readable|writable
		 header  9: address=0xf77421dc
			 type=Read-only after relocation, flags=readable

name=/lib/ld-linux.so.2 (7 segments) address=0xf7770000
		 header  0: address=0xf7770000
			 type=Loadable program segment, flags=executable|readable
		 header  1: address=0xf7790cc0
			 type=Loadable program segment, flags=readable|writable
		 header  2: address=0xf7790f30
			 type=Dynamic linking information, flags=readable|writable
		 header  3: address=0xf7770114
			 type=Auxiliary information, flags=readable
		 header  4: address=0xf778c8c0
			 type=GCC .eh_frame_hdr segment, flags=readable
		 header  5: address=0xf7770000
			 type=Indicates stack executability, flags=readable|writable
		 header  6: address=0xf7790cc0
			 type=Read-only after relocation, flags=readable
如何在gdb里打印字串的全部?set print elements 0

一月十九日 等待变化等待机会

我简单比较了一下loadtime relocation和pic的代码的长度,后者比前者多24个指令,这还没有包括plt等仅仅是函数本身的指令数字。前者54,后者78.

int myglob = 42;
int ml_util_func(int a)
{
    return a + 1;
}
int ml_func(int a, int b)
{
    int c = b + ml_util_func(a);
    myglob += c;
    return b + myglob;
}
编译前者:gcc -m32 -shared func.c -o libloadrelo.so

00000556 <ml_func>:
 556:   55                      push   ebp
 557:   89 e5                   mov    ebp,esp
 559:   83 ec 14                sub    esp,0x14
 55c:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 55f:   89 04 24                mov    DWORD PTR [esp],eax
 562:   e8 fc ff ff ff          call   563 <ml_func+0xd>
 567:   8b 55 0c                mov    edx,DWORD PTR [ebp+0xc]
 56a:   01 d0                   add    eax,edx
 56c:   89 45 fc                mov    DWORD PTR [ebp-0x4],eax
 56f:   8b 15 00 00 00 00       mov    edx,DWORD PTR ds:0x0
 575:   8b 45 fc                mov    eax,DWORD PTR [ebp-0x4]
 578:   01 d0                   add    eax,edx
 57a:   a3 00 00 00 00          mov    ds:0x0,eax
 57f:   8b 15 00 00 00 00       mov    edx,DWORD PTR ds:0x0
 585:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 588:   01 d0                   add    eax,edx
 58a:   c9                      leave  
 58b:   c3                      ret  
后者编译:gcc -m32 -shared -fPIC -o libpic.so

00000556 <ml_func>:
 556:   55                      push   ebp
 557:   89 e5                   mov    ebp,esp
 559:   53                      push   ebx
 55a:   83 ec 24                sub    esp,0x24
 55d:   e8 be fe ff ff          call   420 <__x86.get_pc_thunk.bx>
 562:   81 c3 9e 1a 00 00       add    ebx,0x1a9e
 568:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
 56b:   89 04 24                mov    DWORD PTR [esp],eax
 56e:   e8 9d fe ff ff          call   410 <ml_util_func@plt>
 573:   8b 55 0c                mov    edx,DWORD PTR [ebp+0xc]
 576:   01 d0                   add    eax,edx
 578:   89 45 f4                mov    DWORD PTR [ebp-0xc],eax
 57b:   8b 83 ec ff ff ff       mov    eax,DWORD PTR [ebx-0x14]
 581:   8b 10                   mov    edx,DWORD PTR [eax]
 583:   8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
 586:   01 c2                   add    edx,eax
 588:   8b 83 ec ff ff ff       mov    eax,DWORD PTR [ebx-0x14]
 58e:   89 10                   mov    DWORD PTR [eax],edx
 590:   8b 83 ec ff ff ff       mov    eax,DWORD PTR [ebx-0x14]
 596:   8b 10                   mov    edx,DWORD PTR [eax]
 598:   8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
 59b:   01 d0                   add    eax,edx
 59d:   83 c4 24                add    esp,0x24
 5a0:   5b                      pop    ebx
 5a1:   5d                      pop    ebp
 5a2:   c3                      ret    

一月二十二日 等待变化等待机会

大师的文章深入浅出,尤其可贵的是绝不像很多略知一二的人的流于浮光掠影式的只空谈理论,而是拿出实实在在的干货,正应了大师自己说的那样:plz show me the codes!这个和老祖师Linus的RTFC!有异曲同工之妙。这一篇是关于debugger的,保存一个版本

一月二十四日 等待变化等待机会

我的两台打印机设置起来着实麻烦。hp的设置可以使用wps的button,我使用hp-wificonfig之类的工具似乎不行。不过我不是很确认因为我只是把它当作扫描仪使用,因此能够设定wifi就好了,有个办法可以查看所有的device: ping -b 192.168.1.255然后arp -n可以看看有什么设备。
Pantum2500w的设置比较麻烦,首先可以按cancel button开关电源5秒以上,这样reset factory setting,然后可以使用pantum的wifi连入,他的默认的ip=192.168.223.1密码是admin/000000。打印的时候需要连入pantum的wifi。这个是什么问题?

一月二十五日 等待变化等待机会

hld是一个什么东东?这是它的白皮书。关于bitcoin的概念很深奥的。

一月二十六日 等待变化等待机会

下载Satoshi nakamoto的原论文也许更好懂一些,因为不懂原理就贸然读实现后的细节是一个费时费力的错误。

一月二十七日 等待变化等待机会

浑浑噩噩读的似懂非懂,其实我原来明白的还是那些基本原理,没有看到实际的bitcoin的存储记录一切的一切都是不明其所以然。这个是论文引用的源头,我下载一个版本。这篇短文一望而知是某个大学教授之类的空想家的乌托邦式的臆想,因为他批判的政府滥用发钞权实际上被移交给了某些服务器控制者,在他的眼里似乎系统管理员都是宅心高尚不食人间烟火的圣人。所以,从这个层面上说Satoshi的设计是理性的因为不能相信任何机构或者个人,而是制度,一个分散的共享信息的系统,防止作弊的方法也不是靠良心,而是简单的计算能力的投入与产出。而且bmoney的作者在货币初始发行量的决定上也含混不清。这一点上Satoshi的方法更加的简单容易计量,因为所谓的计算能力的招标是一个空泛的想法,而挖矿的概念似乎更加的实际,直接解决了公共记录维护的成本摊销问题。
其实我需要从更加基本的问题看起,对于这个也许温故而知新文件数字签名的再学习。看完这篇论文你就知道所谓的区块链的想法并非什么突然一夜之间冒出来的天才的想法,他的基础来自于数字文件的时间戳的解决方法。使用序列号使得变造成本极其高昂。

一月二十八日 等待变化等待机会

关于给文件加时间戳的数字签名我只能从原理上理解,就是有这么几个要素:
  1. 首先,你不会把你的文件的原件交给任何加时间戳的机构,因为会泄密,不要相信任何人和机构,所以,你做一个数字的hash。
  2. 其次,你需要一个签名机构的序列号,这个理由是因为为了防止篡改,不仅仅是其他作签名的客户,更主要的是防止发放时间戳的机构和某些人串通,这个序列号可以让你检索到你之前的或者之后的申请时间戳的客户。
  3. 你需要时间戳机构在发放给你的证书里明确的把时间戳作为证书的一部分。这个原因就不要解释了吧?
  4. 你需要你上一个和你一样申请时间戳的客户的证书的一个hash,原因是什么呢?因为你要让你的证书牢牢的“咬死”上一个证书,如果每一份证书都通通咬死上一个证书,那么谁想篡改的成本就非常大,因为要修改的不仅仅是你本身的证书,还需要之前和所有之后的,这个也就是区块链的概念。

    He who comes after me has surpassed me because he was before me.

  5. 总结一下,你的证书里要包含如下:cert=Hash(n,id,x,t,l),其中,n是你的序列号,id是时间戳机构分配给每个客户的id以便能够联系到,x是你的文件的hash,t是时间戳,l是上一份证书的hash。

一月二十九日 等待变化等待机会

关于证书你打算了解多少呢?从原理上说几乎每个人都明白一个大概,那就是证书实际上是一个Public key的容器其中自然也包含了它是如何进行验证的信息?在wiki的解说中是这样子的: 所谓pem的格式其实很简单,就是一种电子邮件的格式,privacy-enhanced electronic mail,应该就是base64的encoding。所以现在可以更加理解到pem或者crt等仅仅是传递数据的格式,并不需要加密。

一月三十日 等待变化等待机会

其实我想做的验证是完全不一样的两件事。首先,给定一个RSA key怎样作数字签名和验证。其次,另一件事是怎样验证一张证书。后者是很多网站的所谓的服务器端安全证书,有很多的CApath需要相当复杂的过程,不过这方面你可以google到无数的资料。对于前者实际上是一个相对简单的问题,在openssl的例子里就有:

/* demos/sign/sign.c */
/* sign-it.cpp  -  Simple test app using SSLeay envelopes to sign data
   29.9.1996, Sampo Kellomaki <sampo@iki.fi> */

/* converted to C - eay :-) */

/* reformated a bit and converted to use the more common functions: this was
 * initially written at the dawn of time :-) - Steve.
 */

#include <stdio.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>

int main ()
{
  int err;
  int sig_len;
  unsigned char sig_buf [4096];
  static char certfile[] = "cert.pem";
  static char keyfile[]  = "key.pem";
  static char data[]     = "I owe you...";
  EVP_MD_CTX     md_ctx;
  EVP_PKEY *      pkey;
  FILE *          fp;
  X509 *	x509;

  /* Just load the crypto library error strings,
   * SSL_load_error_strings() loads the crypto AND the SSL ones */
  /* SSL_load_error_strings();*/
  ERR_load_crypto_strings();
  
  /* Read private key */
  
  fp = fopen (keyfile, "r");
  if (fp == NULL) exit (1);
  pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
  fclose (fp);

  if (pkey == NULL) { 
	ERR_print_errors_fp (stderr);
	exit (1);
  }
  
  /* Do the signature */
  
  EVP_SignInit   (&md_ctx, EVP_sha1());
  EVP_SignUpdate (&md_ctx, data, strlen(data));
  sig_len = sizeof(sig_buf);
  err = EVP_SignFinal (&md_ctx, sig_buf, &sig_len, pkey);

  if (err != 1) {
	ERR_print_errors_fp(stderr);
	exit (1);
  }

  EVP_PKEY_free (pkey);
  
  /* Read public key */
  
  fp = fopen (certfile, "r");
  if (fp == NULL) exit (1);
  x509 = PEM_read_X509(fp, NULL, NULL, NULL);
  fclose (fp);

  if (x509 == NULL) {
	ERR_print_errors_fp (stderr);
	exit (1);
  }
  
  /* Get public key - eay */
  pkey=X509_get_pubkey(x509);
  if (pkey == NULL) {
	ERR_print_errors_fp (stderr);
	exit (1);
  }

  /* Verify the signature */
  
  EVP_VerifyInit   (&md_ctx, EVP_sha1());
  EVP_VerifyUpdate (&md_ctx, data, strlen((char*)data));
  err = EVP_VerifyFinal (&md_ctx, sig_buf, sig_len, pkey);
  EVP_PKEY_free (pkey);

  if (err != 1) {
	ERR_print_errors_fp (stderr);
	exit (1);
  }
  printf ("Signature Verified Ok.\n");
  return(0);
}
这个是c的代码,对于此有一个更简单的做法使用命令行
  1. 先产生公钥和私钥,私钥我们存储方式简单,但是公钥我们存为x509证书而不是req形式:
    openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout "private_key.pem" -out "certificate.pem" -days 365 -subj "/C=US/ST=CA/L=SAN JOSE/O=PARC22/OU=HOME/CN=NICK SIGN KEY"
    这里-nodes不是node的复数形式而是no des的加密的意思,就是说key不要加密。-x509是说公钥生成证书形式而不是certificate request形式。-newkey rsa:4096是说key要4096bits。-days等等的参数是x509的参数。
  2. 签名是用私钥:
    openssl dgst -sha256 -sign private_key.pem -out nick.sign.sha256 sign.txt
  3. 最关键的部分是用公钥来验证签名:
    openssl dgst -sha256 -verify <(openssl x509 -in certificate.pem -pubkey -noout) -signature sign_rsa256.signature sign.txt
    -verify本来要跟一个存储公钥的文件名,而x509的-pubkey可以直接把公钥读出。结果Verified OK
我又一次陷入一个白痴的问题,我的dns nameserver应该怎样设置成静态的?我的服务器地址经常陷入不响应的情况导致我重新把所有的ip都设成静态ip,然后出现的问题是我可以ping到所有的ip但是没有办法访问任何网址,所以,是我的dns name没有正确设置,为了急救我就在/etc/resolv.conf里手动设置nameserver 192.168.1.1,当然重新启动网络服务就会被覆盖。
google发现这个应该设置在/etc/network/interfaces里的每个网卡上:
auto eth0
iface eth0 inet static
address 192.168.1.226
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255
gateway 192.168.1.1
dns-nameservers 192.168.1.1
dns-nameservers 8.8.8.8
如果遇到umount nfs长时间等待可以尝试使用-l,这个是lazy的意思,我觉得只是迅速卸载但是很有可能有后遗症。在下载json文件时候使用python -m json.tool。对于curl跳过Proxy的命令有些奇怪:curl --noproxy the-ip-to-connect http://the-ip-to-connect

二月一日 等待变化等待机会

你有没有想过怎样产生质数来生成key作加密运算?openss里的一个demo程序很简单的说明了这个过程的主要函数的用法:BN_generate_prime(结果我google发现它已经deprecated了)
我在链接openssl的libcrypto.so的时候遇到一个奇怪的现象,/usr/lib/x86_64-linux-gnu/libcrypto.so -> /lib/x86_64-linux-gnu/libcrypto.so.1.0.0,这个似乎是gcc自带的库?我即便使用-L/usr/lib/x86_64-linux-gnu依然链接不成,我检查函数名字:objdump -T /lib/x86_64-linux-gnu/libcrypto.so.1.0.0发现了如下的内容:
000000000009fb50 g    DF .text  000000000000000a OPENSSL_1.0.0 BN_is_prime_ex
这个"OPENSSL_1.0.0"是什么意思呢?原来是所谓的version definition
Version definitions:
1 0x01 0x0746be50 libcrypto.so.1.0.0
2 0x00 0x066a2b20 OPENSSL_1.0.0
3 0x00 0x066a2b21 OPENSSL_1.0.1
        OPENSSL_1.0.0 
4 0x00 0x06a2b214 OPENSSL_1.0.1d
        OPENSSL_1.0.1 
我终于找到了一个寻找了很久很久的功能,就是怎样知道gcc到底链接到那个路径的library呢?这个以前我被迫使用ld_debug的一些功能,可是那个是dso的运行期的行为,我需要的是编译链接过程的答案:gcc -print-file-name=libcrypto.so
  
$ gcc -print-file-name=libcrypto.so
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libcrypto.so
这个Version Definition是怎么使用的呢?我找到了这个解释很不错的样子,还没有仔细阅读,夜已经深了,半夜了,明天再看吧。

二月二日 等待变化等待机会

难道我这么多年都白学了?你在链接的搜索位置居然影响到最后结果?比如gcc -o "primeTest" primeTest.c -lcrypto和gcc -lcrypto -o "primeTest" primeTest.c结果大不相同!!!
总结一下,当我看到链接的错误的时候的反应如下:
  1. 是否架构不对?使用file来查看
  2. 是否路径不对?看看-L的路径如何?
  3. 是否真的没有这个symbol呢?使用objdump -T查看,尤其要注意c++与c的名字装饰不同,使用--demangle有时候反而掩盖了问题
  4. 是否这个symbol也是没有实现的呢?比如UNKNOWN
  5. 设定LD_DEBUG=help再编译可以看到linker的某些行为。不过我尝试了不同的参数并没有发现怎样追踪symbolbinding的痕迹。
    
      libs        display library search paths
      reloc       display relocation processing
      files       display progress for input file
      symbols     display symbol table processing
      bindings    display information about symbol binding
      versions    display version dependencies
      scopes      display scope information
      all         all previous options combined
      statistics  display relocation statistics
      unused      determined unused DSOs
      help        display this help message and exit
  6. 如果是静态库你想要链接注意动态库是有默认的优先的。
  7. gcc -print-file-name=libcrypto.so可以查看编译器的所搜索的库的路径

二月三日 等待变化等待机会

我试图比较BN_generate_prime和BN_generate_prime_ex的效率,结果非常的惊人,足以证明前者deprecated的原因。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <openssl/bn.h>

void callback(type,
              num)
    int type, num;
{
//    if (type == 0)
//        fprintf(stderr, ".");
//    else if (type == 1)
//        fprintf(stderr, "+");
//    else if (type == 2)
//        fprintf(stderr, "*");
//    fflush(stderr);
}

int newCallback(int a, int b, BN_GENCB* cb)
{
//    switch (a)
//    {
//    case 0:
//        //printf("0(%d)", b);
//        printf("0");
//        break;
//    case 1:
//        //printf("1[%d]", b);
//        printf("1");
//        break;
//    case 2:
//        //printf("2{%d}", b);
//        printf("2");
//        break;
//    default:
//        printf("%d", a);
//        break;
//    }
    return 1;
}
char buff[20];
time_t now;

int oldFunc(int argc, char* argv[])
{
    BIGNUM* rand = BN_new();;
    int num=256;

    if (argc >= 2)
    {
        num=atoi(argv[1]);
        if (num == 0)
            num=256;
    }

    fprintf(stderr, "generate a strong prime\n");

    now = time(NULL);
    strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));
    printf("\ntime:[%s]\n", buff);
    rand = BN_generate_prime(rand, num, 1, NULL, NULL, callback, NULL);
    BN_print_fp(stdout, rand);
    now = time(NULL);
    strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));
    printf("\ntime:[%s]\n", buff);

    fprintf(stdout, "\n");

    fprintf(stdout, "\n");

    return 0;
}

int newFunc(int argc, char *argv[])
{
    int num = 256;
    if (argc >= 2)
    {
        num=atoi(argv[1]);
        if (num == 0)
        {
            num=256;
        }
    }
    BIGNUM *bn = BN_new();

    BN_GENCB cb;
    cb.ver = 2;
    cb.cb.cb_2 = newCallback;
    cb.arg = NULL;
    now = time(NULL);
    strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));
    printf("\ntime:[%s]\n", buff);
    if (!BN_generate_prime_ex(bn, num, 1, NULL, NULL, &cb))
    {
        printf("Failed to generate prime.\n");
    }
    else
    {
        printf("bits:%d\n", num);
        BN_print_fp(stdout, bn);
        printf("\n");
    }
    now = time(NULL);
    strftime(buff, 20, "%Y-%m-%d %H:%M:%S", localtime(&now));
    printf("\ntime:[%s]\n", buff);
    BN_free(bn);
    return 0;
}

int main(int argc, char* argv[])
{
    oldFunc(argc, argv);
    newFunc(argc, argv);
    return 0;
}
以下是运行的结果,老函数产生4096bits的prime需要12分钟,后者只需要5分钟。 time:[2018-02-02 22:18:42]

time:[2018-02-02 22:30:46]
time:[2018-02-02 22:30:46]

time:[2018-02-02 22:55:50]
以下纯粹是照抄crypto的manpage的example,尽管我改了一些,不过这个就是Hello world版本的如何生成rsa的key:

#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/bio.h>

int printBIO(const char* strCtx, BIO* bio)
{
    char buffer[2048+1];
    int num = 0;
    printf("*******%s begins*****\n", strCtx);
    while ((num = BIO_gets(bio, buffer, 2048))>0)
    {
        buffer[num] = '\0';
        printf("%s", buffer);
    }
    printf("********%s ends*****\n", strCtx);
    if (num < 0)
    {
        printf("BIO_gets error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        return -1;
    }
    return 0;
}

int main(void)
{
    EVP_PKEY_CTX *ctx = NULL;
    BIO* bio = NULL;
    EVP_PKEY *pkey = NULL;
    pkey = EVP_PKEY_new();
    if (pkey == NULL)
    {
        printf("EVP_PKEY_new error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }
    ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    if (!ctx)
    {
        printf("EVP_PKEY_CTX_new_id error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }

    if (EVP_PKEY_keygen_init(ctx) <= 0)
    {
        printf("EVP_PKEY_CTX_new_id error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }

    if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0)
    {
        printf("EVP_PKEY_CTX_set_rsa_keygen_bits error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }

    /* Generate key */
    if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
    {
        printf("EVP_PKEY_keygen error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }
    bio = BIO_new(BIO_s_mem());
    if (bio == NULL)
    {
        printf("BIO_new error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }
    if (EVP_PKEY_print_public(bio, pkey, 1, NULL)!= 1)
    {
        printf("EVP_PKEY_print_public error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }
    if (printBIO("public key", bio) < 0)
    {
        printf("print public key failed\n");
        goto clear;
    }

    if (EVP_PKEY_print_private(bio, pkey, 1, NULL)!= 1)
    {
        printf("EVP_PKEY_print_private error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }
    if (printBIO("private key", bio) < 0)
    {
        printf("print private key failed\n");
        goto clear;
    }

    if (EVP_PKEY_print_params(bio, pkey, 1, NULL)!= 1)
    {
        printf("EVP_PKEY_print_params error: %s\n", ERR_error_string(ERR_get_error(), NULL));
        goto clear;
    }
    if (printBIO("key params", bio) < 0)
    {
        printf("print key params failed\n");
        goto clear;
    }
clear:
    if (pkey)
    {
        EVP_PKEY_free(pkey);
    }
    if (ctx)
    {
        EVP_PKEY_CTX_free(ctx);
    }
    if (bio)
    {
        BIO_free(bio);
    }
    return 0;
}
*******public key begins*****
 Public-Key: (2048 bit)
 Modulus:
     00:c0:06:57:34:eb:12:9a:87:b1:a9:53:6d:b8:a6:
     c1:99:6d:35:53:b3:c0:01:c0:5b:1b:1f:8e:ca:ec:
     28:9f:29:d3:62:b0:fa:f9:38:72:f7:14:18:1b:fa:
     0c:28:45:2b:c2:39:35:2e:9f:c3:f2:47:a8:ba:5b:
     53:c6:db:be:d1:6d:67:d3:15:6b:d0:6d:db:5a:1f:
     2b:70:1e:8d:30:eb:12:29:9f:83:e0:0f:3b:a3:76:
     2e:12:61:16:d8:34:54:cd:a6:7e:71:3d:68:67:13:
     b1:37:95:66:24:c3:3e:2e:6a:b2:f1:a2:9c:a7:24:
     44:58:21:28:b5:b1:8c:32:a6:77:44:be:11:9b:df:
     74:77:36:2f:06:2b:8e:52:47:67:10:26:ef:d1:2b:
     42:f7:6c:b0:bb:98:36:0e:bd:1e:a2:58:68:20:7c:
     35:e9:55:c5:6c:bb:c8:e1:6e:99:d4:ec:f3:59:14:
     ea:3a:03:36:20:95:90:e9:ad:8b:09:49:9b:65:b2:
     65:14:00:05:4a:c1:bb:15:6d:92:78:2c:a1:44:f9:
     9a:59:28:ab:6c:73:5e:65:cb:81:20:8c:62:2f:3a:
     3b:7a:da:a1:84:e3:d4:84:e5:9f:38:e5:ae:22:3f:
     92:03:42:ce:72:49:6c:f9:7f:9a:98:ce:82:94:22:
     c0:2f
 Exponent: 65537 (0x10001)
********public key ends*****
*******private key begins*****
 Private-Key: (2048 bit)
 modulus:
     00:c0:06:57:34:eb:12:9a:87:b1:a9:53:6d:b8:a6:
     c1:99:6d:35:53:b3:c0:01:c0:5b:1b:1f:8e:ca:ec:
     28:9f:29:d3:62:b0:fa:f9:38:72:f7:14:18:1b:fa:
     0c:28:45:2b:c2:39:35:2e:9f:c3:f2:47:a8:ba:5b:
     53:c6:db:be:d1:6d:67:d3:15:6b:d0:6d:db:5a:1f:
     2b:70:1e:8d:30:eb:12:29:9f:83:e0:0f:3b:a3:76:
     2e:12:61:16:d8:34:54:cd:a6:7e:71:3d:68:67:13:
     b1:37:95:66:24:c3:3e:2e:6a:b2:f1:a2:9c:a7:24:
     44:58:21:28:b5:b1:8c:32:a6:77:44:be:11:9b:df:
     74:77:36:2f:06:2b:8e:52:47:67:10:26:ef:d1:2b:
     42:f7:6c:b0:bb:98:36:0e:bd:1e:a2:58:68:20:7c:
     35:e9:55:c5:6c:bb:c8:e1:6e:99:d4:ec:f3:59:14:
     ea:3a:03:36:20:95:90:e9:ad:8b:09:49:9b:65:b2:
     65:14:00:05:4a:c1:bb:15:6d:92:78:2c:a1:44:f9:
     9a:59:28:ab:6c:73:5e:65:cb:81:20:8c:62:2f:3a:
     3b:7a:da:a1:84:e3:d4:84:e5:9f:38:e5:ae:22:3f:
     92:03:42:ce:72:49:6c:f9:7f:9a:98:ce:82:94:22:
     c0:2f
 publicExponent: 65537 (0x10001)
 privateExponent:
     64:9f:6d:f7:36:ef:6a:b6:8c:6c:74:61:5c:8f:55:
     87:b6:68:69:52:19:dd:51:14:ec:a3:9e:c9:ca:f2:
     86:64:fe:76:0a:d3:98:e0:f1:ad:73:84:26:b4:e2:
     c5:ff:69:2c:bf:99:94:b2:f7:94:ef:e4:04:b3:72:
     b5:13:d5:6c:1c:ee:78:f8:12:a5:07:cc:17:ef:99:
     6d:89:34:e7:5b:67:0b:14:a1:a0:39:9b:40:ce:de:
     d9:96:b2:c7:dc:b4:4b:87:61:5e:a9:95:19:de:81:
     d0:e4:a2:bf:c4:e7:c2:9e:fa:e2:10:1a:12:a3:b8:
     84:8d:eb:ad:3a:3a:83:17:39:59:4d:9b:8c:76:73:
     0d:8c:dc:e0:dd:21:0d:af:45:4a:29:57:e1:04:df:
     9e:9c:26:dc:72:7b:5b:e5:5c:a7:0f:d7:db:f0:fe:
     2b:3d:b5:f8:00:5c:ee:1f:b2:ca:04:8d:3a:94:26:
     86:08:1a:b1:1e:9b:df:81:82:ae:d9:db:6f:74:9a:
     dc:7c:f9:f0:f0:62:c4:2a:c6:89:84:e5:9d:5e:da:
     54:d4:c5:da:2d:fd:b2:6a:ab:d7:d4:ee:8d:bf:d7:
     e5:43:ef:e8:d8:88:0b:0d:13:58:26:45:d9:3a:1f:
     ee:d9:74:dd:f2:75:cb:1c:1b:35:57:c6:3d:d4:2a:
     a1
 prime1:
     00:ff:70:89:15:d8:db:20:f9:38:05:bc:83:7a:c0:
     da:29:ed:96:6e:21:0b:65:1e:cd:dd:a6:76:3e:13:
     d0:39:54:9b:bb:0c:d4:c6:65:34:ee:b2:66:5b:6a:
     c3:c9:f1:ab:96:20:8f:4a:e1:b1:67:bc:79:aa:9d:
     33:9f:2d:23:1f:4a:b1:7b:b1:1b:43:7a:f8:35:b7:
     b1:c3:70:85:b2:4e:00:24:d0:3c:67:58:7f:19:19:
     51:95:da:99:23:b9:c9:db:6c:da:8c:91:08:8f:02:
     af:5a:82:e6:84:c1:73:95:b9:3f:0f:e6:79:76:18:
     e3:69:c8:75:ac:ba:8e:dc:97
 prime2:
     00:c0:72:30:62:9a:41:8f:78:72:85:8f:88:c8:8a:
     66:f8:46:75:d5:97:a3:76:5a:4c:a2:cd:9e:aa:f5:
     4c:a2:e5:7d:96:ff:94:93:38:99:d4:7e:61:f9:51:
     e5:b5:cc:a0:eb:ae:70:d4:ce:8a:c8:50:63:c8:60:
     34:8c:8b:81:19:43:cf:0d:d8:60:68:6f:06:b9:59:
     66:08:65:a0:78:2f:12:af:e7:c7:9d:67:9b:e4:54:
     17:88:96:77:96:48:20:c3:ba:1f:d4:36:64:37:b8:
     97:56:47:82:91:7d:8a:92:5a:bb:64:cc:9c:7c:6b:
     52:a8:66:e3:ab:73:bb:74:29
 exponent1:
     1d:c3:2a:f5:d4:b2:93:80:f6:3a:14:e0:ec:e8:93:
     82:d7:1f:f3:ec:fd:ba:e6:7f:ee:17:7b:27:78:7f:
     86:6e:89:46:9e:14:1c:99:a6:35:39:ac:1b:72:b1:
     25:52:77:3f:a2:c3:8f:62:04:9d:a5:a4:37:70:e1:
     96:e6:57:75:49:ea:ac:12:d2:ae:66:21:8d:80:bd:
     c1:9b:d6:b8:13:c6:88:83:8a:c6:2f:d5:7f:43:27:
     f2:61:e9:fd:f3:a3:c5:61:45:85:a3:50:6f:f9:4c:
     1d:2b:ab:58:44:82:07:2b:f2:61:13:e5:8b:11:13:
     4e:3c:01:1a:03:12:aa:39
 exponent2:
     3c:38:7f:63:9b:99:c0:67:94:8e:ae:e7:c2:b9:6c:
     97:6c:95:1b:0d:a4:9d:38:8c:9a:b5:75:5d:51:fd:
     75:4c:13:0c:d9:55:88:ec:41:56:f9:65:77:3b:65:
     82:8a:52:20:ee:29:68:ae:69:47:5d:c0:9b:c7:5f:
     2a:4b:cd:4e:36:a5:e8:53:39:77:f4:a6:4a:01:eb:
     a7:dd:05:66:16:74:e6:8e:c5:2c:40:6b:cb:fb:74:
     91:cd:64:06:a6:f3:79:cc:18:81:b3:b3:90:ac:36:
     cf:4c:f9:52:c5:d2:43:12:19:69:e5:8d:62:71:c3:
     1f:e7:72:7f:08:6c:34:61
 coefficient:
     00:db:d4:8c:80:bc:17:3f:f5:68:51:da:d3:a9:8d:
     01:50:e4:5d:a5:c1:bc:55:20:83:64:b5:2b:0e:cb:
     a6:b5:d6:8f:0c:7c:d8:c4:2e:a8:72:ed:dc:a3:d3:
     a7:73:be:05:33:27:bd:59:7e:01:e1:9f:88:08:73:
     ee:ee:9a:1e:cf:19:28:1e:dc:b8:0d:ef:1b:50:91:
     d3:0b:f9:93:6a:52:99:9f:8d:74:80:9b:51:87:0e:
     c2:b3:a9:7f:d5:79:1c:4b:88:bc:5e:16:42:f2:92:
     e9:d8:78:de:68:1f:ef:43:24:6a:ad:3d:30:c3:76:
     f5:c1:4f:94:9b:df:83:ce:9e
********private key ends*****
*******key params begins*****
 Parameters algorithm "rsaEncryption" unsupported
********key params ends*****
在《Yes,Prime Minister》里有大概这么一句台词,大英帝国的首相说: I am supposed to lead the people, and that is why I have to follow them. 这个大概就是今天所谓民主社会的困局,民主选举是选出人民的领袖来作为领导,可是缺乏leadership的职业政客完全没有任何的领袖气质,只是懂得逐选票而居的游牧投机家。如果人民可以替代领袖来决策,那么何必要选举,不妨事事都公投?难道未来的科技进步就是为了这个超级民主体制而准备的?

二月五日 等待变化等待机会

照猫画虎式的把manpage的例子代码稍微的实践了一下:
这个函数基本上没有什么意义,因为我过了两天才明白参数是digest

int signTest(EVP_PKEY *pkey, const unsigned char* msg, size_t msgSize, unsigned char** sig, size_t* sigSize)
{
    if (pkey == NULL || msg == NULL || msgSize == 0 || sig == NULL || sigSize== NULL)
    {
        printf("invalid parameter!\n");
        return -1;
    }
    int result = 0;
    EVP_PKEY_CTX *ctx = NULL;
    ctx = EVP_PKEY_CTX_new(pkey, NULL); //a very different ctx from key generation
    if (!ctx)
    {
        result = ERR_get_error();
        printf("EVP_PKEY_CTX_new error: %s\n", ERR_error_string(result, NULL));
        goto clear;
    }
    if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
    {
        result = ERR_get_error();
        printf("EVP_PKEY_CTX_set_rsa_padding error: %s\n", ERR_error_string(result, NULL));
        goto clear;
    }
    if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0)
    {
        result = ERR_get_error();
        printf("EVP_PKEY_CTX_set_signature_md error: %s\n", ERR_error_string(result, NULL));
        goto clear;
    }

    /* Determine buffer length */
if (EVP_PKEY_sign(ctx, NULL, sigSize, msg, msgSize) <= 0)
    {
        result = ERR_get_error();
        printf("EVP_PKEY_sign error: %s\n", ERR_error_string(result, NULL));
        goto clear;
    }

    *sig = OPENSSL_malloc(sigSize);
    if (*sig == NULL)
    {
        result = ERR_get_error();
        printf("OPENSSL_malloc error: %s\n", ERR_error_string(result, NULL));
        goto clear;
    }
    if (EVP_PKEY_sign(ctx, *sig, sigSize, msg, msgSize) <= 0)
    {
        OPENSSL_free(*sig);
        result = ERR_get_error();
        printf("OPENSSL_malloc error: %s\n", ERR_error_string(result, NULL));
        goto clear;
    }
clear:
    if (ctx)
    {
        EVP_PKEY_CTX_free(ctx);
    }
    return result;
}
这个小函数是从openssl的manpage里扩展而来,可是我遇到了一些问题,似乎ctx是不可以互换的,需要再研究测试一下。我花了很多的时间在纠缠__FILE__这个标准的宏,很多帖子在讨论怎样获得其文件名不带路径。实际上这个是__BASE_FILE__
__FILE__ This macro expands to the name of the current input file, in the form of a C string constant. This is the path by which the preprocessor opened the file, not the short name specified in ‘#include’ or as the input file name argument. For example, "/usr/local/include/myheader.h" is a possible expansion of this macro.
而我遇到的恰恰是在eclipse里得到的是相对路径而非绝对路径,经过一些探查我发现这个宏的定义实际上是makefile在传递给gcc的时候的代码文件名,也就是说传递给gcc的参数而已,所以,eclipse通过一个一致性的makefile的设置导致传递给gcc的../src/src.c这样的文件名,所以,我的代码里也就获得了这样的__FILE__的值。

二月七日 等待变化等待机会

我花了两天时间才开始意识到我也同样的被文档所误导,没有意识到EVP_PKEY_sign的传入参数不是用户的数据而是它的digest,这个实际上对于熟悉加密算法的人其实是显而易见的,所以,我犯了这个错误只能证明我的无知,当然对于与我犯了相似错误的人没有任何的不敬的意思,我只是对于自己的不满而已。其实那位同道所领悟的远远比我多。

二月八日 等待变化等待机会

对于private/public key的长度我一直不是很清楚才导致怀疑我的程序有错误,因为公钥比私钥的pem文件小的多。

/*
 * digestTest.c
 *
 *  Created on: Feb 7, 2018
 *      Author: nick
 */
/*
 ============================================================================
 Name        : keyGenTest.c
 Author      : Nick Huang
 Version     : 1.0
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/bio.h>

#define HANDLE_ERR(func)\
    printf("%s: %s\n", #func, ERR_reason_error_string(ERR_get_error())); result = -1; goto clear;

/* Bogus key and IV: we'd normally set these from
 * another source.
 */
unsigned char key[EVP_MAX_KEY_LENGTH] = "01234567890123456789012345678901";
unsigned char iv[EVP_MAX_IV_LENGTH] = "0123456789012345";
#define CIPHER EVP_aes_256_cbc()[1]

const char* StrPrivateKeyFileName="privateKey.pem";
const char* StrPublicKeyFileName="publicKey.pem";
const char* StrPrivateKeyTextFileName="privateKey.txt";
const char* StrPublicKeyTextFileName="publicKey.txt";
const char* StrParamTextFileName="param.txt";

const char* StrCipherFileName="cipher.txt";
const char* StrPlainFileName="plain.txt";


int printBIO(const char* strCtx, BIO* bio)
{
    char buffer[2048+1];
    int num = 0;
    printf("*******%s begins*****\n", strCtx);
    while ((num = BIO_gets(bio, buffer, 2048))>0)
    {
        buffer[num] = '\0';
        printf("%s", buffer);
    }
    printf("********%s ends*****\n", strCtx);
    if (num < 0)
    {
        printf("BIO_gets error: %s\n", ERR_reason_error_string(ERR_get_error()));
        return -1;
    }
    return 0;
}

int printKey(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
{
    int result = 0;
    BIO* bio = NULL;
    bio = BIO_new(BIO_s_mem());
    if (bio == NULL)
    {
        HANDLE_ERR(BIO_new);
    }
    if (EVP_PKEY_print_public(bio, pkey, 1, NULL)!= 1)
    {
        HANDLE_ERR(EVP_PKEY_print_public);
    }
    if (printBIO("public key", bio) < 0)
    {
        HANDLE_ERR(printBIO);
    }

    if (EVP_PKEY_print_private(bio, pkey, 1, NULL)!= 1)
    {
        HANDLE_ERR(EVP_PKEY_print_private);
    }
    if (printBIO("private key", bio) < 0)
    {
        HANDLE_ERR(printBIO);
    }

    if (EVP_PKEY_print_params(bio, pkey, 1, NULL)!= 1)
    {
        HANDLE_ERR(EVP_PKEY_print_params);
    }
    if (printBIO("key params", bio) < 0)
    {
        HANDLE_ERR(printBIO);
    }
clear:
    if (bio)
    {
        BIO_free(bio);
    }
    return result;
}

int printKeyFile(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
{
    int result = 0;
    BIO* bio = NULL;
    bio = BIO_new_file(StrPublicKeyTextFileName, "w+b");
    if (bio == NULL)
    {
        HANDLE_ERR(BIO_new);
    }
    if (EVP_PKEY_print_public(bio, pkey, 1, NULL)!= 1)
    {
        HANDLE_ERR(EVP_PKEY_print_public);
    }
    if (bio)
    {
        BIO_free(bio);
        bio = NULL;
    }
    bio = BIO_new_file(StrPrivateKeyTextFileName, "w+b");
    if (bio == NULL)
    {
        HANDLE_ERR(BIO_new);
    }
    if (EVP_PKEY_print_private(bio, pkey, 1, NULL)!= 1)
    {
        HANDLE_ERR(EVP_PKEY_print_private);
    }

    if (bio)
    {
       BIO_free(bio);
       bio = NULL;
    }
    bio = BIO_new_file(StrParamTextFileName, "w+b");
    if (bio == NULL)
    {
        HANDLE_ERR(BIO_new);
    }

    if (EVP_PKEY_print_params(bio, pkey, 1, NULL)!= 1)
    {
        HANDLE_ERR(EVP_PKEY_print_params);
    }
clear:
    if (bio)
    {
        BIO_free(bio);
    }
    return result;
}
int createKey(unsigned int nBits, EVP_PKEY **ppkey)
{
    int result = 0;
    ERR_load_crypto_strings();
    EVP_PKEY_CTX *ctx = NULL;

    EVP_PKEY *pkey = NULL;
    pkey = EVP_PKEY_new();
    if (pkey == NULL)
    {
        HANDLE_ERR(EVP_PKEY_new);
    }
    ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
    if (!ctx)
    {
        HANDLE_ERR(EVP_PKEY_CTX_new_id);
    }

    if (EVP_PKEY_keygen_init(ctx) <= 0)
    {
        HANDLE_ERR(EVP_PKEY_keygen_init);
    }

    if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
    {
        HANDLE_ERR(EVP_PKEY_CTX_set_rsa_padding);
    }

    if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, nBits [2]) <= 0)
    {
        HANDLE_ERR(EVP_PKEY_CTX_set_rsa_keygen_bits);
    }
     /* Generate key */
    if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
    {
        HANDLE_ERR(EVP_PKEY_keygen);
    }
    if (printKey(ctx, pkey) != 0)
    {
        HANDLE_ERR(testKey);
    }
    if (printKeyFile(ctx, pkey) != 0)
    {
        HANDLE_ERR(printKeyFile);
    }

clear:
    if (result == 0)
    {
        *ppkey = pkey;
    }
    else
    {
        EVP_PKEY_free(pkey);
    }
    if (ctx)
    {
        EVP_PKEY_CTX_free(ctx);
    }
    return result;
}

int encrypt(EVP_PKEY *pkey)
{
    const unsigned int BufferSize = 1024;
    int toEncrypt = 1;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[BufferSize], outbuf[BufferSize + EVP_MAX_BLOCK_LENGTH];
    int inlen, outlen;
    FILE* in = NULL;
    FILE* out = NULL;
    /* Don't set key or IV because we will modify the parameters */
    EVP_CIPHER_CTX ctx;

    in = fopen(__FILE__, "r+b");
    if (in == NULL)
    {
        HANDLE_ERR(fopen);
    }
    out = fopen(StrCipherFileName, "w+b");
    if (out == NULL)
    {
        HANDLE_ERR(fopen);
    }
    EVP_CIPHER_CTX_init(&ctx);
    if (EVP_CipherInit_ex(&ctx, CIPHER, NULL, NULL, NULL, toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    if (EVP_CIPHER_CTX_set_key_length(&ctx, EVP_CIPHER_key_length(CIPHER)) <= 0)
    {
        HANDLE_ERR(EVP_CIPHER_CTX_set_key_length);
    }
    /* We finished modifying parameters so now we can set key and IV */
    if (EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    printf("Length of the key is %d\n",EVP_CIPHER_key_length(CIPHER));
    printf("Length of the IV is %d\n",EVP_CIPHER_CTX_iv_length(&ctx));

    for(;;)
    {
        inlen = fread(inbuf, 1, BufferSize, in);
        if(inlen <= 0)
        {
            break;
        }
        if(EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen) <= 0)
        {
            HANDLE_ERR(EVP_CipherUpdate);
        }
        if (fwrite(outbuf, 1, outlen, out) != outlen)
        {
            HANDLE_ERR(fwrite);
        }
    }
    outlen = EVP_MAX_BLOCK_LENGTH;[3]
    if(EVP_CipherFinal_ex(&ctx, outbuf, &outlen) <= 0)
    {
        HANDLE_ERR(EVP_CipherFinal_ex);
    }
    if (fwrite(outbuf, 1, outlen, out) != outlen)
    {
        HANDLE_ERR(fwrite);
    }
clear:
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (in)
    {
        fclose(in);
    }
    if (out)
    {
        fclose(out);
    }
    return result;
}

int decrypt(EVP_PKEY *pkey)
{
    const unsigned int BufferSize = 1024;
    int toEncrypt = 0;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[BufferSize], outbuf[BufferSize + EVP_MAX_BLOCK_LENGTH];
    int inlen, outlen;
    FILE* in = NULL;
    FILE* out = NULL;
    /* Don't set key or IV because we will modify the parameters */
    EVP_CIPHER_CTX ctx;

    in = fopen(StrCipherFileName, "r+b");
    if (in == NULL)
    {
        HANDLE_ERR(fopen);
    }
    out = fopen(StrPlainFileName, "w+b");
    if (out == NULL)
    {
        HANDLE_ERR(fopen);
    }
    EVP_CIPHER_CTX_init(&ctx);
    if (EVP_CipherInit_ex(&ctx, CIPHER, NULL, NULL, NULL, toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    if (EVP_CIPHER_CTX_set_key_length(&ctx, EVP_CIPHER_key_length(CIPHER)) <= 0)
    {
        HANDLE_ERR(EVP_CIPHER_CTX_set_key_length);
    }
    /* We finished modifying parameters so now we can set key and IV */
    if (EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    printf("Length of the key is %d\n",EVP_CIPHER_key_length(CIPHER));
    printf("Length of the IV is %d\n",EVP_CIPHER_CTX_iv_length(&ctx));

    for(;;)
    {
        inlen = fread(inbuf, 1, BufferSize, in);
        if(inlen <= 0)
        {
            break;
        }
        if(EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen) <= 0)
        {
            HANDLE_ERR(EVP_CipherUpdate);
        }
        if (fwrite(outbuf, 1, outlen, out) != outlen)
        {
            HANDLE_ERR(fwrite);
        }
    }
    outlen = EVP_MAX_BLOCK_LENGTH;[3]
    if(EVP_CipherFinal_ex(&ctx, outbuf, &outlen) <= 0)
    {
        HANDLE_ERR(EVP_CipherFinal_ex);
    }
    if (fwrite(outbuf, 1, outlen, out) != outlen)
    {
        HANDLE_ERR(fwrite);
    }
clear:
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (in)
    {
        fclose(in);
    }
    if (out)
    {
        fclose(out);
    }
    return result;
}

int verify()
{
    const unsigned int BufferSize = 1024;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[BufferSize], outbuf[BufferSize];
    int inlen, outlen;
    FILE* in = NULL;
    FILE* out = NULL;


    in = fopen(__FILE__, "r+b");
    if (in == NULL)
    {
        HANDLE_ERR(fopen);
    }
    out = fopen(StrPlainFileName, "r+b");
    if (out == NULL)
    {
        HANDLE_ERR(fopen);
    }

    for(;;)
    {
        inlen = fread(inbuf, 1, BufferSize, in);

        outlen =fread(outbuf, 1, BufferSize, out);
        if (inlen != outlen)
        {
            HANDLE_ERR(EVP_CipherFinal_ex);
        }
        if (inlen == 0)
        {
            break;
        }
        if (memcmp(inbuf, outbuf, inlen) != 0)
        {
            HANDLE_ERR(memcmp);
        }
    }

clear:
    if (in)
    {
        fclose(in);
    }
    if (out)
    {
        fclose(out);
    }
    return result;
}

int saveKey(EVP_PKEY *pkey)
{
    int result = 0;
    FILE* fp = NULL;
    fp = fopen(StrPublicKeyFileName, "w+b");
    if (fp == NULL)
    {
        HANDLE_ERR(fopen);
    }
    if (PEM_write_PUBKEY(fp, pkey) <= 0)
    {
        HANDLE_ERR(PEM_write_PUBKEY);
    }
    fclose(fp);
    fp = NULL;
    fp = fopen(StrPrivateKeyFileName, "w+b");
    if (fp == NULL)
    {
        HANDLE_ERR(fopen);
    }
    if (PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL) <= 0)
    {
        HANDLE_ERR(PEM_write_PrivateKey);
    }
    fclose(fp);
    fp = NULL;
clear:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return result;
}

int loadKey(EVP_PKEY **ppkey)
{
    int result = 0;
    FILE* fp = NULL;
    fp = fopen(StrPublicKeyFileName, "r+b");
    if (fp == NULL)
    {
        HANDLE_ERR(fopen);
    }
    if (PEM_read_PUBKEY(fp, ppkey, NULL, NULL) == NULL)
    {
        HANDLE_ERR(PEM_read_PUBKEY);
    }
    fclose(fp);
    fp = NULL;
    fp = fopen(StrPrivateKeyFileName, "r+b");
    if (fp == NULL)
    {
        HANDLE_ERR(fopen);
    }
    if (PEM_read_PrivateKey(fp, ppkey, NULL, NULL) == NULL)
    {
        HANDLE_ERR(PEM_read_PrivateKey);
    }
    fclose(fp);
    fp = NULL;
clear:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return result;
}

int main()
{
    EVP_PKEY *pkey = NULL;
    if (createKey(2048, &pkey) == 0)
    {
        printf("createKey succeeds!\n");
        if (encrypt(pkey) == 0)
        {
            printf("1. encrypt succeeds\n");
            if (decrypt(pkey) == 0)
            {
                printf("1. decrypt succeeds\n");
                if (verify() == 0)
                {
                    printf("1. verify succeeds\n");
                }
            }

        }
        if (saveKey(pkey) == 0)
        {
            EVP_PKEY_free(pkey);
            pkey = NULL;
            printf("saveKey succeeds\n");
            if (loadKey(&pkey) == 0)
            {
                printf("loadKey succeeds\n");
                if (encrypt(pkey) == 0)
                {
                    printf("2. encrypt succeeds\n");
                    if (decrypt(pkey) == 0)
                    {
                       printf("2. decrypt succeeds\n");
                       if (verify() == 0)
                       {
                           printf("2. verify succeeds\n");
                       }
                    }
                }
            }
        }
        OPENSSL_free(pkey);
    }
    return 0;
}
有大侠解释如此,结合RSA的基础知识非常明晰,因为公钥只需要module和public exponent也就是公钥,而私钥则包含所有的元素:

The content of the RSA private key is as follows:

-----BEGIN RSA PRIVATE KEY-----
RSAPrivateKey ::= SEQUENCE {
  version           Version,
  modulus           INTEGER,  -- n
  publicExponent    INTEGER,  -- e
  privateExponent   INTEGER,  -- d
  prime1            INTEGER,  -- p
  prime2            INTEGER,  -- q
  exponent1         INTEGER,  -- d mod (p-1)
  exponent2         INTEGER,  -- d mod (q-1)
  coefficient       INTEGER,  -- (inverse of q) mod p
  otherPrimeInfos   OtherPrimeInfos OPTIONAL
}
-----END RSA PRIVATE KEY-----

while a RSA public key contains only the following data:

-----BEGIN RSA PUBLIC KEY-----
RSAPublicKey ::= SEQUENCE {
    modulus           INTEGER,  -- n
    publicExponent    INTEGER   -- e
}
-----END RSA PUBLIC KEY-----

and this explains why the private key block is larger.

Note that a more standard format for non-RSA public keys is

-----BEGIN PUBLIC KEY-----
PublicKeyInfo ::= SEQUENCE {
  algorithm       AlgorithmIdentifier,
  PublicKey       BIT STRING
}
AlgorithmIdentifier ::= SEQUENCE {
  algorithm       OBJECT IDENTIFIER,
  parameters      ANY DEFINED BY algorithm OPTIONAL
}
-----END PUBLIC KEY-----
这个大侠给我的启示很多,他的代码值得学习

二月十二日 等待变化等待机会

如何获得当前所支持的所有的cipher列表呢?我试图找到crypto原声的支持,但是很困难,只能从openssl的app里的代码找到类似的方法,就是使用ssl的当前初始化的所有的支持的encryption的算法,查看那段代码就明白这个完全是一个一个枚举的硬代码,也就是说这openssl_init是只能依赖编译开关才能增减。

#include <openssl/err.h>
#include <openssl/ssl.h>
#define HANDLE_ERR(func)\
    printf("%s: %s\n", #func, ERR_reason_error_string(ERR_get_error())); goto clear;

int main()
{
    SSL_CTX *ctx=NULL;
    const SSL_METHOD *meth=SSLv3_client_method();<[2]
    BIO *STDout=NULL;
    SSL *ssl=NULL;
    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();
    ctx=SSL_CTX_new(meth);
    if (ctx == NULL)
    {
        HANDLE_ERR(SSL_CTX_new);
    }
    ssl=SSL_new(ctx);
    if (ssl == NULL)
    {
        HANDLE_ERR(SSL_new);
    }
    STDout=BIO_new_fp(stdout,BIO_NOCLOSE);
    if (STDout == NULL)
    {
        HANDLE_ERR(BIO_new_fp);
    }

    for (int i=0; ; i++)
    {
        SSL_CIPHER *c = NULL;
        STACK_OF(SSL_CIPHER) *sk = NULL;

        sk = SSL_get_ciphers(ssl);
        if ((sk == NULL) || (sk_SSL_CIPHER_num(sk) <= i))
        {
            BIO_printf(STDout,"number %d invalid\n", i);
            break;
        }
        c=sk_SSL_CIPHER_value(sk,i);
        if (c != NULL)
        {
            BIO_printf(STDout,"number[%d]:%s [%lu] [%s] [%d]\n", i, SSL_CIPHER_get_name(c),
                       SSL_CIPHER_get_id(c),
                       SSL_CIPHER_get_version(c),
                       SSL_CIPHER_get_bits(c, NULL));            
            const EVP_CIPHER* evp_cipher = EVP_get_cipherbynid(SSL_CIPHER_get_id(c));[1]
            if (evp_cipher)
            {
                BIO_printf(STDout, "name:[%s];nid:[%d];key:[%d];iv:[%d];block:[%d];flags:[%lu];",
                       EVP_CIPHER_name(evp_cipher),
                       EVP_CIPHER_nid(evp_cipher),
                       EVP_CIPHER_key_length(evp_cipher),
                       EVP_CIPHER_iv_length(evp_cipher),
                       EVP_CIPHER_block_size(evp_cipher),
                       EVP_CIPHER_flags(evp_cipher));
            }
        }
        else
        {
            break;
        }
    }
    BIO_printf(STDout,"\n");
clear:
    if (ctx)
    {
        SSL_CTX_free(ctx);
    }
    if (STDout)
    {
        BIO_free(STDout);
    }
    if (ssl)
    {
        SSL_free(ssl);
    }
    return 0;
}
这个结果也许是有用的。

number[0]:ECDHE-RSA-AES256-GCM-SHA384 [50380848] [TLSv1/SSLv3] [256]
number[1]:ECDHE-ECDSA-AES256-GCM-SHA384 [50380844] [TLSv1/SSLv3] [256]
number[2]:ECDHE-RSA-AES256-SHA384 [50380840] [TLSv1/SSLv3] [256]
number[3]:ECDHE-ECDSA-AES256-SHA384 [50380836] [TLSv1/SSLv3] [256]
number[4]:ECDHE-RSA-AES256-SHA [50380820] [TLSv1/SSLv3] [256]
number[5]:ECDHE-ECDSA-AES256-SHA [50380810] [TLSv1/SSLv3] [256]
number[6]:SRP-DSS-AES-256-CBC-SHA [50380834] [TLSv1/SSLv3] [256]
number[7]:SRP-RSA-AES-256-CBC-SHA [50380833] [TLSv1/SSLv3] [256]
number[8]:DHE-DSS-AES256-GCM-SHA384 [50331811] [TLSv1/SSLv3] [256]
number[9]:DHE-RSA-AES256-GCM-SHA384 [50331807] [TLSv1/SSLv3] [256]
number[10]:DHE-RSA-AES256-SHA256 [50331755] [TLSv1/SSLv3] [256]
number[11]:DHE-DSS-AES256-SHA256 [50331754] [TLSv1/SSLv3] [256]
number[12]:DHE-RSA-AES256-SHA [50331705] [TLSv1/SSLv3] [256]
number[13]:DHE-DSS-AES256-SHA [50331704] [TLSv1/SSLv3] [256]
number[14]:DHE-RSA-CAMELLIA256-SHA [50331784] [TLSv1/SSLv3] [256]
number[15]:DHE-DSS-CAMELLIA256-SHA [50331783] [TLSv1/SSLv3] [256]
number[16]:ECDH-RSA-AES256-GCM-SHA384 [50380850] [TLSv1/SSLv3] [256]
number[17]:ECDH-ECDSA-AES256-GCM-SHA384 [50380846] [TLSv1/SSLv3] [256]
number[18]:ECDH-RSA-AES256-SHA384 [50380842] [TLSv1/SSLv3] [256]
number[19]:ECDH-ECDSA-AES256-SHA384 [50380838] [TLSv1/SSLv3] [256]
number[20]:ECDH-RSA-AES256-SHA [50380815] [TLSv1/SSLv3] [256]
number[21]:ECDH-ECDSA-AES256-SHA [50380805] [TLSv1/SSLv3] [256]
number[22]:AES256-GCM-SHA384 [50331805] [TLSv1/SSLv3] [256]
number[23]:AES256-SHA256 [50331709] [TLSv1/SSLv3] [256]
number[24]:AES256-SHA [50331701] [TLSv1/SSLv3] [256]
number[25]:CAMELLIA256-SHA [50331780] [TLSv1/SSLv3] [256]
number[26]:PSK-AES256-CBC-SHA [50331789] [TLSv1/SSLv3] [256]
number[27]:ECDHE-RSA-DES-CBC3-SHA [50380818] [TLSv1/SSLv3] [168]
number[28]:ECDHE-ECDSA-DES-CBC3-SHA [50380808] [TLSv1/SSLv3] [168]
number[29]:SRP-DSS-3DES-EDE-CBC-SHA [50380828] [TLSv1/SSLv3] [168]
number[30]:SRP-RSA-3DES-EDE-CBC-SHA [50380827] [TLSv1/SSLv3] [168]
number[31]:EDH-RSA-DES-CBC3-SHA [50331670] [TLSv1/SSLv3] [168]
number[32]:EDH-DSS-DES-CBC3-SHA [50331667] [TLSv1/SSLv3] [168]
number[33]:ECDH-RSA-DES-CBC3-SHA [50380813] [TLSv1/SSLv3] [168]
number[34]:ECDH-ECDSA-DES-CBC3-SHA [50380803] [TLSv1/SSLv3] [168]
number[35]:DES-CBC3-SHA [50331658] [TLSv1/SSLv3] [168]
number[36]:PSK-3DES-EDE-CBC-SHA [50331787] [TLSv1/SSLv3] [168]
number[37]:ECDHE-RSA-AES128-GCM-SHA256 [50380847] [TLSv1/SSLv3] [128]
number[38]:ECDHE-ECDSA-AES128-GCM-SHA256 [50380843] [TLSv1/SSLv3] [128]
number[39]:ECDHE-RSA-AES128-SHA256 [50380839] [TLSv1/SSLv3] [128]
number[40]:ECDHE-ECDSA-AES128-SHA256 [50380835] [TLSv1/SSLv3] [128]
number[41]:ECDHE-RSA-AES128-SHA [50380819] [TLSv1/SSLv3] [128]
number[42]:ECDHE-ECDSA-AES128-SHA [50380809] [TLSv1/SSLv3] [128]
number[43]:SRP-DSS-AES-128-CBC-SHA [50380831] [TLSv1/SSLv3] [128]
number[44]:SRP-RSA-AES-128-CBC-SHA [50380830] [TLSv1/SSLv3] [128]
number[45]:DHE-DSS-AES128-GCM-SHA256 [50331810] [TLSv1/SSLv3] [128]
number[46]:DHE-RSA-AES128-GCM-SHA256 [50331806] [TLSv1/SSLv3] [128]
number[47]:DHE-RSA-AES128-SHA256 [50331751] [TLSv1/SSLv3] [128]
number[48]:DHE-DSS-AES128-SHA256 [50331712] [TLSv1/SSLv3] [128]
number[49]:DHE-RSA-AES128-SHA [50331699] [TLSv1/SSLv3] [128]
number[50]:DHE-DSS-AES128-SHA [50331698] [TLSv1/SSLv3] [128]
number[51]:DHE-RSA-SEED-SHA [50331802] [TLSv1/SSLv3] [128]
number[52]:DHE-DSS-SEED-SHA [50331801] [TLSv1/SSLv3] [128]
number[53]:DHE-RSA-CAMELLIA128-SHA [50331717] [TLSv1/SSLv3] [128]
number[54]:DHE-DSS-CAMELLIA128-SHA [50331716] [TLSv1/SSLv3] [128]
number[55]:ECDH-RSA-AES128-GCM-SHA256 [50380849] [TLSv1/SSLv3] [128]
number[56]:ECDH-ECDSA-AES128-GCM-SHA256 [50380845] [TLSv1/SSLv3] [128]
number[57]:ECDH-RSA-AES128-SHA256 [50380841] [TLSv1/SSLv3] [128]
number[58]:ECDH-ECDSA-AES128-SHA256 [50380837] [TLSv1/SSLv3] [128]
number[59]:ECDH-RSA-AES128-SHA [50380814] [TLSv1/SSLv3] [128]
number[60]:ECDH-ECDSA-AES128-SHA [50380804] [TLSv1/SSLv3] [128]
number[61]:AES128-GCM-SHA256 [50331804] [TLSv1/SSLv3] [128]
number[62]:AES128-SHA256 [50331708] [TLSv1/SSLv3] [128]
number[63]:AES128-SHA [50331695] [TLSv1/SSLv3] [128]
number[64]:SEED-SHA [50331798] [TLSv1/SSLv3] [128]
number[65]:CAMELLIA128-SHA [50331713] [TLSv1/SSLv3] [128]
number[66]:PSK-AES128-CBC-SHA [50331788] [TLSv1/SSLv3] [128]
number[67]:ECDHE-RSA-RC4-SHA [50380817] [TLSv1/SSLv3] [128]
number[68]:ECDHE-ECDSA-RC4-SHA [50380807] [TLSv1/SSLv3] [128]
number[69]:ECDH-RSA-RC4-SHA [50380812] [TLSv1/SSLv3] [128]
number[70]:ECDH-ECDSA-RC4-SHA [50380802] [TLSv1/SSLv3] [128]
number[71]:RC4-SHA [50331653] [TLSv1/SSLv3] [128]
number[72]:RC4-MD5 [50331652] [TLSv1/SSLv3] [128]
number[73]:PSK-RC4-SHA [50331786] [TLSv1/SSLv3] [128]
number[74]:EDH-RSA-DES-CBC-SHA [50331669] [TLSv1/SSLv3] [56]
number[75]:EDH-DSS-DES-CBC-SHA [50331666] [TLSv1/SSLv3] [56]
number[76]:DES-CBC-SHA [50331657] [TLSv1/SSLv3] [56]
number[77]:EXP-EDH-RSA-DES-CBC-SHA [50331668] [TLSv1/SSLv3] [40]
number[78]:EXP-EDH-DSS-DES-CBC-SHA [50331665] [TLSv1/SSLv3] [40]
number[79]:EXP-DES-CBC-SHA [50331656] [TLSv1/SSLv3] [40]
number[80]:EXP-RC2-CBC-MD5 [50331654] [TLSv1/SSLv3] [40]
number[81]:EXP-RC4-MD5 [50331651] [TLSv1/SSLv3] [40]
number 82 invalid

二月十三日 等待变化等待机会

我可能又忘记了这个语法,就是cifs的路径形式:mount -t cifs -o,user=myuser,password=mypasswd //172.17.245.230/repository repositary

二月十四日 等待变化等待机会

我花了差不多两天时间在寻找ssl_cipher和EVP_cipher之间的关联,或者说在寻找两个体系遍历的方式。前者有现成的函数,而后者我花了很久才google到,然后就忘了存那个链接于是这个就无耻的成了我自己的成果了。不过我依稀记得原来作者提到这段小代码是在原来openssl里的一段,实在是不值一提了。
可是如果没有人提示,找到这个办法还真的是不容易,因为在sort的时候的回调函数确实不容易直接想到:

static void show_ciphers(const OBJ_NAME *name,void *bio_)
{
    BIO *bio=bio_;
    if (islower(name->name[0]) || name->alias)[2]
    {
        //printf("alias: %s\n", name->name);
    }
    else
    {
        BIO_printf(bio,"%s\n",name->name);
    }
}

void collectCipherName()
{
    OpenSSL_add_all_algorithms();
    BIO* bio = BIO_new(BIO_s_mem());
    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
                           show_ciphers,
                           bio);
    char *bptr = NULL;
    int size = BIO_get_mem_data(bio, &bptr);
    BIO_set_close(bio, BIO_NOCLOSE);[1]
    printf("[%d]\n", size);
    printf("%s\n", bptr);
    BIO_free(bio);
}
我收集了这些所谓的alias:

AES128
AES192
AES256
BF
CAMELLIA128
CAMELLIA192
CAMELLIA256
CAST
CAST-cbc
DES
DES3
DESX
RC2
SEED
aes128
aes192
aes256
bf
blowfish
camellia128
camellia192
camellia256
cast
cast-cbc
des
des3
desx
rc2
seed
另一个问题是这些个cipher到底怎样和ssl_cipher来关联呢?仅仅通过名字是有些困难的。
结果如下,这些是过滤了小写和所谓的alias的部分:

AES-128-CBC
AES-128-CBC-HMAC-SHA1
AES-128-CFB
AES-128-CFB1
AES-128-CFB8
AES-128-CTR
AES-128-ECB
AES-128-OFB
AES-128-XTS
AES-192-CBC
AES-192-CFB
AES-192-CFB1
AES-192-CFB8
AES-192-CTR
AES-192-ECB
AES-192-OFB
AES-256-CBC
AES-256-CBC-HMAC-SHA1
AES-256-CFB
AES-256-CFB1
AES-256-CFB8
AES-256-CTR
AES-256-ECB
AES-256-OFB
AES-256-XTS
BF-CBC
BF-CFB
BF-ECB
BF-OFB
CAMELLIA-128-CBC
CAMELLIA-128-CFB
CAMELLIA-128-CFB1
CAMELLIA-128-CFB8
CAMELLIA-128-ECB
CAMELLIA-128-OFB
CAMELLIA-192-CBC
CAMELLIA-192-CFB
CAMELLIA-192-CFB1
CAMELLIA-192-CFB8
CAMELLIA-192-ECB
CAMELLIA-192-OFB
CAMELLIA-256-CBC
CAMELLIA-256-CFB
CAMELLIA-256-CFB1
CAMELLIA-256-CFB8
CAMELLIA-256-ECB
CAMELLIA-256-OFB
CAST5-CBC
CAST5-CFB
CAST5-ECB
CAST5-OFB
DES-CBC
DES-CFB
DES-CFB1
DES-CFB8
DES-ECB
DES-EDE
DES-EDE-CBC
DES-EDE-CFB
DES-EDE-OFB
DES-EDE3
DES-EDE3-CBC
DES-EDE3-CFB
DES-EDE3-CFB1
DES-EDE3-CFB8
DES-EDE3-OFB
DES-OFB
DESX-CBC
RC2-40-CBC
RC2-64-CBC
RC2-CBC
RC2-CFB
RC2-ECB
RC2-OFB
RC4
RC4-40
RC4-HMAC-MD5
SEED-CBC
SEED-CFB
SEED-ECB
SEED-OFB

二月十五日 等待变化等待机会

我的网络完全碎片化了,因为motorola的cable modem自带无线路由和switch功能,但是它的两个wifi都是在不同的子网里的,而我原来的两个路由器通过交换机连进网络后也只能在自己的子网,于是我不知道怎样才能访问我的扫描仪了。不过使用nmap -sn 192.168.1.0/24扫描我又找到了原来的NAS在192.168.1.6,为什么会这样呢?我猜想是我之前把synalogy的这个nas设定为固定ip,结果它就只有在192.168.1.x的这个子网内,而我的新cable modem只能够在192.168.0.1,因为其中的一个它自带的wifi在192.168.1.1,结果这个nas就只能在自己的子网里重新分配ip?总之我不是很明白怎么发生的。nmap的/24是256个ip的意思,我照猫画虎使用/16导致65536个ip去扫描。无意中发现主机的另一个网卡被分配到192.168.1.6结果我以前设置的micro_httpd居然还在工作。这个设置其实很简单直接follow它的manpage,在/etc/inetd.conf和/etc/services里各自加一行,不过要注意就是它只会找index.html的文件。为了重新启动inetd我不得不下载了inetutils-inetd。我的/etc/inetd.conf增加了一行:
micro-http      stream  tcp     nowait  nick    /usr/sbin/micro-httpd   micro-httpd /BigDisk/diabloforum/public_html
同样的在/etc/services里增加了一行:
micro-http      80/tcp
重启inetd使用inetutils-inetd这个服务。

二月十七日 等待变化等待机会

我整整花了一整天时间在寻找加密操作错误的原因,最后真的是头昏脑胀的恶心的受不了了。结果还是没有完全解决,不过我已经至少缩小了范围,就是说这三个算法我的程序肯定是不完善:"AES-128-CBC-HMAC-SHA1", "AES-256-CBC-HMAC-SHA1", "DES-EDE3-CFB1",具体的原因我打算重点的debug,也许自己编译openssl的debug版本跟踪一下吧。这里是一个比较稳定的版本,其实和之前的做法差别不大,就是说完善了一些的小的细节,比如比对文件就是比较原文和解密后的文件时候其实很罗嗦的,因为你不能因为两个文件的fread指定读取的字节数返回不同就武断说两个文件不同,因为,fread的返回字节数其实已经是有内部缓存了,还有很多的情况要处理,写起来麻烦,所以,我就直接使用diff了,因为我只关心一个yes/no的结果而已。
一不小心把源代码删除了痛心不已,还好eclipse有这样一个恢复历史记录的功能,还算是还原了一个我临时备份的版本,总算是聊胜于无啊。

#include <openssl/err.h>
#include <openssl/ssl.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

#define HANDLE_ERR(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error())); result = -1; goto clear;

static void collect_ciphers(const OBJ_NAME *name,void *vect)
{
    vector<string>* pvect = (vector<string>*) vect;
    if (islower(name->name[0]) || name->alias)
    {
        //printf("%s\n", name->name);
    }
    else
    {
        pvect->push_back(name->name);
    }
}

void collectCipherName(vector<string>& vect)
{

    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
                           collect_ciphers,
                           &vect);

}

int encrypt(const string& strName, const EVP_CIPHER* cipher, string& strKey, string& strIV)
{
    const unsigned int BufferSize = 1024;
    int toEncrypt = 1;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[BufferSize], outbuf[BufferSize + EVP_MAX_BLOCK_LENGTH];
    int inlen, outlen;
    FILE* in = NULL;
    FILE* out = NULL;
    /* Don't set key or IV because we will modify the parameters */
    EVP_CIPHER_CTX ctx;
    int keyLength = 0;
    int ivLength = 0;
    const string strFileName = "/tmp/" + strName;

    in = fopen(__FILE__, "r");
    if (in == NULL)
    {
        HANDLE_ERR(fopen);
    }
    out = fopen(strFileName.c_str(), "w");
    if (out == NULL)
    {
        HANDLE_ERR(fopen);
    }
    EVP_CIPHER_CTX_init(&ctx);
    if (EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    if (EVP_CIPHER_CTX_set_key_length(&ctx, EVP_CIPHER_key_length(cipher)) <= 0)
    {
        HANDLE_ERR(EVP_CIPHER_CTX_set_key_length);
    }
    // let's randomize key and iv
    keyLength = EVP_CIPHER_key_length(cipher);
    strKey.resize(keyLength);
    for (int i = 0; i < keyLength; i ++)
    {
        strKey[i] = random()%2==1?random()%26 + 'A':random()%26 + 'a';
    }

    ivLength = EVP_CIPHER_CTX_iv_length(&ctx);
    strIV.resize(ivLength);
    for (int i = 0; i < ivLength; i ++)
    {
        strIV[i] = random()%2==1?random()%26 + 'A':random()%26 + 'a';
    }

    /* We finished modifying parameters so now we can set key and IV */
    if (EVP_CipherInit_ex(&ctx, NULL, NULL, (const unsigned char*)strKey.c_str(), (const unsigned char*)strIV.c_str(), toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    while (!ferror(in))
    {
        inlen = fread(inbuf, 1, BufferSize, in);
        if(inlen <= 0)
        {
            if (feof(in))
            {
                break;
            }
        }
        else
        {
            if(EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen) <= 0)
            {
                HANDLE_ERR(EVP_CipherUpdate);
            }
            if (fwrite(outbuf, 1, outlen, out) != outlen)
            {
                HANDLE_ERR(fwrite);
            }
        }
    }
    if (ferror(in))
    {
        HANDLE_ERR(ferror);
    }
    if(EVP_CipherFinal_ex(&ctx, outbuf, &outlen) <= 0)
    {
        HANDLE_ERR(EVP_CipherFinal_ex);
    }
    if (fwrite(outbuf, 1, outlen, out) != outlen)
    {
        HANDLE_ERR(fwrite);
    }
clear:
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (in)
    {
        fclose(in);
    }
    if (out)
    {
        fclose(out);
    }
    fflush(NULL);
    return result;
}

int verify(const string& src, const string& tgt)
{
    string cmd = "diff -q " + src + " " + tgt;
    int ret = system(cmd.c_str());
    return WEXITSTATUS(ret);
}

int decrypt(const string& strCipherName, const EVP_CIPHER* cipher, const string& strKey, const string& strIV)
{
    const unsigned int BufferSize = 1024;
    int toEncrypt = 0;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[BufferSize], outbuf[BufferSize + EVP_MAX_BLOCK_LENGTH];
    int inlen, outlen;
    int tmpOut = 0, tmpTotal = 0;
    FILE* in = NULL;
    FILE* out = NULL;
    /* Don't set key or IV because we will modify the parameters */
    EVP_CIPHER_CTX ctx;
    const string strPlainName = "/tmp/" + strCipherName + ".txt";
    const string strCipherFileName = "/tmp/"+strCipherName;
    in = fopen(strCipherFileName.c_str(), "r");
    if (in == NULL)
    {
        HANDLE_ERR(fopen);
    }

    out = fopen(strPlainName.c_str(), "w");
    if (out == NULL)
    {
        HANDLE_ERR(fopen);
    }
    EVP_CIPHER_CTX_init(&ctx);
    if (EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, NULL, toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    if (EVP_CIPHER_CTX_set_key_length(&ctx, EVP_CIPHER_key_length(cipher)) <= 0)
    {
        HANDLE_ERR(EVP_CIPHER_CTX_set_key_length);
    }
    /* We finished modifying parameters so now we can set key and IV */
    if (EVP_CipherInit_ex(&ctx, NULL, NULL, (const unsigned char*)strKey.c_str(),
                          (const unsigned char*)strIV.c_str(),
                          toEncrypt) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    while (!ferror(in) && !ferror(out))
    {
        inlen = fread(inbuf, 1, BufferSize, in);
        if(inlen <= 0)
        {
            if (feof(in))
            {
                break;
            }
        }
        else
        {
            if(EVP_CipherUpdate(&ctx, outbuf, &outlen, inbuf, inlen) <= 0)
            {
                HANDLE_ERR(EVP_CipherUpdate);
            }
            tmpTotal = 0;
            while (tmpTotal < outlen)
            {
                tmpOut = fwrite(outbuf, 1, outlen, out);
                if (tmpOut <= 0)
                {
                    HANDLE_ERR(fwrite);
                }
                else
                {
                    tmpTotal+= tmpOut;
                    outlen -= tmpOut;
                }
            }
        }
    }
    if (ferror(in) && ferror(out))
    {
        HANDLE_ERR(ferror);
    }

    if(EVP_CipherFinal_ex(&ctx, outbuf, &outlen) <= 0)
    {
        HANDLE_ERR(EVP_CipherFinal_ex);
    }
    if (fwrite(outbuf, 1, outlen, out) != outlen)
    {
        HANDLE_ERR(fwrite);
    }
    fflush(in);
    fflush(out);
    if (verify(__FILE__, strPlainName) != 0)
    {
        HANDLE_ERR(verify);
    }
clear:
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (in)
    {
        fclose(in);
    }
    if (out)
    {
        fclose(out);
    }
    return result;
}

int main()
{
    OpenSSL_add_all_algorithms();
    vector<string> vect;
    collectCipherName(vect);
    for (size_t i = 0; i < vect.size(); i ++)
    {
        if (vect[i].compare("AES-128-CBC-HMAC-SHA1") == 0
                || vect[i].compare("AES-256-CBC-HMAC-SHA1") == 0
                || vect[i].compare("DES-EDE3-CFB1") == 0)
        {
            continue;
        }
        const EVP_CIPHER* cipher = EVP_get_cipherbyname(vect[i].c_str());
        if (cipher)
        {
            string strKey, strIV;
            if (encrypt(vect[i], cipher, strKey, strIV) == 0)
            {
                cout << "encrypt method " << vect[i] << " succeeded " << endl;
                if (decrypt(vect[i], cipher, strKey, strIV) == 0)
                {
                    cout << "decrypt method " << vect[i] << " succeeded " << endl;
                }
                else
                {
                    cout << "decrypt method " << vect[i] << " failed " << endl;
                    break;
                }
            }
            else
            {
                cout << "encrypt method " << vect[i] << " failed " << endl;
                break;
            }
        }
    }
    return 0;
}
编译指令当然是非常简单的了:

all:
	g++ -std=c++98 -g -O0 cryptoTest.cpp -o cryptoTest -lcrypto -lssl
	
clean:
	rm -f cryptoTest
下载了一个openssl的教案,也许有些价值。本地存一份

二月十八日 等待变化等待机会

这件简单的事情折腾了我许久,就是怎样编译一个openssl的静态库,同时我还想要debug build因为想要gdb看看是怎么执行的,所以,我也需要它的apps,就是openssl的binary executable。一层窗户纸捅破是不值钱的,可是我看了它的说明和wiki怎么也想不出怎么办?实际上本来是有线索的,因为我编译的静态库链接的时候总是有很多链接错误。最后我的编译命令如此:
g++ -std=c++98 -g -O0 -I/BigDisk/openssl-1.0.1f/include\
	  cryptoTest.cpp -static -L/BigDisk/openssl-1.0.1f -lssl \
	 -L/BigDisk/openssl-1.0.1f -lcrypto -ldl -o cryptoTest
但是我的openssl的apps编译不成功,这让我怀疑我遗漏了什么在config阶段,的确:
./config -d no-shared threads -lpthread
不要忘记了编译不一定支持并行编译,比如make -j2都未必成功。
我依然没有找到完整的解密AES-128-CBC-HMAC-SHA1等的方法,不过对于cipher的操作略作修改:

int encrypt(const string& strName, const EVP_CIPHER* cipher, string& strKey, string& strIV)
{
    const unsigned int BufferSize = 1024;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[BufferSize], outbuf[BufferSize + EVP_MAX_BLOCK_LENGTH];
    int inlen, outlen;
    FILE* in = NULL;
    FILE* out = NULL;
    EVP_CIPHER_CTX ctx;
    int keyLength = 0;
    int ivLength = 0;
    const string strFileName = "/tmp/" + strName;

    in = fopen(__FILE__, "r");
    if (in == NULL)
    {
        HANDLE_ERR(fopen);
    }
    out = fopen(strFileName.c_str(), "w");
    if (out == NULL)
    {
        HANDLE_ERR(fopen);
    }
    EVP_CIPHER_CTX_init(&ctx);

    // let's randomize key and iv
    keyLength = EVP_CIPHER_key_length(cipher);
    strKey.resize(keyLength);
    for (int i = 0; i < keyLength; i ++)
    {
        strKey[i] = random()%2==1?random()%26 + 'A':random()%26 + 'a';
    }

    ivLength = EVP_CIPHER_iv_length(cipher);
    strIV.resize(ivLength);
    for (int i = 0; i < ivLength; i ++)
    {
        strIV[i] = random()%2==1?random()%26 + 'A':random()%26 + 'a';
    }

    if (EVP_EncryptInit(&ctx, cipher, (const unsigned char*)strKey.c_str(), (const unsigned char*)strIV.c_str()) <= 0)
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    while (!ferror(in))
    {
        inlen = fread(inbuf, 1, BufferSize, in);
        if(inlen <= 0)
        {
            if (feof(in))
            {
                break;
            }
        }
        else
        {
            if(EVP_EncryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen) <= 0)
            {
                HANDLE_ERR(EVP_CipherUpdate);
            }
            if (fwrite(outbuf, 1, outlen, out) != outlen)
            {
                HANDLE_ERR(fwrite);
            }
        }
    }
    if (ferror(in))
    {
        HANDLE_ERR(ferror);
    }

    if(EVP_EncryptFinal(&ctx, outbuf, &outlen) <= 0)
    {
        HANDLE_ERR(EVP_CipherFinal_ex);
    }
    if (fwrite(outbuf, 1, outlen, out) != outlen)
    {
        HANDLE_ERR(fwrite);
    }
clear:
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (in)
    {
        fclose(in);
    }
    if (out)
    {
        fclose(out);
    }
    fflush(NULL);
    return result;
}

int verify(const string& src, const string& tgt)
{
    string cmd = "diff -q " + src + " " + tgt;
    int ret = system(cmd.c_str());
    return WEXITSTATUS(ret);[2]
}

int decrypt(const string& strCipherName, const EVP_CIPHER* cipher, const string& strKey, const string& strIV)
{
    const unsigned int BufferSize = 1024;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char inbuf[BufferSize], outbuf[BufferSize + EVP_MAX_BLOCK_LENGTH];
    int inlen, outlen;
    int tmpOut = 0, tmpTotal = 0;
    FILE* in = NULL;
    FILE* out = NULL;
    EVP_CIPHER_CTX ctx;
    const string strPlainName = "/tmp/" + strCipherName + ".txt";
    const string strCipherFileName = "/tmp/"+strCipherName;
    in = fopen(strCipherFileName.c_str(), "r");
    if (in == NULL)
    {
        HANDLE_ERR(fopen);
    }

    out = fopen(strPlainName.c_str(), "w");
    if (out == NULL)
    {
        HANDLE_ERR(fopen);
    }
    EVP_CIPHER_CTX_init(&ctx);

    if (EVP_DecryptInit(&ctx, cipher, (const unsigned char*)strKey.c_str(),
                          (const unsigned char*)strIV.c_str()) <= 0)[1]
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    while (!ferror(in) && !ferror(out))
    {
        inlen = fread(inbuf, 1, BufferSize, in);
        if(inlen <= 0)
        {
            if (feof(in))
            {
                break;
            }
        }
        else
        {
            if(EVP_DecryptUpdate(&ctx, outbuf, &outlen, inbuf, inlen) <= 0)
            {
                HANDLE_ERR(EVP_CipherUpdate);
            }
            tmpTotal = 0;
            while (tmpTotal < outlen)
            {
                tmpOut = fwrite(outbuf, 1, outlen, out);
                if (tmpOut <= 0)
                {
                    HANDLE_ERR(fwrite);
                }
                else
                {
                    tmpTotal+= tmpOut;
                    outlen -= tmpOut;
                }
            }
        }
    }
    if (ferror(in) && ferror(out))
    {
        HANDLE_ERR(ferror);
    }

    if(EVP_DecryptFinal(&ctx, outbuf, &outlen) <= 0)
    {
        HANDLE_ERR(EVP_CipherFinal_ex);
    }
    if (fwrite(outbuf, 1, outlen, out) != outlen)
    {
        HANDLE_ERR(fwrite);
    }
    fflush(in);
    fflush(out);
    if (verify(__FILE__, strPlainName) != 0)
    {
        HANDLE_ERR(verify);
    }
clear:
    EVP_CIPHER_CTX_cleanup(&ctx);
    if (in)
    {
        fclose(in);
    }
    if (out)
    {
        fclose(out);
    }
    return result;
}

二月十九日 等待变化等待机会

参照openssl的apps/enc重新改写了一下,加密解密的函数一致化了,而且似乎节约了一个缓存,因为输入输出的buff是同一个,这个应该是一个改进,但是对于解密的那三个cipher失败的原因还是没有发现:

#include <openssl/err.h>
#include <openssl/ssl.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

#define HANDLE_ERR(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error())); result = -1; goto clear;

static void collect_ciphers(const OBJ_NAME *name,void *vect)
{
    vector<string>* pvect = (vector<string>*) vect;
    if (islower(name->name[0]) || name->alias)
    {
        //printf("%s\n", name->name);
    }
    else
    {
        pvect->push_back(name->name);
    }
}

void collectCipherName(vector<string>& vect)
{

    OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
                           collect_ciphers,
                           &vect);
}

void getKey(const EVP_CIPHER* cipher, string& strKey, string& strIV)
{
    int keyLength = 0;
    int ivLength = 0;
    // let's randomize key and iv
    keyLength = EVP_CIPHER_key_length(cipher);
    strKey.resize(keyLength);
    for (int i = 0; i < keyLength; i ++)
    {
        strKey[i] = random()%2==1?random()%26 + 'A':random()%26 + 'a';
    }

    ivLength = EVP_CIPHER_iv_length(cipher);
    strIV.resize(ivLength);
    for (int i = 0; i < ivLength; i ++)
    {
        strIV[i] = random()%2==1?random()%26 + 'A':random()%26 + 'a';
    }
}

int verify(const string& strName)
{
    const string src = __FILE__;
    const string tgt = "/tmp/" + strName + ".txt";;
    string cmd = "diff -q " + src + " " + tgt;
    int ret = system(cmd.c_str());
    return WEXITSTATUS(ret);
}

int crypt(const string& strName, const EVP_CIPHER* cipher, string& strKey, string& strIV, int encrypt)
{
    const unsigned int BufferSize = 8*1024;
    int result = 0;
    /* Allow enough space in output buffer for additional block */
    unsigned char buf[EVP_ENCODE_LENGTH(BufferSize)];
    int inlen, outlen;
    BIO* in = NULL;
    BIO* out = NULL;
    BIO* benc = NULL;
    EVP_CIPHER_CTX* ctx = NULL;

    string strInFileName;
    string strOutFileName;

    if (encrypt)
    {
        strInFileName = __FILE__;
        strOutFileName = "/tmp/" + strName;
    }
    else
    {
        strInFileName = "/tmp/" + strName;
        strOutFileName = "/tmp/" + strName + ".txt";
    }

    in = BIO_new(BIO_s_file());
    if (in == NULL)
    {
        HANDLE_ERR(BIO_new);
    }
    if (BIO_read_filename(in,(void*)strInFileName.c_str()) <= 0)
    {
        HANDLE_ERR(BIO_read_filename);
    }
    out = BIO_new(BIO_s_file());
    if (out == NULL)
    {
        HANDLE_ERR(BIO_new);
    }
    if (BIO_write_filename(out, (void*)strOutFileName.c_str()) <= 0)
    {
        HANDLE_ERR(BIO_write_filename);
    }
    if ((benc = BIO_new(BIO_f_cipher())) == NULL)
    {
        HANDLE_ERR(BIO_new);
    }
    if (BIO_get_cipher_ctx(benc, &ctx) <= 0)
    {
        HANDLE_ERR(BIO_get_cipher_ctx);
    }

    if (!EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt))
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }
    if (!EVP_CipherInit_ex(ctx, NULL, NULL, (const unsigned char*)strKey.c_str(),
                          (const unsigned char*)strIV.c_str(), encrypt))
    {
        HANDLE_ERR(EVP_CipherInit_ex);
    }

    if ((out = BIO_push(benc,out)) == NULL)
    {
        HANDLE_ERR(BIO_push);
    }
    while (true)
    {
        inlen = BIO_read(in, (char *)buf, BufferSize);
        if (inlen <= 0)
        {
            break;
        }
        if (BIO_write(out, (char *)buf, inlen) != inlen)
        {
            HANDLE_ERR(BIO_write);
        }
    }

    if (!BIO_flush(out))
    {
        HANDLE_ERR(BIO_flush);
    }
clear:
    if (in)
    {
        BIO_free(in);
    }
    if (out)
    {
        BIO_free_all(out);
    }
//    if (benc)
//    {
//        BIO_free(benc);
//    }
    return result;
}

int main()
{
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    vector<string> vect;
    collectCipherName(vect);
    for (size_t i = 0; i < vect.size(); i ++)
    {
        if ((vect[i].compare("AES-128-CBC-HMAC-SHA1") == 0
                || vect[i].compare("AES-256-CBC-HMAC-SHA1") == 0
                || vect[i].compare("DES-EDE3-CFB1") == 0))
        {
            continue;
        }
        const EVP_CIPHER* cipher = EVP_get_cipherbyname(vect[i].c_str());
        if (cipher)
        {
            string strKey, strIV;
            getKey(cipher, strKey, strIV);
            if (crypt(vect[i], cipher, strKey, strIV, 1) == 0)
            {
                cout << "encrypt method " << vect[i] << " succeeded " << endl;
                if (crypt(vect[i], cipher, strKey, strIV, 0) == 0)
                {
                    cout << "decrypt method " << vect[i] << " succeeded " << endl;
                    if (verify(vect[i]) == 0)
                    {
                        cout << "verify method " << vect[i] << " succeeded " << endl;
                    }
                    else
                    {
                        cout << "verify method " << vect[i] << " failed " << endl;
                        break;
                    }
                }
                else
                {
                    cout << "decrypt method " << vect[i] << " failed " << endl;
                    break;
                }
            }
            else
            {
                cout << "encrypt method " << vect[i] << " failed " << endl;
                break;
            }
        }
    }
    return 0;
}
对于c/c++代码转化为html是一个看似非常简单的工作,可是细思之下并非如此简单,因为至少有很多的语法操作需要处理,可以说是一个简单的parser,我花了一点时间搜到了这个简单的工具[其实根本不能说简单!作者的思路是很不错的,而且从语法上来动手是最可靠的],当然依然有些小的瑕疵
当然都是很trivial的,比如我想直接输出到文件,同时tab不要转换,这个可能是作者疏忽了,或者个人喜好,当然首尾的html为了好看需要加回车,可是我要保真,所以去掉了,太惭愧了,这些也值得一提吗?最后一点,我的改动是不要完整的html文件,因为我只想把代码转为html嵌入我的日记部分。以下这部分就是程序自己把自己的代码转换为html
#include<fstream>
#include<string>
#include <cstring>
#include<ctype.h>
#include <cstdlib>
#include <iostream>

#define _TABSIZE	4

using namespace std;

int tabsize = _TABSIZE;

class token {
public:
	token() : _what(code) {}
protected:
	enum type {code, comment, pp, keyword};
	string _str;
	type _what;
	friend istream& operator>>(istream&, token&);
	friend ostream& operator<<(ostream&, const token&);
};

bool iskeyword(const string& s)
{
	static const char* keywords[] = {
		"and",
		"and_eq",
		"asm",
		"auto",
		"bitand",
		"bitor",
		"bool",
		"break",
		"case",
		"catch",
		"char",
		"class",
		"compl",
		"const",
		"const_cast",
		"continue",
		"default",
		"delete",
		"do",
		"double",
		"dynamic_cast",
		"else",
		"enum",
		"explicit",
		"export",
		"extern",
		"false",
		"float",
		"for",
		"friend",
		"goto",
		"if",
		"inline",
		"int",
		"long",
		"mutable",
		"namespace",
		"new",
		"not",
		"not_eq",
		"operator",
		"or",
		"or_eq",
		"private",
		"protected",
		"public",
		"register",
		"reinterpret_cast",
		"return",
		"short",
		"signed",
		"sizeof",
		"static",
		"static_cast",
		"struct",
		"switch",
		"template",
		"this",
		"throw",
		"true",
		"try",
		"typedef",
		"typeid",
		"typename",
		"union",
		"unsigned",
		"using",
		"virtual",
		"void",
		"volatile",
		"wchar_t",
		"while",
		"xor",
		"xor_eq"
	};

	for (size_t i = 0; i < sizeof(keywords) / sizeof(char*); i++)
		if (string(keywords[i]) == s)
			return true;

	return false;
}

bool containspp(const string& s)
{
	static const char* pptokens[] = {
		"define",
		"elif",
		"else",
		"endif",
		"error",
		"if",
		"ifdef",
		"ifndef",
		"include",
		"line",
		"pragma",
		"undef"
	};

	for (size_t i = 0; i < sizeof(pptokens) / sizeof(char*); i++)
		if (s.find(pptokens[i]) != string::npos)
			return true;

	return false;
}

istream& operator>>(istream& is, token& t)
{
	t._str = "", t._what = token::code;
	int c = is.get();
	switch (c) {
		case '/':
			c = is.get();
			if (c == '*') {
				t._str = "/*";
				t._what = token::comment;
				while (1) {
					c = is.get();
					if (c == EOF)
						return is.unget(), is.clear(), is;
					if (c == '/') {
						if (t._str.length() > 2 &&
							t._str[t._str.length() - 1] == '*') {
							return t._str += '/', is;
						}
					}
					t._str += (char)c;
				}
			} else if (c == '/') {
				t._str = "//";
				t._what = token::comment;
				c = is.get();
				while (c != '\n' && c != EOF) {
					t._str += (char)c;
					c = is.get();
				}
				if (c == '\n') {
					t._str += '\n';
				}
				return is;
			}
			t._str = '/';
			return is.unget(), is.clear(), is;
		case '#':
			t._str = '#';
			c = is.get();
			while (strchr(" \r\n\t", c)) {
				t._str += (char)c;
				c = is.get();
			}
			if (c == EOF)
				return is.unget(), is.clear(), is;
			while (strchr("abcdefghijklmnopqrstuvwxyz", c)) {
				t._str += (char)c;
				c = is.get();
			}
			is.unget(), is.clear();
			if (containspp(t._str))
				t._what = token::pp;
			return is;
		case '\'':
		case '"': {
			char q = (char)c;
			t._str = q;
			while (1) {
				c = is.get();
				if (c == EOF)
					return is.unget(), is.clear(), is;
				if (c == q) {
					if (t._str.length() >= 2) {
						if (!(t._str[t._str.length() - 1] == '\\' &&
							t._str[t._str.length() - 2] != '\\'))
							return t._str += q, is;
					} else {
						return t._str += q, is;
					}
				}
				t._str += (char)c;
			}
		}
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
		case 'g':
		case 'i':
		case 'l':
		case 'm':
		case 'n':
		case 'o':
		case 'p':
		case 'r':
		case 's':
		case 't':
		case 'u':
		case 'v':
		case 'w':
		case 'x':
			t._str += (char)c;
			c = is.get();
			while (isalpha(c) || isdigit(c) || c == '_') {
				t._str += (char)c;
				c = is.get();
			}
			is.unget(), is.clear();
			if (iskeyword(t._str))
				t._what = token::keyword;
			return is;
		case EOF:
			return is;
		default:
			t._str += (char)c;
			c = is.get();
			while (c != '/' && c != '#' && !strchr("abcdefgilmnoprstuvwx", c) &&
				c != '\'' && c != '"' && c != EOF) {
				t._str += (char)c;
				c = is.get();
			}
			is.unget(), is.clear();
			return is;
	}
}

string html(const string& s)
{
	string s1;
	string::size_type i;
	for (i = 0; i < s.length(); i++) {
		switch (s[i]) {
			case '&':
				s1 += "&amp;";
				break;
			case '<':
				s1 += "&lt;";
				break;
			case '>':
				s1 += "&gt;";
				break;
			case '"':
				s1 += "&quot;";
				break;
			case '\t':
				// I suspect this is correct for tab?
			    //s1.append(tabsize, ' ');
			    s1 += "&#09;";
				break;
			default:
				s1 += s[i];
		}
	}
	return s1;
}

ostream& operator<<(ostream& os, const token& t)
{
	if (t._what == token::code)
		os << html(t._str);
	else if (t._what == token::comment)
		os << "<span class=comment>" << html(t._str) << "</span>";
	else if (t._what == token::keyword)
		os << "<span class=keyword>" << html(t._str) << "</span>";
	else if (t._what == token::pp)
	    os << "<span class=pp>" << html(t._str) << "</span>";
	else
	    os << html(t._str);
	return os;
}

int main(int argc, char **argv)
{
	if (argc != 2 && argc != 3) {
		cout << "usage: cpphtml srcfile [tab size]" << endl;
		return 0;
	}
	ifstream is(argv[1]);
	if (!is.good()) {
		cerr << "bad input file" << endl;
		return -1;
	}
	if (argc == 3) {
		tabsize = atoi(argv[2]);
		if (tabsize <= 0)
			tabsize = _TABSIZE;
	}
	// let's use htm or html ext to generate new file
	string outName = argv[1];
	outName += ".htm";
	ofstream out(outName.c_str(), std::fstream::out);
	if (!out.good())
	{
	    cerr << "bad output file" << endl;
	            return -1;
	}
	//actually what I want is a embedded part of html, not whole file
//	out << "<html><head><style>";
//	out << ".keyword{color:rgb(0,0,255);}";
//	out << ".comment{color:rgb(0,128,0);}";
//	out << ".pp{color:rgb(0,0,255);}";
//	out << "</style><body>";
	out << "<pre style=\"font-family:courier;font-size:10pt\">";
	token t;
	while (is >> t) {
	    out << t;
	}
	//out << "</pre></body></html>";
	out << "</pre>";
	return 0;
}
关于pkey的部分始终不是很清楚,这里是一个学习的方法。首先,产生public key的算法有哪些呢?这个是从apps里摘录出来的,其中EVP_PKEY_ASN1_METHOD是一个不对外的结构不要试图直接访问其内部元素,而应该使用特定方法如代码:
/*
 * keyTest.cpp
 *
 *  Created on: Feb 19, 2018
 *      Author: nick
 */

#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/asn1t.h>
#include <openssl/err.h>

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;


int main()
{
    OPENSSL_init();
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    for (int i = 0; i < EVP_PKEY_asn1_get_count(); i++)
    {
        const EVP_PKEY_ASN1_METHOD * ameth = EVP_PKEY_asn1_get0(i);
        int pkeyId=0, pkeyBaseId = 0;
        const char* pinfo = NULL, *pstr = NULL;
        if (EVP_PKEY_asn1_get0_info(&pkeyId, &pkeyBaseId, NULL,
                        &pinfo, &pstr,
                            ameth) > 0)
        {
            pinfo = pinfo?pinfo:" none ";
            pstr = pstr?pstr:" none ";
            cout << pkeyId << "|" << pkeyBaseId << "|" << pinfo << "|" << pstr << endl;
        }
    }
}
其结果是有用的,也就是说这些算法是产生公钥的:

6|6|OpenSSL RSA method|RSA
19|6| none | none 
28|28|OpenSSL PKCS#3 DH method|DH
66|116| none | none 
67|116| none | none 
70|116| none | none 
113|116| none | none 
116|116|OpenSSL DSA method|DSA
408|408|OpenSSL EC algorithm|EC
855|855|OpenSSL HMAC method|HMAC
894|894|OpenSSL CMAC method|CMAC

二月二十日 等待变化等待机会

<关于pkey的产生是这样子的。首先使用昨天的简单的遍历所有algorithm的方法获得algo的名字,然后使用其nid,来获得EVP_PKEY_CTX,然后调用各自算法的EVP_PKEY_keygen,其实这个方法十有八九不成功,原因在后面,因为大部分算法都有各自的参数,这个很难统一,还不如直接调用各自算法的genkey的方法。
/*
 * keyTest.cpp
 *
 *  Created on: Feb 19, 2018
 *      Author: nick
 */
#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/asn1t.h>
#include <openssl/err.h>

#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

#define HANDLE_ERR(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error())); goto clear;

static int genpkey_cb(EVP_PKEY_CTX *ctx)
{
    char c='*';
    BIO *b = (BIO*)EVP_PKEY_CTX_get_app_data(ctx);
    int p;
    p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
    if (p == 0) c='.';
    if (p == 1) c='+';
    if (p == 2) c='*';
    if (p == 3) c='\n';
    BIO_write(b,&c,1);
    (void)BIO_flush(b);
    return 1;
}
int main()
{
    EVP_PKEY_CTX *ctx = NULL;
    BIO* out = NULL;
    EVP_PKEY *pkey = NULL;
    OPENSSL_init();
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    for (int i = 0; i < EVP_PKEY_asn1_get_count(); i++)
    {
        const EVP_PKEY_ASN1_METHOD * ameth = EVP_PKEY_asn1_get0(i);
        int pkeyId=0;
        const char* pinfo = NULL, *pstr = NULL;
        if (EVP_PKEY_asn1_get0_info(&pkeyId, NULL, NULL,
                        &pinfo, &pstr, ameth) > 0)
        {
            pinfo = pinfo?pinfo:" none ";
            pstr = pstr?pstr:" none ";
            cout << pkeyId << "|" << pinfo << "|" << pstr << endl;
            ctx = EVP_PKEY_CTX_new_id(pkeyId, NULL);
            if (!ctx)
            {
                HANDLE_ERR(EVP_PKEY_CTX_new_id);
            }
            if (EVP_PKEY_keygen_init(ctx) <= 0)
            {
                HANDLE_ERR(EVP_PKEY_keygen_init);
            }
            out = BIO_new_fp (stdout, BIO_NOCLOSE);
            if (!out)
            {
                HANDLE_ERR(BIO_new_fp);
            }
            EVP_PKEY_CTX_set_cb(ctx, genpkey_cb);
            EVP_PKEY_CTX_set_app_data(ctx, out);
            if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
            {
                HANDLE_ERR(EVP_PKEY_keygen);
            }
            if (EVP_PKEY_print_private(out, pkey, 0, NULL) <= 0)
            {
                HANDLE_ERR(EVP_PKEY_print_private);
            }
            clear:
            if (ctx)
            {
                EVP_PKEY_CTX_free(ctx);
            }
        }
    }
    return 0;
}
实际上所谓的dsa本来并不是需要什么很复杂的参数,但是对于不熟悉的使用者肯定还是摸不着头脑,这个简单的示范就是如此:

int main()
{
    unsigned char* seed = NULL;
    int seedlen = 100;
    int result = -1;
    int nbits = 2048;
    DSA* dsa = DSA_new();
    seed = (unsigned char*)malloc(seedlen);
    for (int i = 0; i < seedlen; i ++)
    {
        seed[i] = random()%26+'a';
    }
    if (DSA_generate_parameters_ex(dsa, nbits, seed, seedlen, NULL, NULL, NULL) <= 0)
    {
        cout << "failure" << endl;
    }
    else
    {
        cout << " succeed " << endl;
        result = 0;
    }
    if (result == 0)
    {
        DSA_print_fp(stdout, dsa, 4);
    }
clear:
    if (dsa)
    {
        DSA_free(dsa);
    }
    if (seed)
    {
        free(seed);
    }
    return result;
}
我为了比较rsa和dsa从我以前的练习中找到了这段代码,这个是一个实践rsa的简单的测试,不过我现在没有时间修改了,等下班回来再说吧,不过目前当然是有了更清楚的认识,就是rsa的加密是一个非常昂贵的操作,所以,不是之前的那些对称加密可以快速操作,因此它一般是作为digest的加密之类的固定大小,应该是这样子所以我才搞错了它的应用。这一点说明对于一个算法的基本认识有多重要,即便你不知道细节也能判断是否正确的使用。
今天上班发现了一个firefox不能打开设备web server的原因:
An error occurred during a connection to 172.17.95.20. Cannot communicate securely with peer: no common encryption algorithm(s). Error code: SSL_ERROR_NO_CYPHER_OVERLAP
This is actually an expected behavior as it clearly says the browser and CB500 web server does NOT share common cipher, just as it claims. The following step confirms this issue by: a) find out what cipher CB500 support b) what cipher Firefox support
  1. Using openssl to find CB500 supporting cipher:
    
    echo -n | openssl s_client -connect 172.17.95.20:443
    … (omit long output)
    New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-GCM-SHA384
    …
  2. Open firefox by typing about:config and search for “security.ssl3.dhe_rsa_aes_256_sha” and you will find there is NO security.ssl3.dhe_rsa_aes_256_sha384 exists. The current firefox only has security.ssl3.dhe_rsa_aes_256_sha instead of security.ssl3.dhe_rsa_aes_256_sha384 which is a little more secure.
怎样遍历所有的数字签名的方法呢?模仿apps的做法:
#include "openssl/ssl.h"
#include "openssl/err.h"
#include <vector>
#include <string>
#include <iostream>

using namespace std;

static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
{
    const char *mname;
    /* Skip aliases */
    if (!m)
        return;
    mname = OBJ_nid2ln(EVP_MD_type(m));
    /* Skip shortnames */
    if (strcmp(from, mname))
        return;
    /* Skip clones */
    if (EVP_MD_flags(m) & EVP_MD_FLAG_PKEY_DIGEST)
        return;
    if (strchr(mname, ' '))
        mname= EVP_MD_name(m);
    vector<string>* pvect = (vector<string>*) arg;
    pvect->push_back(mname);
}

int main(void)
{
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    BIO* bio_err = NULL;
    vector<string> vect;
    EVP_MD_do_all_sorted(list_md_fn, &vect);
    for (size_t i = 0; i < vect.size(); i ++)
    {
        cout << vect[i] << endl;
    }
    return 0;
}
这里是运行结果也许有用,不过我现在还不确定这个结果是完全的列表。

md4
md5
mdc2
ripemd160
sha
sha1
sha224
sha256
sha384
sha512
whirlpool

二月二十一日 等待变化等待机会

如何产生数字摘要呢?这是google的标准翻译digital digest,我不知道中文怎么说。函数并不是很复杂,是从openssl里面跟踪gdb学习的,其实是很好的学习方法,而且是效率很高的io操作方式,也就是说不要自己使用常规的文件函数而是尽量使用openssl的方式这样如果他们能够被优化这是必须的。就是一系列的BIO形成filter链,至少节省了buffer,如果要写入文件的话。
#include "openssl/ssl.h"
#include "openssl/err.h"
#include <vector>
#include <string>
#include <iostream>

using namespace std;

#define HANDLE_ERR(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error())); goto clear;

void digest(const string& strMD)
{
#undef BUFSIZE
#define BUFSIZE 1024*8
    unsigned char buf[BUFSIZE];
    const string strFile = __FILE__;
    BIO* in = NULL;
    BIO* bmd = NULL;
    BIO* inp = NULL;
    BIO* out = NULL;
    const EVP_MD *md=NULL;
    int i = 0, len = 0;
    in=BIO_new(BIO_s_file());
    if (!in)
    {
        HANDLE_ERR(BIO_new(BIO_s_file()));
    }
    if (BIO_read_filename(in,strFile.c_str()) <= 0)
    {
        HANDLE_ERR(BIO_read_filename);
    }
    bmd=BIO_new(BIO_f_md());
    if (!bmd)
    {
        HANDLE_ERR(BIO_new(BIO_f_md()));
    }

    out = BIO_new_fp(stdout, BIO_NOCLOSE);
    if (!out)
    {
        HANDLE_ERR(BIO_new_fp);
    }
    md = EVP_get_digestbyname(strMD.c_str());
    if (!md)
    {
        HANDLE_ERR(EVP_get_digestbyname);
    }

    if (!BIO_set_md(bmd,md))
    {
        HANDLE_ERR(BIO_set_md);
    }
    inp=BIO_push(bmd,in);
    for (;;)
    {
        i=BIO_read(inp,(char *)buf,BUFSIZE);
        if(i < 0)
        {
            HANDLE_ERR(BIO_read);
        }
        if (i == 0) break;
    }

    len = BIO_gets(inp,(char *)buf,BUFSIZE);
    if (len < 0)
    {
        HANDLE_ERR(BIO_gets);
    }
    BIO_printf(out, "%s(%s)", strFile.c_str(), EVP_MD_name(md));
    for (i=0; i<(int)len; i++)
    {
        BIO_printf(out, "%02x",buf[i]);
    }
    BIO_printf(out, "\n");
clear:
    if (in)
    {
        BIO_free(in);
    }
    if (bmd)
    {
        BIO_free(bmd);
    }
    if (out)
    {
        BIO_free(out);
    }
}
这是执行的结果,可以给大家一个概念各种数字摘要的大小。

dgstTest.cpp(MD4)7ed5abb9e73db7e0cdca8087ed47d591
dgstTest.cpp(MD5)d162fb7a450600883e470cd99d11a9bc
dgstTest.cpp(MDC2)c90fded0cbe6d85ee8fd7ed1ffb0caac
dgstTest.cpp(RIPEMD160)100d332f2b9b50fb918d486e99fe663a12bdf3d4
dgstTest.cpp(SHA)67c656842faebf31066f352d5dcd48efd3690e5b
dgstTest.cpp(SHA1)b0267c2d5f35a809d1d1b0e2d3330f3df31b9776
dgstTest.cpp(SHA224)914ad20697d74ef4f6a3ec5ac05e2c92b08bd6356e588035d784134c
dgstTest.cpp(SHA256)510df323593d778558f163ea763599dbe89450caa6a4afbcf6c8da9947e81bd1
dgstTest.cpp(SHA384)af6c9d7710bf42cb183f03cefd4c023f2113a1065b58ec54643d5601e3eb304ad8dc1c4c6c7282c846768864b5b91d90
dgstTest.cpp(SHA512)db57d634a4cbe3c3dddfb0aa5e1affc610b4e985a932e64bfe3787cfb682fdd1dcb482047baaa03816db0c5aeee652b978b7bd350c30bd0b4699b223f2cfe5ee
dgstTest.cpp(whirlpool)1d719684050d53c69e8102023fa49341bf61311a081cb369a260a30562b87d942c69923227a8ceb8d3e61086bdf595c316b37cd8c2ebdc8b1b281d1d3b6a2361
工作中无意中把oel7.3使用yum升级到了7.4,结果需要降级:yum history undo xxx。可是遇到了systemd是保护中,google发现其实无需担心,只要把/etc/yum/protected.d下面的systemd.conf删除就好。

二月二十三日 等待变化等待机会

早上写了一个小函数结果忘了,因为又在折腾网络,换回了旧的路由器一切有变了。也许是因为之前设置的静态地址有冲突链接都是出现问题,观察到我的笔记本总是被我的服务器上的dhcp干扰,在/var/lib/dhcp/下可以看到lease文件。同时有些命令很有用的比如:ip route get some-ip,为什么呢?因为今天工作中有客户在现场设置了比较复杂的网络配置,有两个网卡配置了两个网络,然后我们的服务需要监听来自一个网络的命令去查询的时候使用另一个网卡前往另一个网络的设备,那么这个命令就比较有用比traceroute有别样的功能。
Where the Black Hark falls, the Red Dragon rises. When China roars, the world trembles on its knees.

二月二十五日 等待变化等待机会

我对于dsa的部分始终没有办法正确的加载我自己定义的hash的方法,因为这是一个明显的代码错误(我不知道我为什么下载了一个比较老的1.01f的版本,也许是ubuntu的默认版本?总之,我现在开始下载官方的git来看看最新版这个错误是否被改正了,当然我100%相信这个明显的错误应该早就解决了,但是为了幻想我也能“贡献”一个bugfix,我还是想看看,虽然机会比中powerball还要小。的确我下载了最新版这个错误早已改正了。),当然是比较的cosmatic的小的typo。我是这么的改了一下。

		case EVP_PKEY_CTRL_DSA_PARAMGEN_MD:
		if (EVP_MD_type((const EVP_MD *)p2) != NID_sha1   &&
		    EVP_MD_type((const EVP_MD *)p2) != NID_sha224 &&
		    EVP_MD_type((const EVP_MD *)p2) != NID_sha256)
			{
			DSAerr(DSA_F_PKEY_DSA_CTRL, DSA_R_INVALID_DIGEST_TYPE);
			return 0;
			}
		// This is an obvious typo because md is for digest and for param gen it should be pmd
		//dctx->md = p2;
		dctx->pmd = p2;
		return 1;
关于列举所有的pkey,cipher,md的方法实际上在openssl的apps里有很明确的代码,虽然没有一个library的公开api,但是这个都不是什么了不起的方法,但凡有意愿深入学习的人根本不是问题,不过我还是花了相当的时间才意识到这个基本的事实,作为个人笔记还是值得收藏的(即便是抄袭源代码也需要一定的功力与努力吧?我一向认为如果链抄袭的能力都没有何来创新?):

#include "openssl/ssl.h"
#include "openssl/err.h"
static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
{
    if (m)
        BIO_printf((BIO*)arg, "%s\n", EVP_MD_name(m));
    else
    {
        // if you read source code, you know this is case of alias
        if (!from)
            from = "<undefined>";
        if (!to)
            to = "<undefined>";
        BIO_printf((BIO*)arg, "%s => %s\n", from, to);
    }
}
static void list_md(BIO *out)
{
    EVP_MD_do_all_sorted(list_md_fn, out);
}
static void list_cipher_fn(const EVP_CIPHER *c, const char *from, const char *to, void *arg)
{
    if (c)
        BIO_printf((BIO*)arg, "%s\n", EVP_CIPHER_name(c));
    else
    {
        if (!from)
            from = "<undefined>";
        if (!to)
            to = "<undefined>";
        BIO_printf((BIO*)arg, "%s => %s\n", from, to);
    }
}
static void list_cipher(BIO *out)
{
    EVP_CIPHER_do_all_sorted(list_cipher_fn, out);
}
static void list_pkey(BIO *out)
{
    int i;
    for (i = 0; i < EVP_PKEY_asn1_get_count(); i++)
    {
        const EVP_PKEY_ASN1_METHOD *ameth;
        int pkey_id, pkey_base_id, pkey_flags;
        const char *pinfo, *pem_str;
        ameth = EVP_PKEY_asn1_get0(i);
        EVP_PKEY_asn1_get0_info(&pkey_id, &pkey_base_id, &pkey_flags,
                        &pinfo, &pem_str, ameth);
        if (pkey_flags & ASN1_PKEY_ALIAS)
        {
            BIO_printf(out, "Name: %s\n",
                    OBJ_nid2ln(pkey_id));
            BIO_printf(out, "\tType: Alias to %s\n",
                    OBJ_nid2ln(pkey_base_id));
        }
        else
        {
            BIO_printf(out, "Name: %s\n", pinfo);
            BIO_printf(out, "\tType: %s Algorithm\n",
                pkey_flags & ASN1_PKEY_DYNAMIC ?
                    "External" : "Builtin");
            BIO_printf(out, "\tOID: %s\n", OBJ_nid2ln(pkey_id));
            BIO_printf(out, "\tOID: %d\n", (pkey_id));
            if (pem_str == NULL)
                pem_str = "(none)";
            BIO_printf(out, "\tPEM string: %s\n", pem_str);
        }
    }
}
int main(void)
{
    BIO* out = NULL;
    OPENSSL_init();
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    out = BIO_new_fp(stdout, BIO_NOCLOSE);
    if (out)
    {
        BIO_printf(out, "**************method list*****************\n");
        list_md(out);
        BIO_printf(out, "**************cipher list*****************\n");
        list_cipher(out);
        BIO_printf(out, "**************pkey list*****************\n");
        list_pkey(out);
        BIO_free(out);
    }
    return 0;
}
现在这里就是我的简单的关于dsa的pkey的paramgen的测试,这个也就是上文所说的发现1.0.1f的小typo的地方,有两个flavor,或者你采用命令行风格传递字符串参数,或者是直接使用实际的参数。

int main()
{
    BIO* out = NULL;
    EVP_PKEY* pkey = NULL;
    OPENSSL_init();
    CRYPTO_malloc_init();
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    out = BIO_new_fp(stdout, BIO_NOCLOSE);
    if (out)
    {
        EVP_PKEY* dsakey = EVP_PKEY_new();
        if (dsakey)
        {
            if (EVP_PKEY_assign_DSA(dsakey, DSA_new())>0)[1]
            {
                EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(dsakey, NULL);
                if (ctx)
                {
                    if (EVP_PKEY_paramgen_init(ctx))
                    {
                        const char* q_bits = "160";//224; 256;
                        const char* bits = "2048";
                        const char* method="sha1"; // "sha224", "sha256";
                        if (EVP_PKEY_CTX_ctrl_str(ctx, "dsa_paramgen_q_bits", q_bits)>0
                            && EVP_PKEY_CTX_ctrl_str(ctx, "dsa_paramgen_bits", bits)>0
                            && EVP_PKEY_CTX_ctrl_str(ctx, "dsa_paramgen_md", method)>0)[2]
                        {
                            if (EVP_PKEY_paramgen(ctx, &pkey)>=0)[3]
                            {
                                EVP_PKEY_print_public(out, pkey, 4, NULL);
                                EVP_PKEY_print_private(out, pkey, 4, NULL);
                            }
                        }
                    }
                    EVP_PKEY_CTX_free(ctx);
                }
            }
            EVP_PKEY_free(dsakey);
        }
        BIO_free(out);
    }
    return 0;
}}
重新编译最新的openssl的1.1.1,情况和之前的1.0.1f又有不同。./config no-asm no-deprecated --debug no-dso threads这样的编译并不能把pthread的静态库编译进去,因此我的测试程序需要-lpthread,我还没有自己研究问题在哪里。有趣的结果是之前曾经失败的那三个cipher现在ok了,但是新出现了一些cipher不正确:AES-128-OCB,AES-192-OCB,AES-256-OCB,ARIA-128-CCM,ARIA-128-GCM,ARIA-192-CCM,ARIA-192-GCM,ARIA-256-CCM,ARIA-256-GCM
折腾了大半个晚上才明白了一件事,那就是我对于两类函数的理解的偏差,对于pkey的paramgen和普通的gen究竟是什么样的使用场景呢?我一直希望能够给用户一个比较友好的方式就是不传递任何参数的情况下也能够生成一个默认的基本的pkey的产生,于是我以为我需要改造的是后者,结果我照猫画虎的改造结果是与前者类似的参数的产生过程,于是我回过头来仔细大量才意识到后者是专门为了一个情况而准备的就是,当你已经有一个现成的pkey的时候你想要再产生一个,所以,这个基本上就是用现有的pkey来复制它的参数来产生。于是我明白了我需要实现的是一个缺失的pkey_dsa_paramgen_init。我修改了或者说添加了DSA/RSA/EC的默认的paramgen/paramgen_init以便能够不需用户设定任何参数也能产生pkey,当然这个做法近似无意义,充其量就是自动化测试的方便。不过作为一种练习是不错的,只不过我的粗劣的无意义的代码完全不可能被接受。

二月二十六日 等待变化等待机会

最新版的openssl的配置似乎有些调整,不过也许是我自己的误判。这个是我现在的配置尝试:./Configure linux-x86_64 -lpthread threads no-shared no-zlib no-asm no-dso no-hw原因是因为发现no-shared和no-dso的意义似乎不一样,我也不明白为什么openssl的apps需要dso而当我已经明确no-dso,什么叫做no-shared?我的理解就是no-pic,难道不对吗?再次尝试:./config -d -v -lpthread threads no-shared no-zlib no-asm no-dso no-hw

三月二日 等待变化等待机会

关于libssh2的怎样连续执行多个命令的做法我找了很久才明白,一定要重开一个channel。
#include <cstdio>
#include <cstdlib>
#include <string>
#include "libssh2.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <iostream>

using namespace std;

#define BUFSIZE 32000

const unsigned long int BufSize=2048;

class MySSH2
{
private:
	int m_sock;
	LIBSSH2_SESSION *m_session;
	LIBSSH2_CHANNEL *m_channel;
	char m_err[1025];
	string m_strIp;
	string m_strUser;
	string m_strPasswd;
	bool bInitialized;
public:
	int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
	{
	    struct timeval timeout;
	    int rc;
	    fd_set fd;
	    fd_set *writefd = NULL;
	    fd_set *readfd = NULL;
	    int dir;

	    timeout.tv_sec = 10;
	    timeout.tv_usec = 0;

	    FD_ZERO(&fd);

	    FD_SET(socket_fd, &fd);

	    /* now make sure we wait in the correct direction */
	    dir = libssh2_session_block_directions(session);

	    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
	        readfd = &fd;

	    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
	        writefd = &fd;

	    rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);

	    return rc;
	}

	bool init_library()
	{
		if (libssh2_init (0) == 0)
		{
			bInitialized = true;
		}
	}

	bool init_socket()
	{
		if (m_sock != -1)
		{
			return true;
		}
		unsigned long hostaddr;

		struct sockaddr_in sin;

		hostaddr = inet_addr(m_strIp.c_str());

		m_sock = socket(AF_INET, SOCK_STREAM, 0);

		sin.sin_family = AF_INET;
		sin.sin_port = htons(22);
		sin.sin_addr.s_addr = hostaddr;
		if (connect(m_sock, (struct sockaddr*)(&sin),	sizeof(struct sockaddr_in)) != 0)
		{
			snprintf(m_err,1024, "failed to connect!\n");
			uninit_socket();
			return false;
		}
		return true;
	}

	bool init_session()
	{
		if (m_session != NULL)
		{
			return true;
		}
		int rc;
		if (m_sock == -1)
		{
			snprintf(m_err, 1024, "Socket is not initialized \n");
			return false;
		}
		m_session = libssh2_session_init();
		if (!m_session)
		{
			return false;
		}

		/* tell libssh2 we want it all done non-blocking */
		libssh2_session_set_blocking(m_session, 0);
		while ((rc = libssh2_session_handshake(m_session, m_sock)) == LIBSSH2_ERROR_EAGAIN)
		{
			waitsocket(m_sock, m_session);
		}
		if (rc)
		{
			snprintf(m_err, 1024, "Failure establishing SSH session: %d\n", rc);
			uninit_session();
			return false;
		}

		while ((rc = libssh2_userauth_password(m_session, m_strUser.c_str(), m_strPasswd.c_str())) == LIBSSH2_ERROR_EAGAIN)
		{
			waitsocket(m_sock, m_session);
		}
		if (rc)
		{
			snprintf(m_err, 1024,  "Authentication by password failed.\n");
			uninit_session();
			return false;
		}

		return true;
	}

	int init_channel()
	{
		if (m_channel)
		{
			return true;
		}
		if (m_sock == -1)
		{
			snprintf(m_err, 1024, "Socket is not initialized \n");
			return false;
		}
		if (m_session == NULL)
		{
			snprintf(m_err, 1024, "Session is not initialized \n");
			return false;
		}
		while( (m_channel = libssh2_channel_open_session(m_session)) == NULL &&
				           libssh2_session_last_error(m_session, NULL, NULL, 0) == LIBSSH2_ERROR_EAGAIN )
		{
			waitsocket(m_sock, m_session);
		}
		if( m_channel == NULL )
		{
			snprintf(m_err, 1024,"Error\n");
			uninit_channel();
			return false;
		}
		return true;
	}

	void uninit_library()
	{
		if (bInitialized)
		{
			libssh2_exit();
		}
	}

	void uninit_socket()
	{
		if (m_sock!= -1)
		{
			close(m_sock);
			m_sock = -1;
		}
	}

	void uninit_session()
	{
		if (m_session)
		{
			libssh2_session_disconnect(m_session, "Normal Shutdown, Thank you for playing");
			m_session = NULL;
		}
	}
	void uninit_channel()
	{
		if (m_channel)
		{
			if (m_sock != -1 && m_session != NULL)
			{
				while( libssh2_channel_close(m_channel) == LIBSSH2_ERROR_EAGAIN )
				{
					waitsocket(m_sock, m_session);
				}
			}

			libssh2_channel_free(m_channel);
			m_channel = NULL;
		}
	}

	bool do_exec(const string& strCommand, string& strResult)
	{
	    if (init_channel())
        {
	        int rc;
            while( (rc = libssh2_channel_exec(m_channel, strCommand.c_str())) == LIBSSH2_ERROR_EAGAIN )
            {
                waitsocket(m_sock, m_session);
            }
            if( rc != 0 )
            {
                snprintf(m_err, 1024, "Error\n");
                uninit_channel();
                return false;
            }
            char buffer[0x4000];
            while ((rc = libssh2_channel_read(m_channel, buffer, sizeof(buffer))) > 0 || rc == LIBSSH2_ERROR_EAGAIN)
            {
                if( rc > 0 )
                {
                    strResult.append(buffer, rc);
                }
                if( rc == LIBSSH2_ERROR_EAGAIN )
                {
                    waitsocket(m_sock, m_session);
                }
            }
            if (rc == 0)
            {
                char *exitsignal=(char *)"none";
                int exitcode = 127;
                exitcode = libssh2_channel_get_exit_status( m_channel );
                libssh2_channel_get_exit_signal(m_channel, &exitsignal, NULL, NULL, NULL, NULL, NULL);
                if (exitsignal)
                {
                    snprintf(m_err, 1024, "\nGot signal: %s\n", exitsignal);
                }
                else
                {
                    snprintf(m_err, 1024, "\nEXIT: %d\n", exitcode);
                }
                uninit_channel();
                return true;
            }
            snprintf(m_err, 1024, "libssh2_channel_read returned %d\n", rc);
            uninit_channel();
        }
		return false;
	}

	bool init()
	{
	    if (init_library())
        {
            if (init_socket())
            {
                if (init_session())
                {
                    return true;
                }
            }
        }
	    return false;
	}

	~MySSH2()
	{
		uninit_channel();
		uninit_session();
		uninit_socket();
		uninit_library();
	}

	MySSH2(const string& strIp, const string& strUser, const string& strPasswd)
	{
		m_sock = -1;
		m_session = NULL;
		m_channel = NULL;
		bInitialized = false;
		m_strIp = strIp;
		m_strUser = strUser;
		m_strPasswd = strPasswd;
		memset(m_err, 0, sizeof(m_err));
	}
};

int test1()
{
	MySSH2 ssh("192.168.1.115", "nick", "202409");
	string strResult;;
	string strCmd;
	if (ssh.init())
	{
        strCmd = "uname -a";
        strResult.clear();
        if (ssh.do_exec(strCmd, strResult))
        {
            cout << strCmd << ":" << endl << strResult << endl;
        }
        strCmd = "ls -asl";
        strResult.clear();
        if (ssh.do_exec(strCmd, strResult))
        {
           cout << strCmd << ":" << endl << strResult << endl;
        }
        strCmd = "lsb_release -a";
        strResult.clear();
        if (ssh.do_exec(strCmd, strResult))
        {
           cout << strCmd << ":" << endl << strResult << endl;
        }
	}
	return 0;
}

int main(int argc, char** argv)
{
	return test1();
}

三月四日 等待变化等待机会

我的想法还是挺幼稚的,因为我压根不熟悉ssh的协议,似乎这个是耸人听闻,但凡有基本计算机使用经验的不会没有使用过ssh客户端,那么怎么会说不熟悉呢?原因是在正式的协议本身.

三月五日 等待变化等待机会

熬夜在debug一件无意义的事情,就是我又一次遇到了我在ubuntu14.04下制作的ssh keyless login不成功的问题,我记得我以前解决了,可是搜索不到了究竟我怎么解决了.ssh的安全属性的问题了。一成不变的是只有stackoverflow的帖子是最靠谱的,我相信答案就在那里,可是我还是找不到,于是只能采用作者的建议使用另一个端口来监听ssh请求来看debug3的输出:sudo /usr/sbin/sshd -d -d -d -f /etc/ssh/sshd_config -e -p 2222 本来还想编译源码来gdb可是发现困难很多,首先有相当多的开发库需要,我也不一定知道有那些是必须的,很多开发包并不常用官方不一定有。其次在sudo下跟踪sshd也有可能有很多的问题,我以前就遇到过这类的干扰。于是按照源码分析log输出就是一个基本功的比拼了。

debug1: userauth-request for user nick service ssh-connection method publickey [preauth]
debug1: attempt 1 failures 0 [preauth]
debug2: input_userauth_request: try method publickey [preauth]
debug1: test whether pkalg/pkblob are acceptable [preauth]
debug3: mm_key_allowed entering [preauth]
debug3: mm_request_send entering: type 22 [preauth]
debug3: mm_key_allowed: waiting for MONITOR_ANS_KEYALLOWED [preauth]
debug3: mm_request_receive_expect entering: type 23 [preauth]
debug3: mm_request_receive entering [preauth]
debug3: mm_request_receive entering
debug3: monitor_read: checking request 4
debug3: mm_answer_authserv: service=ssh-connection, style=, role=
debug2: monitor_read: 4 used once, disabling now
debug3: mm_request_receive entering
debug3: monitor_read: checking request 22
debug3: mm_answer_keyallowed entering
debug3: mm_answer_keyallowed: key_from_blob: 0x56368a53c2f0
debug1: temporarily_use_uid: 1000/1000 (e=0/0)
debug1: trying public key file /home/nick/.ssh/authorized_keys
debug1: fd 4 clearing O_NONBLOCK
debug1: matching key found: file /home/nick/.ssh/authorized_keys, line 1 RSA eb:a5:e7:59:7d:24:0e:ce:b4:c4:d4:f8:7f:87:d6:d9
debug1: restore_uid: 0/0
debug3: mm_answer_keyallowed: key 0x56368a53c2f0 is allowed
debug3: mm_request_send entering: type 23
debug2: userauth_pubkey: authenticated 0 pkalg ssh-rsa [preauth]
Postponed publickey for nick from 192.168.1.116 port 59340 ssh2 [preauth]
我花了很久来分析这个log,还是不知原因在哪里,sshd相当的复杂,尤其是monitor的机制我不甚了解,似乎是通讯到另一个进程?我对此很困惑,总之我放弃了。我找到了以前的笔记。在重启ssh服务的时候发现不能重新绑定22端口,这个是这样发现的: sudo netstat -tapn|grep 22。注意,使用-tap的话你没有转为numeric port你只能grep ssh。

三月六日 等待变化等待机会

关于libssh2的读写我做了一个测试程序。
#include <cstdio>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>
#include "libssh2.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <iostream>
#include <pthread.h>
#include "boost/algorithm/string.hpp"


using namespace std;


time_t startTime, endTime;

#define PROFILE_NOW(context) \
  cout <<"&&&&&&&&&"<<context<<"[" << time(&endTime)-startTime<<"]&&&&&&&&&"<<endl;startTime=endTime;

static bool smashInvalidChar (char c)
{
    return !(c>=0 && c <128);
}

class MySSH2
{
private:
    int m_sock;
    LIBSSH2_SESSION *m_session;
    LIBSSH2_CHANNEL *m_channel;
    char m_err[1025];
    string m_strIp;
    string m_strUser;
    string m_strPasswd;
    int m_timeout;
    bool bInitialized;
public:
    int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
    {
        int rc;
        fd_set fd;
        fd_set *writefd = NULL;
        fd_set *readfd = NULL;
        int dir;
        struct timeval timeout;
        timeout.tv_sec = m_timeout;
        timeout.tv_usec = 0;

        FD_ZERO(&fd);
        FD_SET(socket_fd, &fd);
        /* now make sure we wait in the correct direction */
        dir = libssh2_session_block_directions(session);
        if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
            readfd = &fd;

        if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
            writefd = &fd;

        rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
        return rc;
    }

    bool init_library()
    {
        if (libssh2_init(0) == 0)
        {
            cout << "libssh2 version:"<< libssh2_version(1) << endl;
            bInitialized = true;
        }
    }

    bool init_socket()
    {
        if (m_sock != -1)
        {
            return true;
        }
        unsigned long hostaddr;

        struct sockaddr_in sin;

        hostaddr = inet_addr(m_strIp.c_str());

        m_sock = socket(AF_INET, SOCK_STREAM, 0);

        sin.sin_family = AF_INET;
        sin.sin_port = htons(22);
        sin.sin_addr.s_addr = hostaddr;
        if (connect(m_sock, (struct sockaddr*)(&sin),   sizeof(struct sockaddr_in)) != 0)
        {
            snprintf(m_err,1024, "failed to connect!\n");
            uninit_socket();
            return false;
        }
        return true;
    }

    bool init_session()
    {
        if (m_session != NULL)
        {
            return true;
        }
        int rc;
        if (m_sock == -1)
        {
            snprintf(m_err, 1024, "Socket is not initialized \n");
            return false;
        }
        m_session = libssh2_session_init();
        if (!m_session)
        {
            return false;
        }

        /* tell libssh2 we want it all done non-blocking */
        libssh2_session_set_blocking(m_session, 0);
        while ((rc = libssh2_session_handshake(m_session, m_sock)) == LIBSSH2_ERROR_EAGAIN)
        {
            waitsocket(m_sock, m_session);
        }
        if (rc)
        {
            snprintf(m_err, 1024, "Failure establishing SSH session: %d\n", rc);
            uninit_session();
            return false;
        }

        while ((rc = libssh2_userauth_password(m_session, m_strUser.c_str(), m_strPasswd.c_str())) == LIBSSH2_ERROR_EAGAIN)
        {
            waitsocket(m_sock, m_session);
        }
        if (rc)
        {
            snprintf(m_err, 1024,  "Authentication by password failed.\n");
            uninit_session();
            return false;
        }

        return true;
    }

    bool init_channel()
    {
        if (m_channel)
        {
            return true;
        }
        if (m_sock == -1)
        {
            snprintf(m_err, 1024, "Socket is not initialized \n");
            return false;
        }
        if (m_session == NULL)
        {
            snprintf(m_err, 1024, "Session is not initialized \n");
            return false;
        }
        while( (m_channel = libssh2_channel_open_session(m_session)) == NULL &&
                           libssh2_session_last_error(m_session, NULL, NULL, 0) == LIBSSH2_ERROR_EAGAIN )
        {
            waitsocket(m_sock, m_session);
        }
        if( m_channel == NULL )
        {
            snprintf(m_err, 1024,"Error\n");
            uninit_channel();
            return false;
        }
        return true;
    }
    bool init_channel_tty()
    {
        if (m_channel)
        {
            return true;
        }
        if (m_sock == -1)
        {
            snprintf(m_err, 1024, "Socket is not initialized \n");
            return false;
        }
        if (m_session == NULL)
        {
            snprintf(m_err, 1024, "Session is not initialized \n");
            return false;
        }
        while( (m_channel = libssh2_channel_open_session(m_session)) == NULL &&
                           libssh2_session_last_error(m_session, NULL, NULL, 0) == LIBSSH2_ERROR_EAGAIN )
        {
            if (waitsocket(m_sock, m_session) == 0)
            {
                return false;
            }
        }
        if( m_channel == NULL )
        {
            snprintf(m_err, 1024,"Error\n");
            return false;
        }
        while( libssh2_channel_request_pty_ex(m_channel, "vt102",
                (int)strlen("vt102"), NULL, 0,
                LIBSSH2_TERM_WIDTH, LIBSSH2_TERM_HEIGHT,
                LIBSSH2_TERM_WIDTH_PX, LIBSSH2_TERM_HEIGHT_PX) == LIBSSH2_ERROR_EAGAIN)
        {
            if (waitsocket(m_sock, m_session) == 0) // timeout
            {
                return false;
            }
        }
        while(libssh2_channel_shell(m_channel) == LIBSSH2_ERROR_EAGAIN )
        {
            if (waitsocket(m_sock, m_session) == 0)
            {
                return false;
            }
        }
        return true;
    }


    void uninit_library()
    {
        if (bInitialized)
        {
            libssh2_exit();
        }
    }

    void uninit_socket()
    {
        if (m_sock!= -1)
        {
            close(m_sock);
            m_sock = -1;
        }
    }

    void uninit_session()
    {
        if (m_session)
        {
            while (libssh2_session_disconnect(m_session, "Normal Shutdown, Thank you for playing")
                    == LIBSSH2_ERROR_EAGAIN)
            {
                if (waitsocket(m_sock, m_session) == 0)
                {
                   break; // timeout do nothing...
                }
            }
            m_session = NULL;
        }
    }
    void uninit_channel()
    {
        if (m_channel)
        {
            if (m_sock != -1 && m_session != NULL)
            {
                while(libssh2_channel_close(m_channel) == LIBSSH2_ERROR_EAGAIN )
                {
                    if (waitsocket(m_sock, m_session) == 0) // timeout
                    {
                        break;
                    }
                }
            }
            libssh2_channel_free(m_channel);
            m_channel = NULL;
        }
    }
    bool init()
    {
        if (init_library())
        {
            if (init_socket())
            {
                if (init_session())
                {
                    return true;
                }
            }
        }
        return false;
    }

    ~MySSH2()
    {
        uninit_channel();
        uninit_session();
        uninit_socket();
        uninit_library();
    }
    MySSH2(const string& strIp, const string& strUser, const string& strPasswd, int timeOut = 10);

    bool do_exec_tty(const string& strCmd, vector<string>& vResult,
            const string& strExitCond="***********impossibleconditions******************");

};

MySSH2::MySSH2(const string& strIp, const string& strUser, const string& strPasswd, int timeOut)
{
    m_sock = -1;
    m_session = NULL;
    m_channel = NULL;
    bInitialized = false;
    m_strIp = strIp;
    m_strUser = strUser;
    m_strPasswd = strPasswd;
    m_timeout = timeOut;
    memset(m_err, 0, sizeof(m_err));
}

bool MySSH2::do_exec_tty(const string& strCmd, vector<string>& vResult, const string& strExitCond)
{
    bool bResult = false;
    if (init_channel_tty())
    {
        char buffer[2048];
        size_t writeTotal = 0, readTotal = 0;
        int rc = 0;
        int writeNum = 0, readNum = 0;
        bool bWritten = false;
        bool bRead = false;
        string strResult;
        libssh2_channel_receive_window_adjust2(m_channel, sizeof(buffer), 1, NULL);[1]
        do
        {
            // write first
            while (writeTotal < strCmd.size()
                && (writeNum = libssh2_channel_write(m_channel,
                        strCmd.substr(writeTotal).c_str(),
                        strCmd.size()- writeTotal)) > 0
            )
            {
                writeTotal += writeNum;
            }
            // check on error and break
            if (writeNum != LIBSSH2_ERROR_EAGAIN && writeNum <0)
            {
                //log error
                cout << "#######################write error#####################"<< endl;
                break;
            }
            // set write finished
            if (!bWritten && writeTotal>=strCmd.size())
            {
                libssh2_channel_send_eof(m_channel);[2]
                //PROFILE_NOW("write is done");
                bWritten = true;
            }
            // read all the time
            while (readTotal < sizeof(buffer)
                 && (readNum = libssh2_channel_read(m_channel,
                            buffer+readTotal,
                            sizeof(buffer)-readTotal)) > 0
                    )
            {
                //PROFILE_NOW("readNum....");
                cout << "############read number###########"<<readNum<<"########################" << endl;
                // filter unreadable char
                string strSrc(buffer+readTotal, readNum);
                remove_copy_if(strSrc.begin(), strSrc.end(), back_inserter(strResult), smashInvalidChar);

                if (strResult.find(strExitCond) != string::npos)
                {
                    cout << "########find exit condition#############" << endl;
                    bRead = true;
                    break;
                }
                readTotal += readNum;
                // we need to restart a new buffer for reading.
                if (readTotal >= sizeof(buffer))
                {
                    readTotal = 0;
                }
            }
            if (readNum != LIBSSH2_ERROR_EAGAIN && readNum <0)
            {
                // log it
                cout << "#######################read error#####################"<< endl;
                break;
            }
            if (((!bWritten && writeNum == LIBSSH2_ERROR_EAGAIN)// 1. still need to write
                  || bWritten)  // 2. write is done
                    && readNum == LIBSSH2_ERROR_EAGAIN  // 2. reading blocked
                )[4]
            {
                if ((rc = waitsocket(m_sock, m_session)) <= 0)[3]
                {
                    // this means timeout or error!! not necessarilly!!! can be succeed!!!
                    cout << "#######################timeout#####################"<< endl;
                    bResult = true;
                    break;
                }
            }
            // success condition is either meet exit condition or server send EOF to ask us to
            if (bRead || libssh2_channel_eof(m_channel) == 1)
            {
                bResult = true;
                break;
            }
        }
        while (true);
        // will unit channel always
        uninit_channel();
        // now let's process result by filtering and splitting
        if (bResult)
        {
            // split lines into vectors  
          	boost::split(vResult, strResult, boost::is_any_of("\n\r"),boost::token_compress_on);
        }
    }
    return bResult;
}

static void display(const string& strCmd, const vector<string>& vResult)
{
    cout.flush();
    cout << strCmd << ":["<<vResult.size() << "]" << endl;
    cout << "****************************" << endl;
    for (size_t i = 0; i < vResult.size(); i ++)
    {
         cout <<"["<<i<<"]:"<< vResult[i] << endl;
    }
    cout << "****************************" << endl;
    cout.flush();
}

int quanta()
{
    // quanta smash sucks and needs bigger timeout
    MySSH2 ssh("172.17.11.14", "admin", "cmb9.admin", 30);
    vector<string> vResult;;
    string strCmd;
    if (ssh.init())
    {
        strCmd = "help\r\n";
        if (ssh.do_exec_tty(strCmd, vResult))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }

        strCmd = "version\r\n";
        if (ssh.do_exec_tty(strCmd, vResult))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
    }
    return 0;
}
int linuxSSH()
{
    MySSH2 ssh("172.17.59.177", "root", "Hitachi1");
    vector<string> vResult;;
    string strCmd;
    PROFILE_NOW("test7");
    if (ssh.init())
    {
        strCmd = "lspci\n";
        if (ssh.do_exec_tty(strCmd, vResult))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
        PROFILE_NOW("lspci");

        strCmd = "find /home\n";
        if (ssh.do_exec_tty(strCmd, vResult))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
        PROFILE_NOW("find /home");

        strCmd = "lslogins\n";
        if (ssh.do_exec_tty(strCmd, vResult))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
        PROFILE_NOW("lslogins");
    }
    return 0;
}

int hitachiSmash()
{
    MySSH2 ssh("172.17.59.165", "ceconsl", "cepasswd");
    vector<string> vResult;;
    string strCmd;
    PROFILE_NOW("test8");
    if (ssh.init())
    {
        strCmd = "show chassis setting \r exit \r";
        if (ssh.do_exec_tty(strCmd, vResult, "S0000 : Command was finished."))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
        PROFILE_NOW("show chassis setting");
        strCmd = "show sw-module mgmt-lan \r exit \r";
        if (ssh.do_exec_tty(strCmd, vResult, "S0000 : Command was finished."))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
        PROFILE_NOW("show sw-module mgmt-lan");

        strCmd = "show blade hardware 2 -h\r exit \r";
        if (ssh.do_exec_tty(strCmd, vResult, "S0000 : Command was finished."))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
        PROFILE_NOW("show blade hardware 2");
        strCmd = "show blade firmware 2 \r exit \r";
        if (ssh.do_exec_tty(strCmd, vResult, "S0000 : Command was finished."))
        {
            display(strCmd, vResult);
        }
        else
        {
            cout << "failed" << endl;
        }
        PROFILE_NOW("show blade firmware  2");
    }
    return 0;
}

int main(int argc, char** argv)
{
    quanta();
    hitachiSmash();
    linuxSSH();
    return 0;
}
这里是测试的运行结果。

libssh2 version:1.4.3-20120709

############read number###########8########################

############read number###########45########################

############read number###########8########################

############read number###########24########################

############read number###########2########################

############read number###########2########################

############read number###########18########################

############read number###########364########################

#######################timeout#####################

help

 

:[16]

****************************

[0]:help

[1]:[2J[H

[2]:                                         >> SMASHLITE Scorpio Console <<

[3]:->help

[4]:COMMAND COMPLETED : help

[5]:Command Name: help

[6]:Used to get help on commands and targets

[7]:Usage: help [-options]

[8]:Options:

[9]:         examine - used to examine the command (bypasses executer)

[10]:       help - shows help on how to use help

[11]:       output - formats the output string (should be used with format (text,clpcsv,keyword,clpxml)

[12]:                                                       like help -output format=keyword

[13]:       version - shows the smash version

[14]:->

[15]:->

****************************

############read number###########11########################

############read number###########45########################

############read number###########2########################

############read number###########1########################

############read number###########1########################

############read number###########1########################

############read number###########1########################

############read number###########1########################

############read number###########1########################

############read number###########195########################

#######################timeout#####################

version

 

:[10]

****************************

[0]:version

[1]:[2J[H

[2]:                                         >> SMASHLITE Scorpio Console <<

[3]:->version

[4]:COMMAND COMPLETED : version

[5]:*****************************************************

[6]:Smash CLP Version :SMASH 1.0.0/CLP 1.09

[7]:*****************************************************

[8]:->

[9]:->

****************************

&&&&&&&&&test8[1520389566]&&&&&&&&&

libssh2 version:1.4.3-20120709

############read number###########31########################

############read number###########189########################

############read number###########41########################

############read number###########119########################

############read number###########329########################

############read number###########55########################

############read number###########282########################

########find exit condition#############

show chassis setting

 exit

:[30]

****************************

[0]:show chassis setting

[1]: exit

[2]:[H[JHitachi Compute Blade 2500 Management Module

[3]:ALL RIGHTS RESERVED, COPYRIGHT (C) 2014,2016, HITACHI, LTD.

[4]:Chassis ID        : chassis59_165      

[5]:Firmware Revision : A0160-B-1453

[6]:chassis59_165(1)$ show chassis setting

[7]:-- chassis setting --

[8]:Chassis ID                     : chassis59_165      

[9]:Maintenance classification     : normal

[10]:WDT time-out N+M failover      : disable

[11]:-- chassis FRU setting --

[12]:Part/model number              : GG-RE4A1UBX1-Y                 

[13]:Serial number                  :                           

[14]:Model ID                       : 00

[15]:Midplane ID                    : 00

[16]:First WWN                      : 0102000000000000000000

[17]:-- Weight --

[18]:Weight [kg]                    : 120.47

[19]:-- chassis sensor information --

[20]:-- Watt --

[21]:PresentAC Power                : 433

[22]:PresentDC Power                : 372

[23]:-- Temp --

[24]:Present Ambient                : 22

[25]:-- Flow --

[26]:PresentAir Flow                : 10.21

[27]:S0002 : Command succeeded.

[28]:S0000 : Command was finished.

[29]:

****************************

&&&&&&&&&show chassis setting[4]&&&&&&&&&

############read number###########34########################

############read number###########189########################

############read number###########44########################

############read number###########383########################

########find exit condition#############

show sw-module mgmt-lan

 exit

:[20]

****************************

[0]:show sw-module mgmt-lan

[1]: exit

[2]:[H[JHitachi Compute Blade 2500 Management Module

[3]:ALL RIGHTS RESERVED, COPYRIGHT (C) 2014,2016, HITACHI, LTD.

[4]:Chassis ID        : chassis59_165      

[5]:Firmware Revision : A0160-B-1453

[6]:chassis59_165(1)$ show sw-module mgmt-lan

[7]:-- Switch module management LAN setting --

[8]:Slot            : 1

[9]:IP address      : 172.17.59.163

[10]:Subnetmask      : 255.255.255.0

[11]:Default gateway : 172.17.59.1

[12]:-- Switch module management LAN setting --

[13]:Slot            : 2

[14]:IP address      : 172.17.59.164

[15]:Subnetmask      : 255.255.255.0

[16]:Default gateway : 172.17.59.1

[17]:S0002 : Command succeeded.

[18]:S0000 : Command was finished.

[19]:

****************************

&&&&&&&&&show sw-module mgmt-lan[3]&&&&&&&&&

############read number###########34########################

############read number###########189########################

############read number###########44########################

############read number###########1229########################

############read number###########251########################

############read number###########241########################

############read number###########60########################

############read number###########10########################

############read number###########59########################

########find exit condition#############

show blade hardware 2 -h

exit

:[48]

****************************

[0]:show blade hardware 2 -h

[1]: exit

[2]:[H[JHitachi Compute Blade 2500 Management Module

[3]:ALL RIGHTS RESERVED, COPYRIGHT (C) 2014,2016, HITACHI, LTD.

[4]:Chassis ID        : chassis59_165      

[5]:Firmware Revision : A0160-B-1453

[6]:chassis59_165(1)$ show blade hardware 2 -h

[7]:-- Server blade hardware information --

[8]:Slot                                      : 2

[9]:-- Server blade --

[10]:Product name                              : Compute Blade 520HB4

[11]:Model name                                : GG-RV3XGC0B4X1-Y               

[12]:Serial number                             : 323GG-RV3XGC0B4X1-Y00000087

[13]:-- CPU --

[14]:Name                                      : Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz

[15]:Installed                                 : 2

[16]:Slots                                     : 2

[17]:-- Memory --

[18]:Memory [MB]                               : 131072

[19]:-- I/O card --

[20]:Mezzanine 1                               : -----

[21]:Mezzanine 2                               : PCI Express Adapter Connection Mezzanine Card

[22]:PCI 0                                     : -----

[23]:PCI 1                                     : -----

[24]:Daughter card 0                           : -----

[25]:-- OnBoard LAN 1 --

[26]:Type                                      : 10Gb Onboard LAN

[27]:LOM#1 MAC for Port#0                      : f8:48:97:aa:19:f0

[28]:LOM#1 MAC for Port#1                      : f8:48:97:aa:19:f4

[29]:LOM#1 MAC for Port#2                      : f8:48:97:aa:19:f8

[30]:LOM#1 MAC for Port#3                      : f8:48:97:aa:19:fc

[31]:-- LP license --

[32]:LP model                                  : Essential

[33]:Upper bound of version                    : -----

[34]:-- Power --

[35]:Nameplate power [W]                       : 429

[36]:-- Weight --

[37]:Weight [kg]                               : 7.100

[38]:-- UUID --

[39]:UUID                                      : 3d040dd2-e2ce-11e5-93b1-9521be8ac651

[40]:-- BMC MAC address --

[41]:BMC MAC address 0                         : f8:48:97:ca:58:00

[42]:BMC MAC address 1                         : f8:48:97:ca:58:01

[43]:-- LOM type --

[44]:LOM                                       : enable

[45]:S0002 : Command succeeded.

[46]:S0000 : Command was finished.

[47]:

****************************

&&&&&&&&&show blade hardware 2[4]&&&&&&&&&

############read number###########32########################

############read number###########189########################

############read number###########42########################

############read number###########568########################

########find exit condition#############

show blade firmware 2

 exit

:[26]

****************************

[0]:show blade firmware 2

[1]: exit

[2]:[H[JHitachi Compute Blade 2500 Management Module

[3]:ALL RIGHTS RESERVED, COPYRIGHT (C) 2014,2016, HITACHI, LTD.

[4]:Chassis ID        : chassis59_165      

[5]:Firmware Revision : A0160-B-1453

[6]:chassis59_165(1)$ show blade firmware 2

[7]:-- Server blade firmware version --

[8]:Slot                      : 2

[9]:-- Total version --

[10]:Current version           : 10-02(03)

[11]:Next version              : -----

[12]:-- BMC version --

[13]:Current version           : 10-02(00)

[14]:Next version              : -----

[15]:-- EFI version --

[16]:Current version           : 10-02(03)

[17]:Next version              : -----

[18]:-- HVM firmware --

[19]:Current version           : -----

[20]:Current bank              : -----

[21]:Next version              : -----

[22]:Next bank                 : -----

[23]:S0002 : Command succeeded.

[24]:S0000 : Command was finished.

[25]:

****************************

&&&&&&&&&show blade firmware  2[3]&&&&&&&&&

&&&&&&&&&test7[0]&&&&&&&&&

libssh2 version:1.4.3-20120709

############read number###########58########################

############read number###########7########################

############read number###########30########################

############read number###########1953########################

############read number###########728########################

#######################timeout#####################

lspci

:[46]

****************************

[0]:Last login: Tue Mar  6 19:05:47 2018 from 172.17.58.110

[1]:lspci

[2]:[root@OEL72-59-177 ~]# lspci

[3]:00:00.0 Host bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (rev 01)

[4]:00:01.0 PCI bridge: Intel Corporation 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge (rev 01)

[5]:00:07.0 ISA bridge: Intel Corporation 82371AB/EB/MB PIIX4 ISA (rev 08)

[6]:00:07.1 IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)

[7]:00:07.3 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)

[8]:00:07.7 System peripheral: VMware Virtual Machine Communication Interface (rev 10)

[9]:00:0f.0 VGA compatible controller: VMware SVGA II Adapter

[10]:00:10.0 SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)

[11]:00:11.0 PCI bridge: VMware PCI bridge (rev 02)

[12]:00:15.0 PCI bridge: VMware PCI Express Root Port (rev 01)

[13]:00:15.1 PCI bridge: VMware PCI Express Root Port (rev 01)

[14]:00:15.2 PCI bridge: VMware PCI Express Root Port (rev 01)

[15]:00:15.3 PCI bridge: VMware PCI Express Root Port (rev 01)

[16]:00:15.4 PCI bridge: VMware PCI Express Root Port (rev 01)

[17]:00:15.5 PCI bridge: VMware PCI Express Root Port (rev 01)

[18]:00:15.6 PCI bridge: VMware PCI Express Root Port (rev 01)

[19]:00:15.7 PCI bridge: VMware PCI Express Root Port (rev 01)

[20]:00:16.0 PCI bridge: VMware PCI Express Root Port (rev 01)

[21]:00:16.1 PCI bridge: VMware PCI Express Root Port (rev 01)

[22]:00:16.2 PCI bridge: VMware PCI Express Root Port (rev 01)

[23]:00:16.3 PCI bridge: VMware PCI Express Root Port (rev 01)

[24]:00:16.4 PCI bridge: VMware PCI Express Root Port (rev 01)

[25]:00:16.5 PCI bridge: VMware PCI Express Root Port (rev 01)

[26]:00:16.6 PCI bridge: VMware PCI Express Root Port (rev 01)

[27]:00:16.7 PCI bridge: VMware PCI Express Root Port (rev 01)

[28]:00:17.0 PCI bridge: VMware PCI Express Root Port (rev 01)

[29]:00:17.1 PCI bridge: VMware PCI Express Root Port (rev 01)

[30]:00:17.2 PCI bridge: VMware PCI Express Root Port (rev 01)

[31]:00:17.3 PCI bridge: VMware PCI Express Root Port (rev 01)

[32]:00:17.4 PCI bridge: VMware PCI Express Root Port (rev 01)

[33]:00:17.5 PCI bridge: VMware PCI Express Root Port (rev 01)

[34]:00:17.6 PCI bridge: VMware PCI Express Root Port (rev 01)

[35]:00:17.7 PCI bridge: VMware PCI Express Root Port (rev 01)

[36]:00:18.0 PCI bridge: VMware PCI Express Root Port (rev 01)

[37]:00:18.1 PCI bridge: VMware PCI Express Root Port (rev 01)

[38]:00:18.2 PCI bridge: VMware PCI Express Root Port (rev 01)

[39]:00:18.3 PCI bridge: VMware PCI Express Root Port (rev 01)

[40]:00:18.4 PCI bridge: VMware PCI Express Root Port (rev 01)

[41]:00:18.5 PCI bridge: VMware PCI Express Root Port (rev 01)

[42]:00:18.6 PCI bridge: VMware PCI Express Root Port (rev 01)

[43]:00:18.7 PCI bridge: VMware PCI Express Root Port (rev 01)

[44]:03:00.0 Ethernet controller: VMware VMXNET3 Ethernet Controller (rev 01)

[45]:[root@OEL72-59-177 ~]#

****************************

&&&&&&&&&lspci[12]&&&&&&&&&

############read number###########58########################

############read number###########12########################

############read number###########35########################

############read number###########260########################

#######################timeout#####################

find /home

:[14]

****************************

[0]:Last login: Tue Mar  6 19:11:59 2018 from 172.17.58.110

[1]:find /home

[2]:[root@OEL72-59-177 ~]# find /home

[3]:/home

[4]:/home/nick-obj-rep-59-228-exp

[5]:/home/razor

[6]:/home/razor/.bash_logout

[7]:/home/razor/.bash_profile

[8]:/home/razor/.bashrc

[9]:/home/razor/.mozilla

[10]:/home/razor/.mozilla/extensions

[11]:/home/razor/.mozilla/plugins

[12]:/home/razor/.bash_history

[13]:[root@OEL72-59-177 ~]#

****************************

&&&&&&&&&find /home[10]&&&&&&&&&

############read number###########58########################

############read number###########10########################

############read number###########33########################

############read number###########1947########################

############read number###########930########################

#######################timeout#####################

lslogins

:[53]

****************************

[0]:Last login: Tue Mar  6 19:12:09 2018 from 172.17.58.110

[1]:lslogins

[2]:[root@OEL72-59-177 ~]# lslogins

[3]:  UID USER      PROC PWD-LOCK PWD-DENY LAST-LOGIN GECOS

[4]:    0 root       158        0        0   19:12:20 root

[5]:    1 bin          0        0        1            bin

[6]:    2 daemon       0        0        1            daemon

[7]:    3 adm          0        0        1            adm

[8]:    4 lp           0        0        1            lp

[9]:    5 sync         0        0        1            sync

[10]:    6 shutdown     0        0        1 2017-Mar15 shutdown

[11]:    7 halt         0        0        1            halt

[12]:    8 mail         0        0        1            mail

[13]:   11 operator     0        0        1            operator

[14]:   12 games        0        0        1            games

[15]:   14 ftp          0        0        1            FTP User

[16]:   26 postgres     7        0        1            PostgreSQL Server

[17]:   29 rpcuser      0        0        1            RPC Service User

[18]:   32 rpc          0        0        1            Rpcbind Daemon

[19]:   38 ntp          0        0        1           

[20]:   42 gdm          0        0        1           

[21]:   59 tss          0        0        1            Account used by the trousers p

[22]:   70 avahi        0        0        1            Avahi mDNS/DNS-SD Stack

[23]:   74 sshd         0        0        1            Privilege-separated SSH

[24]:   75 radvd        0        0        1            radvd user

[25]:   81 dbus         0        0        1            System message bus

[26]:   89 postfix      1        0        1           

[27]:   99 nobody       0        0        1            Nobody

[28]:  107 qemu         0        0        1            qemu user

[29]:  113 usbmuxd      0        0        1            usbmuxd user

[30]:  170 avahi-autoipd

[31]:                   0        0        1            Avahi IPv4LL Stack

[32]:  171 pulse        0        0        1            PulseAudio System Daemon

[33]:  172 rtkit        0        0        1            RealtimeKit

[34]:  173 abrt         0        0        1           

[35]:  177 dhcpd        0        0        1            DHCP server

[36]:  990 gnome-initial-setup

[37]:                   0        0        1           

[38]:  991 unbound      0        0        1            Unbound DNS resolver

[39]:  992 colord       0        0        1            User for colord

[40]:  993 setroubleshoot

[41]:                   0        0        1           

[42]:  994 saslauth     0        0        1            Saslauthd user

[43]:  995 chrony       0        0        1           

[44]:  996 geoclue      0        0        1            User for geoclue

[45]:  997 polkitd      0        0        1            User for polkitd

[46]:  998 systemd-network

[47]:                   0        0        1            systemd Network Management

[48]:  999 systemd-bus-proxy

[49]:                   0        0        1            systemd Bus Proxy

[50]: 1000 razor        0        0        0           

[51]:65534 nfsnobody    0        0        1            Anonymous NFS User

[52]:[root@OEL72-59-177 ~]#

****************************

&&&&&&&&&lslogins[10]&&&&&&&&&

三月八日 等待变化等待机会

我基本上在学习openssl的基本使用,写的简直就好像是测试程序,但是我开始厌烦这种方式,原因是我对于evp的方式感到无助,这种努力看起来是非常的困难的,因为各种的算法如此的差别巨大强行统一的做法的意义在哪里?仿佛现实社会中的过分强调多元化的结果导致效率的下降?因为任何的行动都是如此的不一致。我想从明天起我应该挑选一些常用的我感兴趣的算法来深入实践一下。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/bio.h>


#define HANDLE_ERR(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error())); goto clear;

#define HANDLE_FAIL(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error()));continue;

int main()
{
    ERR_load_crypto_strings();
    EVP_PKEY_CTX *ctx = NULL;
    BIO* bio = NULL;
    EVP_PKEY *pkey = NULL;
    int result = -1;
    bio = BIO_new_fp(stdout, BIO_NOCLOSE);
    if (bio == NULL)
    {
        HANDLE_ERR(bio);
    }
    pkey = EVP_PKEY_new();
    if (pkey == NULL)
    {
        HANDLE_ERR(EVP_PKEY_new);
    }
    int algoArray[] = {EVP_PKEY_RSA, EVP_PKEY_RSA2, EVP_PKEY_DSA, EVP_PKEY_DSA1, EVP_PKEY_DSA2,
                    EVP_PKEY_DSA3, EVP_PKEY_DSA4, EVP_PKEY_DH, EVP_PKEY_EC};

    for (int algo = 0; algo < sizeof(algoArray)/sizeof(algoArray[0]); algo++)
    {
        if (ctx)
        {
            EVP_PKEY_CTX_free(ctx);
            ctx = NULL;
        }
        ctx = EVP_PKEY_CTX_new_id(algoArray[algo], NULL);
        if (!ctx)
        {
            HANDLE_FAIL(EVP_PKEY_CTX_new);
        }
        if (EVP_PKEY_keygen_init(ctx) <= 0)
        {
            HANDLE_FAIL(EVP_PKEY_keygen_init);
        }
        const EVP_MD * mdArray[] = {EVP_sha(), EVP_sha1(),EVP_sha224(), EVP_sha256(), EVP_sha384(), EVP_sha512()};

        for (int md = 0; md < sizeof(mdArray)/sizeof(mdArray[0]); md ++)
        {
            if (EVP_PKEY_CTX_ctrl(ctx, algoArray[algo],EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_MD, 0, (void*)mdArray[md])<=0)
            {
                HANDLE_FAIL(EVP_PKEY_CTX_ctrl);
            }
            int bitsArray[] = {256, 1024, 2048, 4096};
            for (int bits = 0; bits < sizeof(bitsArray)/sizeof(bitsArray[0]); bits ++)
            {
                if (EVP_PKEY_CTX_ctrl(ctx, algoArray[algo],EVP_PKEY_OP_KEYGEN,EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bitsArray[bits], NULL)<=0)
                {
                    HANDLE_FAIL(EVP_PKEY_CTX_ctrl);
                }
                int padArray[]={RSA_PKCS1_PADDING, RSA_SSLV23_PADDING, RSA_PKCS1_OAEP_PADDING, RSA_X931_PADDING, RSA_PKCS1_PSS_PADDING};
                for (int pad = 0; pad<= sizeof(padArray)/sizeof(padArray[0]); pad++)
                {
                    if (EVP_PKEY_CTX_ctrl(ctx, algoArray[algo],EVP_PKEY_OP_KEYGEN,EVP_PKEY_CTRL_RSA_PADDING, padArray[pad], NULL)<=0)
                    {
                        HANDLE_FAIL(EVP_PKEY_CTX_ctrl);
                    }
                    if (EVP_PKEY_keygen(ctx, &pkey)<=0)
                    {
                        HANDLE_FAIL(EVP_PKEY_keygen);
                    }
                    BIO_printf(bio, "success: algoArray[%d]mdArray[%d]bitsArray[%d]padArraypadArray[%d]\n",
                               algo, md, bits, pad);;
                }
            }
        }
    }


clear:
if (bio)
{
    BIO_free(bio);
}
if (pkey)
{
    EVP_PKEY_free(pkey);
}
if (ctx)
{
    EVP_PKEY_CTX_free(ctx);
}
    return 0;
}

三月十日 等待变化等待机会

使用libssh2读取我的shell的时候出现了一些不可读的字符,我一开始以为是什么数据的corrupt的问题,查找了一下其实是shell的颜色的字符,这个也算是一个小小的常识吧,可惜我知道的太少了。
比如这个结果010是背景颜色绿色,009是前景颜色红色:
echo -e "\\033[48;5;010;38;5;009mhello world\\033[0m"
hello world

三月十一日 等待变化等待机会

我购买了我最喜欢的电视剧<Guild>的DVD,准备抓下来保存:
for var in `ls /media/nick/THEGUILDS1/VIDEO_TS/VTS_0*_[1,2].VOB`; do index=`echo ${var}|grep -Eo '([0-9_]{5})'`; ffmpeg -y -i ${var} -vcodec h264 -acodec aac -filter_complex "[0:v][0:s]overlay" video${index}.mp4; done
单单这个regex就折腾了我一个多小时。grep没有像sed那么强大可以记住最多9个pattern group,我仅仅需要-o输出抓取的就好了。当然这个前提是我重新下载了最新的ffmpeg并且编译的时候要--enable-gpl才能够--enable-libx264,至于抓取字幕的功能显然不如HandBrake来的强大,不过我至多只有一个字幕track,应该是够了我添加了--scodec copy字幕就好了实际上我还是要用-filter_complex "[0:v][0:s]overlay"才行。这个网站对于regex简直是太棒了:https://regex101.com/
但是这个命令有问题,就是当字幕不存在时候整个失败,而且对于一些小的VOB文件根本没有必要去抓取,所以,我参考了一个大侠的探测字幕的办法,写了这么一个简单的脚本:

#!/bin/bash

season=2

for var in `find /media/nick -type f -name "VTS_*.VOB" -size +1M`
do
index=`echo ${var}|grep -Eo '([0-9_]{5})'`
hasSub=`ffmpeg -i ${var} -c copy -map 0:s -f null - -v 0 -hide_banner && echo $? || echo $?`
if [ ${hasSub} == 0 ]; 
then `ffmpeg -y -i ${var} -vcodec h264 -acodec aac -max_muxing_queue_size 999 -filter_complex "[0:v][0:s]overlay" /home/nick/volume1/video/guild/season${season}/video${index}.mp4`
else `ffmpeg -y -i ${var} -vcodec h264 -acodec aac -max_muxing_queue_size 999 /home/nick/volume1/video/guild/season${season}/video${index}.mp4`
fi 
done
这个脚本的find部分收集文件名不能应对有空格的问题,有很多的解决方法,这个是我搜集的一个。这里的核心是-printf '"%h/%f" '注意空格因为后续的for f in 语句搜索的是空格隔开的字符串,我们使用了引号,所以这个有用。
files=$(find . -name "VTS_*.VOB" -printf '"%h/%f" ');for f in "$files"; do echo "${f}"; done
另一个做法是似乎跟有效率一些,因为在每次find返回的字串中做处理,我一直在寻找这一类的做法,因为很多时候你想要终止find而不是等到所有的搜索返回一个巨大无比的数据集,以前我就遇到过find提前自爆了,因为他的返回值似乎也有上限,至少计数的整形是有上限的,而且很慢。注意这里的while read -d ''这个是循环开始了,他是针对每一个find的返回值检验是否为NULL结尾
The empty string for the delimiter means 'use the zero byte, ASCII NUL, as the delimiter' and is appropriate for parsing 'find ... -print0' output.
现在我发现这个while read -d ''对于目录名有空格似乎不起作用,难道只是对于文件名有空格才有效?
find . -name "VTS_*.VOB" -type f| while read -d '' file;
do echo "<<$file>>"; done
我发现有时候会有这样的错误:Too many packets buffered for output stream 0:1.我找到了这个解决办法,非常好。-max_muxing_queue_size 999

三月十二日 等待变化等待机会

我的扫描仪怎么工作呢?我始终无法正确设置wifi,于是只能使用usb连线然后在命令行下扫描:
hp-scan -dhpaio:/usb/Deskjet_2540_series?serial=CN48K475BT0604 -mcolor --dest=pdf -o./wire2.pdf
我发现lsdvd很小巧,可以作为我检验是否有字幕的工具,当然其中的代码很专注一件基本的功能,这个合乎我的需求。它应该是对于libdvdread的包转,当然libdvdread是libdvdnav和libdvdcss2的再包装,单单折腾这几个库是够你折腾的。令我吃惊的是dvdbackup可以成功的拷贝dvd,它当然也是一个对于以上的包装,对于探究为什么handbrake和ffmpeg抓取失败是一个线索,不过我的猜想是防拷贝的机制不再是对于硬件设备的简单契约式的返回错误,这个应该是及其早期的厂家签订协议不支持第三方驱动的简单做法,那个几十年前只有中国广东无名工厂的dvd机才能在windows下读dvd文件的时代早已过去了,我怀疑的是在解压缩过程中也许制作方添加了"盐“,我这时纯粹的瞎猜,也许完全不是?播放的解压缩和拷贝有区别吗?应该都是在dvd芯片的机制,一旦拷贝到硬盘还有什么措施能够防止解压缩?所以,这个是站不住脚的。我对于这个领域近乎一窍不通,只是内心揣测贻笑大方而已。

三月十四日 等待变化等待机会

我的确是陷入了迷茫,因为很简单的下载/更新了以前就下载的libcss。我简单的尝试了test程序检验是否是加密的,看起来不是,不过也许这个简单的测试过于乐观?总之,我看到了handbrake是使用了dvdread/dvdnav,而ffmpeg没有,我也看到有人希望集成这个但是没有相应。查看handbrake的代码似乎怎么使用很不明显,或者说代码写的是很艺术的,我没有找到直接检验dvd格式调用读的方法的代码。
看来使用handbrakecli是更加的方便,至少它集成了dvdcss2,不过你需要事先获得所有的title,我打算使用命令行工具lsdvd来作,也许handbrake的-t 0的扫描也可以,但是输出太复杂了比较困难parse。

for title in {2..25}; 
do 
./HandBrakeCLI -e x264 -E av_aac -i /dev/sr0 -o ~/volume1/video/guild/season3/video_title_${title}.mp4 -Z "High Profile" -s 1 --subtitle-burned 1 --subtitle-default 1 -t ${title}; 
done

三月十五日 等待变化等待机会

在普通人眼里dvd加密似乎是很平常的事情,从计算机的角度来看对于任何的发明都不是那么凭直觉就能想到的。这里的关于css的解说让人大开眼界,从数学算法来解释,以及代码的实践,这个是我在看libdvdcss之前的热身。我记得很久以前跟踪libdvdread的时候总是爆出一些错误,也许这个是很多年前的问题了。

三月十七日 等待变化等待机会

我厚颜无耻的把这个网站相关dvd css网页抓下来据为己有,只是个人收藏,绝无侵权剽窃的企图。我一时还找不到更好的办法就参考这里的代码
wget --spider --force-html -r -l2 $url 2>&1 \
  | grep '^--' | awk '{ print $3 }' \
  | grep -v '\.\(css\|js\|png\|gif\|jpg\)$' \
  > urls.m3u
然后我简单的过滤了一下url的链接文件,(纯手工的!)批量下载:
 wget -nd -p --clobber --convert-links -i .urls.m3u
其中--clobber我是最疑惑的,我对于--no-clobber的理解似乎正好相反,总之-nd禁止产生目录,-p是什么?我居然没有找到!
出于对大师的无限敬仰我又无耻至极的把大师的网站复制了一份,这个绝对不是为了剽窃,完全是出于广泛传播个人收藏的意思,就像我非常喜欢的网络短剧《Guild》我虽然看过几百遍,而且youtube有免费的官网我还是买了正版的dvd支持一样的道理。当然为了方便个人欣赏我也全部转录了dvd为mp4方便移动设备观看。这里是大师的官网入口。我自己保留了一份使用wget的简单功能的来的:wget -nd -p --clobber --convert-links -r http://www.bitlackeys.org 保存在这里
这里是《The Guild》的官网。这里是剧本
  1. 第一季
  2. 第二季
  3. 第三季
  4. 第四季
  5. 第五季
  6. 第六季
其实我基本上没怎么看懂css的解码算法,这个ppt就保存代表我来过这里。同样无耻的克隆一份,因为这篇文章图文并茂而且有一些链接有意思。

三月二十一日 等待变化等待机会

我应该温故而知新,重新读自己的笔记再次理解,其实以我现在的记忆力再读一次和第一次几乎一样。

三月二十三日 等待变化等待机会

我似乎是在重复之前的rsa的练习。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/bio.h>

#define HANDLE_ERR(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error())); goto clear;

#define HANDLE_FAIL(func)\
    printf("%s[%d]: %s\n", #func, __LINE__, ERR_reason_error_string(ERR_get_error()));continue;

void keyTest(const unsigned char* msg, unsigned int nsize, unsigned char* retBuf, unsigned int* retsize)
{
    ERR_load_crypto_strings();
    int nmethod = NID_md5_sha1;
    BIO* bio = NULL;
    BIGNUM *bn = NULL;
    RSA* rsa = NULL;
    int nbits = 1024;
    bio = BIO_new_fp(stdout, BIO_NOCLOSE);
    if (bio==NULL)
    {
        HANDLE_ERR(BIO_new);
    }
    rsa = RSA_new_method(NULL);
    if (rsa == NULL)
    {
        HANDLE_ERR(RSA_new);
    }
    bn = BN_new();
    if (bn == NULL)
    {
        HANDLE_ERR(BN_new);
    }
    if (BN_set_word(bn, RSA_F4)<=0)[1]
    {
        HANDLE_ERR(BN_set_word);
    }
    if (RSA_generate_key_ex(rsa, nbits, bn, NULL)<=0)[2]
    {
        HANDLE_ERR(RSA_generate_key_ex);
    }

    if (RSA_check_key(rsa)<=0)
    {
        HANDLE_ERR(RSA_check_key);
    }

    if (RSA_print(bio, rsa, 4)<=0)
    {
        HANDLE_ERR(RSA_print);
    }

    if (RSA_sign(nmethod, msg, nsize, retBuf, retsize, rsa) <= 0)
    {
        HANDLE_ERR(RSA_sign);
    }

    if (RSA_verify(nmethod, msg, nsize, retBuf, *retsize, rsa) <= 0)
    {
        HANDLE_ERR(RSA_verify);
    }

clear:
    if (bio)
    {
        BIO_free(bio);
    }
    if (bn)
    {
        BN_free(bn);
    }
    if (rsa)
    {
        RSA_free(rsa);
    }
}

int main()
{
    unsigned char msg[36];[3]
    unsigned char buf[256];
    int size = sizeof(buf);
    for (size_t i = 0; i < sizeof(msg); i++)
    {
        msg[i] = i;
    }
    keyTest(msg, sizeof(msg), buf, &size);
    printf("size: %u\n", size);
    for (size_t i = 0; i < size; i ++)
    {
        printf("%02X", buf[i]);
    }
    return 0;
}
  1. [1]:这个很关键,之前我不知道创建这个参数的意义,结果我也成功的产生了所谓的key,这个隐藏的很深,只有在verify的时候才发现这个错误。
  2. [2]:这个就是我在上面提到的问题,对于这个参数的理解很重要,它就是一个类型RSA_F4。
  3. [3]:这个也很重要的,这个sign的message的size只能是36,这个是针对md5_sha1的类型,我的猜想是16+20,前者是md5,后者是sha1,但是我不明白md5是什么的digest。同样的sign之后的size是128。

三月二十八日 等待变化等待机会

使用dnsmasq作为dhcp服务器有一个最低要求,我使用这个命令行参数配合dhcping来测试功能:sudo dnsmasq -d --dhcp-range=192.168.1.116,192.168.1.116,24h --dhcp-authoritative --port=0 --bind-interface --interface=eth0 其中--port=0是必要的因为我不想要dns服务,--dhcp-range也是必要的,我还不知道有什么替代的方法。
使用dhcping的参数硬件地址也是必须的:sudo dhcping -s 192.168.1.115 -h ec:8e:b5:9c:ac:d6 -t 5
关于dhcpinform和dhcprequest的区别我不甚了了,需要看协议。不过在K的帮助下我的问题是防火墙的问题,我又一次栽在这个问题上。同时有一个有意思的问题是我假装使用别人的mac地址被dnsmasq识破,这个是怎么做到的呢?K一开始推测是arp下的表,似乎不是。。。

三月二十九日 等待变化等待机会

今天遇到了uefi模式下ipxe不能正常启动的问题,我怀疑是ipxe的问题,决定重新编译,首先,git的proxy的设置需要这样做

四月二日 等待变化等待机会

今天研究了一下razor里面编译microkernel的过程,这个也只有旧版的帮助写的清楚,最新版的我又看不懂了。大体上是依靠标准的livecd-tool来先制作一个livecd然后有另一个工具叫做livecd-iso-to-pxeboot把iso转换成为pxe的启动文件,其实就是文件系统和initrd,不过其中在很多包的配置中加入了一个razor的client,这个是一个不太透明的东西我还没有搞明白,不过应该就是使用factor或者lspci/lshw之类的吧?我实践了一小步就是使用qemu直接启动iso,因为模拟pxe启动还需要再学习。这个第一步居然就让我头疼了好一会儿,因为我连qemu的部分模拟和全模拟都不清楚。这个地方我读了两三次都没有意识到自己的问题: qemu-system-x86_64 -boot d -cdrom ./microkernel.iso 然后就是很弱智的问题,鼠标被锁在模拟器里怎么取出来?我以前应该使用的都是sdl的环境只记得是ctrl+shft,遇到ftk这个是ctl+shft+G。这么基本的问题都不清楚说明你还嫩的很。就像这几天我对于synology的nfs的权限设置都搞不清楚,以后都不敢和别人提我曾经给nas输出管理api了。实在是无地自容,因为我还是不明白我的android手机在mount nfs的时候使用的用户到底是什么呢?能够看到文件名但是不能读取文件播放视频文件?

四月七日 等待变化等待机会

为了可持续发展的缘故决定适当的插入一些广告,希望能够得到更多的人的理解。(这个纯粹是自作多情因为没有什么人看这些垃圾只有搜索引擎而已,而这个据说googleads是不付钱的。)这个是一个很小的东西,决定使用一个简单的不能再简单的一个库htmlcxx。在插广告之前我决定先备份:rsync --exclude=/BigDisk/diabloforum/public_html/generatedTrees/ -rltogD --log-file=/tmp/rsync.log /BigDisk/diabloforum/public_html/* /home/nick/volume1/DiskStation/public_html/
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <htmlcxx/html/ParserDom.h>
#include <sys/dir.h>
using namespace std;
using namespace htmlcxx::HTML;

bool getString(const string& strFile, string& strContent)
{
    ifstream ifs(strFile.c_str(), ifstream::in);
    if (ifs.good())
    {
        stringstream ss;
        ss << ifs.rdbuf();
        strContent = ss.str();
        return true;
    }
    return false;
}

bool setString(const string& strFile, const string& strContent)
{
    ofstream ofs(strFile.c_str(), ifstream::out|ifstream::trunc);
    if (ofs.good())
    {
        ofs<< strContent;
        return true;
    }
    return false;
}

bool addAds(const string& strFile, const string& strAds)
{
    string strContent;
    if (getString(strFile, strContent))
    {
        ParserDom dom;
        const tree<Node>& tr = dom.parseTree(strContent);
        for (tree<Node>::pre_order_iterator it = tr.begin(); it != tr.end(); it++)
        {
            if (it->isTag() && it->tagName().compare("head") == 0)
            {
                size_t pos = strContent.find_first_of('>', it->offset());
                if (pos != string::npos)
                {
                    if (strContent.substr(pos+1, strAds.size()).compare(strAds) != 0)
                    {
                        strContent.insert(pos+1, strAds);
                        return setString(strFile, strContent);
                    }
                }
            }
        }
    }
    return false;
}

bool removeAds(const string& strFile, const string& strAds)
{
    string strContent;
    if (getString(strFile, strContent))
    {
        ParserDom dom;
        const tree<Node>& tr = dom.parseTree(strContent);
        for (tree<Node>::pre_order_iterator it = tr.begin(); it != tr.end(); it++)
        {
            if (it->isTag() && it->tagName().compare("head") == 0)
            {
                size_t pos = strContent.find_first_of('>', it->offset());
                if (pos != string::npos)
                {
                    if (strContent.substr(pos+1, strAds.size()).compare(strAds) == 0)
                    {
                        strContent.erase(pos+1, strAds.size());
                        return setString(strFile, strContent);
                    }
                }
            }
        }
    }
    return false;
}

bool isHtml(const string& strName)
{
    size_t pos = strName.find_last_of('.');
    if (pos != string::npos)
    {
        const string& strExt = strName.substr(pos+1);
        if (strcasecmp(strExt.c_str(), "html") == 0 || strcasecmp(strExt.c_str(), "htm") == 0)
        {
            return true;
        }
    }
    return false;
}
bool isForbidden(const string& strDir)
{
    if (strDir.compare("personal") == 0 || strDir.compare("generatedTrees") == 0)
    {
        return true;
    }
    return false;
}

bool doGetAllFiles(const string& strPath, vector<string>& vect)
{
    DIR *dir;
    if ((dir = opendir(strPath.c_str())) != NULL)
    {
        struct dirent *ent;
        while ((ent = readdir(dir)) != NULL)
        {
            switch (ent->d_type)
            {
            case DT_DIR:
                if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0)
                {
                    if (!isForbidden(ent->d_name))
                    {
                        if (!doGetAllFiles(strPath+"/"+ ent->d_name, vect))
                        {
                            return false;
                        }
                    }
                }
                break;
            case DT_REG:
                if (isHtml(ent->d_name))
                {
                    vect.push_back(strPath+"/"+ent->d_name);
                }
                break;
            default:
                break;
            }
        }
        closedir(dir);
    }
    else
    {
        perror (strPath.c_str());
        return false;
    }
    return true;
}

bool getAllFiles(const string& strPath, vector<string>& vect)
{
    char path[PATH_MAX+1];
    if (realpath(strPath.c_str(), path))
    {
        return doGetAllFiles(path, vect);
    }
    return false;
}

bool doSearchAds(const string& strFile)
{
    string strContent;
    if (getString(strFile, strContent))
    {
        ParserDom dom;
        const tree<Node>& tr = dom.parseTree(strContent);
        for (tree<Node>::pre_order_iterator it = tr.begin(); it != tr.end(); it++)
        {
            if (it->isTag() && it->tagName().compare("script") == 0)
            {
                if (it->text().find("google")!= string::npos)
                {
                    cout << "filename:"<< strFile << " text:" << it->text() << endl;
                }

            }
        }
    }
    return false;
}

bool doSearchAllFiles(const string& strPath)
{
    DIR *dir;
    if ((dir = opendir(strPath.c_str())) != NULL)
    {
        struct dirent *ent;

        while ((ent = readdir(dir)) != NULL)
        {
            string strNewPath = strPath+"/" + ent->d_name;
            switch (ent->d_type)
            {
            case DT_DIR:
                if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0)
                {
                    if (!isForbidden(ent->d_name))
                    {
                        if (!doSearchAllFiles(strNewPath))
                        {
                            return false;
                        }
                    }
                }
                break;
            case DT_REG:
                if (isHtml(ent->d_name))
                {
                    doSearchAds(strNewPath);
                }
                break;
            default:
                break;
            }
        }
        closedir(dir);
    }
    else
    {
        perror (strPath.c_str());
        return false;
    }
    return true;
}

bool searchAds(const string& strPath)
{
    char path[PATH_MAX+1];
    if (realpath(strPath.c_str(), path))
    {
        return doSearchAllFiles(path);
    }
    return false;
}

bool test2(const string& strPath)
{
    vector<string> vect;
    if (getAllFiles(strPath, vect))
    {
        for (size_t i =0; i < vect.size(); i ++)
        {
            cout << vect[i] << endl;
        }
        cout << "total file number: " << vect.size() << endl;
        return true;
    }
    return false;
}

bool test1(const string& strFileName)
{
    const string strAdsFile="/home/nick/googleads.txt";
    string strAds;
    string strOldContent, strNewContent;
    if (getString(strAdsFile, strAds))
    {
        if (getString(strFileName, strOldContent))
        {
            if (addAds(strFileName, strAds))
            {
                if (removeAds(strFileName, strAds))
                {
                    if (getString(strFileName, strNewContent))
                    {
                       if (strOldContent.compare(strNewContent) == 0)
                       {
                           cout << "content remain unchanged" << endl;
                           return true;
                       }
                    }
                }
            }
        }
    }
    return false;
}

bool test3(const string& strFileName)
{
    const string strAdsFile="/home/nick/googleads.txt";
    string strAds;
    string strOldContent, strNewContent;
    if (getString(strAdsFile, strAds))
    {
        if (getString(strFileName, strOldContent))
        {
            if (addAds(strFileName, strAds))
            {
                if (removeAds(strFileName, strAds))
                {
                    if (getString(strFileName, strNewContent))
                    {
                       if (strOldContent.compare(strNewContent) == 0)
                       {
                           cout << "content remain unchanged" << endl;
                           return true;
                       }
                    }
                }
            }
        }
    }
    return false;
}

int myAddAds(const string& strPath, const string& strAdsFile)
{
    int result = 0;
    string strAds;
    if (getString(strAdsFile, strAds))
    {
        vector<string> vect;
        if (getAllFiles(strPath, vect))
        {
            for (size_t i =0; i < vect.size(); i ++)
            {
                if (addAds(vect[i], strAds))
                {
                    cout << vect[i] << endl;
                    result ++;
                }
            }
        }
    }
    return result;
}

int main(int argc, char** argv)
{
    if (argc != 3)
    {
        cout << "usage: " << argv[0] << " <diabloPath> <googleads>" << endl;
        return -1;
    }
    cout << myAddAds(argv[1], argv[2]) << endl;
    return 0;
}
我在synology的nfs的exportfs的设置如下:
/volume1/photo *(rw,async,no_wdelay,insecure,no_root_squash,insecure_locks,sec=sys,anonuid=1000,anongid=1000) /volume1/music *(rw,async,no_wdelay,insecure,no_root_squash,insecure_locks,sec=sys,anonuid=1000,anongid=1000) /volume1/DiskStation *(rw,async,no_wdelay,crossmnt,insecure,no_root_squash,insecure_locks,sec=sys,anonuid=1000,anongid=1000) /volume1/video *(rw,async,no_wdelay,crossmnt,insecure,no_root_squash,insecure_locks,sec=sys:krb5:krb5i:krb5p,anonuid=1000,anongid=1000)
因为synology创建的用户id从1026开始和我的ubuntu的1000不同,所以,我只好修改/etc/passwd的我的id,当然需要修改文件。我依然不知道这个做法是否正确。

四月八日 等待变化等待机会

我搞不太明白googleads的做法,难道是因为aws/s3/static website不支持script的原因吗?我的小修小改的小程序这个是我的私心我想尽可能的增加html文件的数目以便增加广告位置。

四月十日 等待变化等待机会

无耻的剽窃linux的manpage,大概是想赚点钱希望googleads能够支撑这个网站的aws的费用。wget -k -p -r --no-parent --no-clobber http://man7.org/linux/man-pages/
这个就是我的linux man page

四月十一日 等待变化等待机会

我的nfs的文件系统有这个问题就是readdir返回的dirent的st_mode值为DT_UNKNOWN的问题,我一开始以为是nfs的实现的问题,后来再读这个部分才意识到这个是一个典型的问题。这一段话我也在manpage里读了,却似乎是清风拂面完全没有意识这个充分说明了我没有文件系统实现的概念所以才没有立刻意识到这个问题的实质:
Currently, only some filesystems (among them: Btrfs, ext2, ext3, and ext4) have full support for returning the file type in d_type. All applications must properly handle a return of DT_UNKNOWN.
这里所做的进一步的解说是可以帮助你理解的。后来我修改我的代码大概的样子和这里是类似的,当然明白了其中的道理
这个代码是小菜一碟的。
  1. 首先,是在readdir的manpage就明确说了不是所有的文件系统都实现了这个type,这个是作为一般的开发者应有的素质要自己处理这个问题。
  2. 其次,这个问题的核心是为什么。原因是很深层次的效率问题,我可以理解不把type信息存在directory里,可是到底是为什么?难道一个byte的type是如此的庞大吗?以下这部分的高亮部分我还是不能理解难道我的英语真的有问题,因为如果在每一个directory里把每一个包含的子目录文件的属性存储下来难道你不需要读取所有的成员的inode来查询吗?这个工作不应该是ls之类的文件夹阅读者的工作吗?为什么要文件夹来保管?你保存了属性也许还有贪心者要你保存inode相关的其他信息这个不是endless game吗?:是不是可以这么理解dirent这个结构为了存储效率和其他原因不保存自己的当然不是自己的,自己就是directory需要的是包含的每个子节点的信息,我可能脑子不清楚type信息,因为有了inode用户自己去查询。这个从实现者的角度来看似乎更有理由。首先是冗余信息的存储,而且这个牵连到修改文件的一致性的问题因为冗余的数据就意味着修改的麻烦。其次我以为文件的所有的信息都应该和其inode联系而文件夹的职责是一个考量,它仅仅是一个容器存储的是一个与文件系统实现无关的所包含的子节点的信息,这句话要这么说,所谓字节点就是它所包含的文件和子目录,那么与实现无关的就是他们的文件名和子目录名。从这一点来理解就是文件夹和软连接一样都是一种特殊的文件,一个内容是包含的文件名,一个内容是它指向的目标的路径。只有名字是和平台实现无关的最无争议的部分。甚至于inode的任何信息都是一个包含了实现细节的累赘。这些实际上是在stat的manpage里讲的很多。这个就是真正的原因
    Some filesystems, like EXT4 I think, and very recent XFS (with the new metadata version), keep type info in the directory, so it can be returned without having to load the inode from disk. This is a huge speedup for find -name: it doesn't have to stat anything to recurse through subdirs. But for filesystems that don't do this, d_type will always be DT_UNKNOWN, because filling it in would require reading all the inodes (which might not even be loaded from disk).
  3. 在进一步的原因实际上是标准的问题,就是说POSIX压根儿没有规定,一下是我摘录的大侠的论断,也许是因为directory是一个特殊的文件它存储什么内容完全是实现者的选择,的确,文件系统需要关心每个文件的内容如何吗?你需要怎样组织你的磁盘inode的分配是你的细节,你的文件存储的内容包含什么也是一个选择。
    The lowest-level portable way (portable to POSIX systems), is to use the libc functions to open a directory and read the entries. POSIX doesn't specify the exact system call interface, unlike for non-directory files.

bool doGetAllFiles(const string& strPath, vector<string>& vect)
{
    DIR *dir;
    if ((dir = opendir(strPath.c_str())) != NULL)
    {
        struct dirent *ent;
        while ((ent = readdir(dir)) != NULL)
        {
            string str = strPath+"/" +ent->d_name;
            bool bFile = false;
            bool bDirectory = false;
            struct stat st;
            if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
            {
                continue;
            }
            switch (ent->d_type)
            {
            case DT_DIR:
                bDirectory = true;
                break;
            case DT_REG:
                bFile = true;
                break;
            case DT_UNKNOWN:
                if (stat(str.c_str(), &st) < 0)
                {
                    return false;
                }
                if (S_ISREG(st.st_mode))
                {
                    bFile = true;
                }
                else
                {
                    if (S_ISDIR(st.st_mode))
                    {
                        bDirectory = true;
                    }
                }
                break;
            default:
                break;
            }
            if (bFile && isHtml(ent->d_name))
            {
                vect.push_back(str);
            }
            if (bDirectory && !isForbidden(ent->d_name))
            {
                if (!doGetAllFiles(str, vect))
                {
                    return false;
                }
            }
        }
        closedir(dir);
    }
    else
    {
        perror (strPath.c_str());
        return false;
    }
    return true;
}

四月十七日 等待变化等待机会

我试图编译最新版的vlc但是卡在它的configure上抱怨我的编译器不支持c++11,c++0x这个让我丈二和尚摸不着头脑,首先我的编译器是gcc4.8不存在不支持的问题,因为使用-std=c++0x是可以支持的。那么只有检查configure的代码,这个确实是有难度,有人抱怨--verbose不起作用,我也一头雾水不知怎么才能debug configure,后来才看到有config.log来检查,发现出错是这个。看来这个是gcc4.8的问题,无法逃避只好把有关max_align_t的测试部分注释掉。

四月十九日 等待变化等待机会

这里是我对于wget-1.15的一个微不足道的改动,因为我要保持临时文件的后缀名。
使用wget盗取整个网站内容的时候有一个小问题,就是产生文件名字的时候wget把数字加在文件名最后,我希望要把扩展名保留。比如index.html如果存在,新文件名原来是改为index.html.1。而我希望改为index1.html。这个是我的小小的改造。你可以下载: git clone http://www.staroceans.org/myprojects/wget-1.15/repo.git

diff --git a/src/utils.c b/src/utils.c
index 4354668..4e48592 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -623,20 +623,42 @@ file_size (const char *filename)
 /* stat file names named PREFIX.1, PREFIX.2, etc., until one that
    doesn't exist is found.  Return a freshly allocated copy of the
    unused file name.  */
-
+#define MAX_EXTENSION_LENGTH 8
 static char *
 unique_name_1 (const char *prefix)
 {
   int count = 1;
   int plen = strlen (prefix);
   char *template = (char *)alloca (plen + 1 + 24);
+  char extention_name[MAX_EXTENSION_LENGTH] = {0};
   char *template_tail = template + plen;
 
   memcpy (template, prefix, plen);
-  *template_tail++ = UNIQ_SEP;
+
+  while (template_tail != template && *template_tail != UNIQ_SEP)
+  {
+      template_tail--;
+  }
+  int size = template + plen - template_tail;
+  if (*template_tail == UNIQ_SEP && size < MAX_EXTENSION_LENGTH)
+  {
+      memcpy(extention_name, template_tail, size);
+  }
+  else
+  {
+      template_tail = template + plen;
+  }
+
+  if (*template_tail != UNIQ_SEP)
+  {
+      *template_tail++ = UNIQ_SEP;
+  }
 
   do
-    number_to_string (template_tail, count++);
+  {
+      number_to_string (template_tail, count++);
+      strcat(template_tail, extention_name);
+  }
   while (file_exists_p (template));
 
   return xstrdup (template);
以下是我的函数:

#define MAX_EXTENSION_LENGTH 8
static char *
unique_name_1 (const char *prefix)
{
  int count = 1;
  int plen = strlen (prefix);
  char *template = (char *)alloca (plen + 1 + 24);
  char extention_name[MAX_EXTENSION_LENGTH] = {0};
  char *template_tail = template + plen;

  memcpy (template, prefix, plen);

  while (template_tail != template && *template_tail != UNIQ_SEP)
  {
      template_tail--;
  }
  int size = template + plen - template_tail;
  if (*template_tail == UNIQ_SEP && size < MAX_EXTENSION_LENGTH)
  {
      memcpy(extention_name, template_tail, size);
  }
  else
  {
      template_tail = template + plen;
  }

  if (*template_tail != UNIQ_SEP)
  {
      *template_tail++ = UNIQ_SEP;
  }

  do
  {
      number_to_string (template_tail, count++);
      strcat(template_tail, extention_name);
  }
  while (file_exists_p (template));

  return xstrdup (template);
}
我再次把www.cplusplus.com/reference全部盗窃下来存在这里:
wget -nd -k -p -r --no-parent --no-clobber http://www.cplusplus.com/reference
www.staroceans.org/cplusplus/index.html

四月二十六日 等待变化等待机会

我发现我经常误会我的apt-get source失败,因为dpkg-source -x会长时间挂起,我一开始以为有什么错误,后来才意识到我是在我的nfs mount的目录下操作,文件读写非常的慢。我的测试是这样子的: time dd if=/dev/zero of=/BigDisk/nfstest.zero bs=16k count=16384发现需要大概5秒多,而在本地硬盘上时间是不到十分之一。于是我开始寻找优化nfs的方法:我尝试了如下方法 然而这一切似乎完全没有任何作用,我的nfs的效率依然很慢。
我开始实践使用libwebkit-1.0:

CXXFLAGS =	-O0 -g -Wall -fmessage-length=0 -I/usr/include/webkitgtk-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
LIBS = -lwebkitgtk-1.0
注意我的代码是#include "webkitdom/webkitdom.h"。另一个注意的是/usr/lib/x86_64-linux-gnu/glib-2.0/include这个是因为glibconfig.h的问题,这个不是在include的头文件夹而是在lib确实让我花了不少时间才意识到。
我又一次无耻的把linux nfs-howto盗用了。我存在了这里作为我自己的收藏

四月二十九日 等待变化等待机会

我煞费苦心的要改代码为wget添加适当的文件扩展名发现代码里居然已经有这个选项-E,这个真是有画蛇添足的嫌疑。另一个问题是当我修改了我的mtu的时候我无法再使用ssh。所以,只能修改回sudo ip link set mtu 1500 dev eth0
然后我再一次的把https://www.w3schools.com/给盗窃过来了。存在这里

五月三日 等待变化等待机会

此事说来话长,我一直想使用libv8或者使用webkit来parsing/run,其中js是一个难点,对于怎么使用我是一头雾水不知如何入手。因此想尝试一下google的开发流程。下载的过程就是相当的复杂,因为要全套使用google的开发工具包和从前的做法很不一样。我在我的nfs的文件目录下始终都出现权限问题,我是在sudo下居然还有权限问题不能chdir这个真实莫名其妙后来只好把目录拷贝的本地的磁盘上才行。然后需要安装需要的开发工具:./install-build-deps.sh --no-arm --no-chromeos-fonts --no-nacl因为默认的安装很多32位操作系统下的软件包很麻烦。

五月十日 等待变化等待机会

我经常会遇到在bootstrap/configure后发生"definition of this LT_INIT comes from libtool 2.4.2"的错误,这个时候需要的是autoreconf -fi。实际上我最后并没有彻底解决问题,我怀疑是我的libtool被降级之后的问题,折腾了许久我只好下载最新版的libtool源代码编译安装,当然首先是卸载了我自己的。

五月十一日 等待变化等待机会

一个看似简单的问题,让我感到惭愧,就是怎样直接呼叫convertion operator,不是constructor,比如你的定义是这样子的:class Foo{public:operator std::string ()const {return "It is my name, idiot!";}};怎样呼叫你的名字呢?Foo fool; cout << fool.operator std::string()<<endl;
我心血来潮想要把dvd的音频抓出来,使用transcode很方便的: for i in {1..17}; do transcode -x null,dvd -y null,tcaud -i /dev/sr0 -T 1,$i,1 -a 0 -E 44100,16,2 --lame_preset medium -m Elektra_$i.mp3; done

五月十六日 等待变化等待机会

本来我对于改造ipmitool充满了期待,然而深入探索sensor的代码才意识到它的global variable大多是埋藏在函数内的static变量,这个仿佛是隐藏的全局变量,它的直接结果是函数的不可重入,我一开始的想法是把这些变量放在intf这样一个不透明的所谓context结构里,仿佛opengl的经典设计,但是传出参数如果不是用户自己的内存同样解决不了函数的reentrable的问题。因此这种改造是伤筋动骨的大动作,不是我所愿意或者当前有能力的,不是说不可能,而是我没有这个时间精力去实施。
我下载了一个全军武器装备采购信息网,其实更像是一个快照,因为它是实时更新的,我仅仅是下载了一个镜像而已。毫无理由的。

五月二十二日 等待变化等待机会

对于ffmpeg不支持mp3的问题似乎是老生长谈,我不知道这么简单的问题我似乎也在不断重复,因为我也记不清楚是否已经不止一次遇到。究其原因是版权的问题,因为mp3是有专利的,如同开源社区对于所有这类问题一样ffmpeg是不会正式的把libmp3lame编译进去的,所以,所谓的安装额外的包是不可能解决这个问题的,只有源代码从头编译,至于说具体的配置,也许是一个小细节,我一开始居然忘了:既然知道这个是版权的问题,肯定你要支持gpl和non-free,否则又何必重新编译,添加enable-libmp3lame并不是问题,问题是如果不是动态库又何谈什么gpl,我也是很惭愧对于gpl之类的法律相关的协议始终记不清楚,可是忘记动态库实在是不应当:
./configure --enable-gpl --enable-version3 --enable-nonfree \
--enable-postproc  --enable-libmp3lame \
--enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libtheora \
--enable-libvorbis --enable-libx264 --enable-libxvid --enable-shared --disable-static \
--enable-libass --enable-libbluray --enable-libopenjpeg   --enable-libfreetype \
--enable-libfontconfig --enable-gmp --enable-librsvg --enable-libtesseract
当然了安装程序似乎有个小的瑕疵,要自己手动ldconfig。直接使用ffmpeg抓取mp3音轨我发现似乎速度过快或者过慢,声音jitty的厉害。所以,我就从我之前的成品来做:
ffmpeg -i ~/volume1/video/YesMinister/OpenGovernment.m4v -c:a:0 libmp3lame -b 48000   -vn -sn  /tmp/output.mp3
其中的encoder的名字不是mp3而是libmp3lame,这个可以在./configure --list-encoders里得到确认。折腾文件扩展名我感到regex真的是一个头疼的事情:

for var in `ls *.m4v`; do name=`echo ${var}|grep -Eoi "^.*[^.]{5}"`; ffmpeg -i ./${var} -c:a:0 libmp3lame -b 48000 -vn -sn ${name}.mp3 ; done
我的KGPE-D16服务器的说明书。找到一个很好的网页在讲述一些基本的网络配置的信息,来不及看了。我保存了一个打印文档。我犯的错误简直是遇不可及,我的bmc芯片究竟要怎样才能被链接上呢?我的服务器有两个网卡,我居然会以为我可以使用其中的一个作为bmc的网卡,这个不是不行,但是是shared,我是dedicated的设置(这个是靠主板上的ipmi_sel1的跳线来控制的。不过这个是原来手册的说法,补充页说这个不需要了,系统会自动配置。)总之,我拼命在修改bmc的macaddress,这个是不可能修改的。最后才忽然焕然大雾我被误导了,有一段时间我可以OOB使用ipmi是我插了三根网线,我这才想起来bmc的是独立的port。其实我以前也是很傻的,配置两个网卡总是不成功,其实,你设成static的根本没有人妨碍你,只要不冲突,但是在/etc/network/interface里不能重复出现auto,network,dns之类的,两个网卡也只能使用一个。当然这个就是我ifdown/ifup的错误的原因。现在我终于可以使用两个网卡了,可是他们又没有failover这个有什么意思呢?还要学习。现在的问题是我的microhttpd在听80端口,而且是两个nic都有吧,以至于我无法访问bmc的web gui。怎么办? 首先是寻找这个程序: netstat -tulpn

五月三十日 等待变化等待机会

我的记忆力太差了,这个是以前的一个测试程序,应该是已经存档了,可是我想不起来放在那里了,这个实际上是一个demo怎样使用ipmitool的intf的第一步。
/*
 ============================================================================
 Name        : ipmiToolTest.c
 Author      : nick
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include "ipmitool/ipmi_intf.h"
#include "ipmitool/log.h"

int verbose = 0;
int csv_output = 0;

int main(void)
{
	static struct ipmi_intf * ipmi_main_intf = NULL;

	int privlvl = IPMI_SESSION_PRIV_ADMIN;

	int retry = 1;
	uint32_t timeout = 15;
	int authtype = IPMI_SESSION_AUTHTYPE_MD5;

	char * hostname = "192.168.1.135";
	char * username = "admin";
	char * password = "admin";
	char * intfname = "lan";
	char * progname = NULL;
	char * oemtype  = NULL;

	int cipher_suite_id = 4; /* See table 22-19 of the IPMIv2 spec */



	/* load interface */
	ipmi_main_intf = ipmi_intf_load(intfname);
	if (ipmi_main_intf == NULL) {
		lprintf(LOG_ERR, "Error loading interface %s", intfname);
		goto out_free;
	}

	/* setup log */
	log_init(progname, 0, verbose);

	/* run OEM setup if found */
	if (oemtype != NULL &&
	    ipmi_oem_setup(ipmi_main_intf, oemtype) < 0) {
		lprintf(LOG_ERR, "OEM setup for \"%s\" failed", oemtype);
		goto out_free;
	}

	/* set session variables */
	if (hostname != NULL)
		ipmi_intf_session_set_hostname(ipmi_main_intf, hostname);
	if (username != NULL)
		ipmi_intf_session_set_username(ipmi_main_intf, username);
	if (password != NULL)
		ipmi_intf_session_set_password(ipmi_main_intf, password);


	if (authtype >= 0)
		ipmi_intf_session_set_authtype(ipmi_main_intf, (uint8_t)authtype);
	if (privlvl > 0)
		ipmi_intf_session_set_privlvl(ipmi_main_intf, (uint8_t)privlvl);
	else
		ipmi_intf_session_set_privlvl(ipmi_main_intf,
				IPMI_SESSION_PRIV_ADMIN);	/* default */
	/* Adding retry and timeout for interface that support it */
	if (retry > 0)
		ipmi_intf_session_set_retry(ipmi_main_intf, retry);
	if (timeout > 0)
		ipmi_intf_session_set_timeout(ipmi_main_intf, timeout);

	ipmi_intf_session_set_cipher_suite_id(ipmi_main_intf, cipher_suite_id);


	struct ipmi_rs * rsp;
	struct ipmi_rq req;

	uint8_t data[256];


	memset(data, 0, sizeof(data));
	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_APP;
	req.msg.lun = 0;
	req.msg.cmd = 0x01;
	req.msg.data = data;

	rsp = ipmi_main_intf->sendrecv(ipmi_main_intf, &req);

	if (rsp == NULL) {
		lprintf(LOG_ERR, "Unable to send RAW command "
			"(channel=0x%x netfn=0x%x lun=0x%x cmd=0x%x)",
			ipmi_main_intf->target_channel & 0x0f, req.msg.netfn, req.msg.lun, req.msg.cmd);
		goto out_free;
	}
	if (rsp->ccode > 0) {
		lprintf(LOG_ERR, "Unable to send RAW command "
			"(channel=0x%x netfn=0x%x lun=0x%x cmd=0x%x rsp=0x%x): %s",
			ipmi_main_intf->target_channel & 0x0f, req.msg.netfn, req.msg.lun, req.msg.cmd, rsp->ccode,
			val2str(rsp->ccode, completion_code_vals));
		goto out_free;
	}

	lprintf(LOG_INFO, "RAW RSP (%d bytes)", rsp->data_len);

	/* print the raw response buffer */
	int i = 0;
	for (i=0; i<rsp->data_len; i++) {
		if (((i%16) == 0) && (i != 0))
			printf("\n");
		printf(" %2.2x", rsp->data[i]);
	}
	printf("\n");

	out_free:
	/* clean repository caches */
	ipmi_cleanup(ipmi_main_intf);

	/* call interface close function if available */
	if (ipmi_main_intf->opened > 0 && ipmi_main_intf->close != NULL)
		ipmi_main_intf->close(ipmi_main_intf);

	log_halt();
	return 0;
}
编译应该是显而易见的:gcc ipmiToolTest.c -L/BigDisk/ipmitool-1.8.13/./src/plugins/.libs/ -lintf -I/BigDisk/ipmitool-1.8.13/include -lm -o ipmitoolTest.exe

六月四日 等待变化等待机会

心血来潮希望编译openbmc的项目,始终遇到一个编译错误:
In file included from Dev/Go/code/src/github.com/veandco/go-sdl2/sdl/sdl_syswm.go:4:0:
/usr/include/SDL2/SDL_syswm.h:97:44: fatal error: mir_toolkit/mir_client_library.h: No such file or directory
 #include <mir_toolkit/mir_client_library.h>
一开始我不知其所以然,因为我始终以为这个是交叉编译的项目所有的编译都是在其自己的环境,因为链编译工具编译环境应该都是封闭的和我的Host无关,后来才意识到这个是我的模拟器qemu的运行的img的编译,因此是我自己的qemu的运行环境,这里解释了这个库的头文件的问题我最后只好把/usr/include/mirclient和mircommon下的东西都移到/usr/include下。

六月六日 等待变化等待机会

这个真的是难以置信的事情!我花了好几天才明白了一个如此简单的道理,难道没有人犯同样的错误吗?似乎我的理解和大多数人都不一样吗?关于dvd里的subtitle,似乎没有什么人不知道,当我使用ffmpeg试图从DVD里的vob文件抓取subtitle的时候总是不成功,后来才明白一个简单的道理,就是ffmpeg里的所有的subtitle的encoder/decoder都是针对subtitle stream的,当你的vob里面只有video/audio的stream而没有subtitle的stream的时候自然是报错说没有包含任何的stream,试看这个输入的文件情况:

Input #0, mpeg, from 'VTS_01_1.VOB':
  Duration: 00:24:35.58, start: 0.045500, bitrate: 5821 kb/s
    Stream #0:0[0x1bf]: Data: dvd_nav_packet
    Stream #0:1[0x1e0]: Video: mpeg2video (Main), yuv420p(tv, top first), 720x480 [SAR 8:9 DAR 4:3], Closed Captions, 29.97 fps, 29.97 tbr, 90k tbn, 59.94 tbc
    Stream #0:2[0x80]: Audio: ac3, 48000 Hz, stereo, fltp, 192 kb/s
你看到了什么?不要以为stream#0:0暗藏什么玄机,Closed Captions不是说的很清楚了吗?字幕就在video stream里。我觉得这里英语母语对于中文使用者是一种不公平,我专门google才明白其中的道理,通常人们把所谓的subtitle和closed caption混为一谈,甚至netflix/youtube也经常不加区分,因为两者不是一样的东西,前者准确的说是translated text,也就是说和spoken language不同的其他语言的翻译文字,后者更准确的说是transcripts,是当前spoken语言的文字版,同时对于背景声音做描述,更加像是电影脚本,而这两个东西在计算机编程也是不同的东西,subtitle因为是“外语”,所以经常用图像来存储,所以才会出现另一个stream,要把这个转换成文字可不简单,需要ocr来识别,很多的decoder费尽心力去做的。而后者仅仅是夹杂在video stream里的,至少目前我还不确定他是怎么存储的,应该是容易的多吧?照例在stackoverflow上的大牛有着无与伦比的正确解决率,可是这一次我犯了一个先入为主的错误,后面再说。那么废话了这么多究竟如何才能取出来呢?我找了很久才找到这个ccextractor的工具,的确很强。然后简单的浏览代码,当然什么也没有看明白,我心生疑虑,ffmpeg绝对不可能没有类似的功能,于是我回到之前的stackoverflow的帖子才明白所言不虚,只是我不相信如此怪异的语法!
ffmpeg -f lavfi -i "movie=VTS_01_1.VOB[out+subcc]"  -map 0:1  -y /tmp/output.srt
你相信这个是ffmpeg的语法吗?我始终不敢相信"[out+subcc]"是正确的。为什么在ffmpeg的FAQ里没有这个!难以置信啊!回想起来我为了这个结果可谓是吃了很多苦,一开始以为找不到subtitle的stream是因为probe/analysis太少了,还专门增加这个长度,而且这个-probesize 50M -analyzeduration 50M必须出现在输入文件参数之前才行。这其中的一些名词至今我还是不很清楚,比如这个标准是所谓的美国标准ATSC,不同于日本标准,然后CEA-608和708是什么关系?

六月八日 等待变化等待机会

很久很久没有写什么关于政治的话题,虽然我一直在观察思考但是不想污染这个日记的风格。今天忍不住记下来因为也许将来有一天这些可能会被验证,对于这种虚荣的追求迫使我想留下来一个痕迹:阎学通的报告很有一些独到见解,他提到了一个很有意思的现象,在多极争霸的格局下先出头的被灭,而在两强争霸的格局下后出头的被灭。这个是一个多么有意思的战略抉择啊,在三国时期曹操之所以挟天子以令诸侯是为了不成为众强的攻击目标,但是在两强争霸的时候先形成优势的会产生磁吸效应形成正反馈加速形成优势。他提到的另一个例子是在短道滑冰比赛时候最容易摔跤的地方是在弯道,原因是超越发生在弯道,在直道阶段大家大部分是匀速前进超越很难发生,而在弯道大家都会减速避免摔倒,而想要超越就要冒险不减速或者少减速,于是超越也许就发生了,但是被超越者绝对不甘心被超越,于是激烈的碰撞就在两强接近的时候发生。我以前一直认为美国最好的策略是越早和中国决战越有利,但是现在看来这种决策本身都不是理性的,因此出于情感性的因素决战发生在被超越的一刹那因为非理性的心理因素,因此最危险的时刻在14到17年后,也就是习近平提到的2035年前后。

六月十日 等待变化等待机会

关于ffmpeg的filter的cc我跟踪了gdb有了一个初步的认识,首先,这个是一个所谓的filter,都是第一个参数,至少我是这么认为的,它实际上是创造出一个format,当然是依靠在打开文件的时候利用文件名后缀[output+subcc]也就是output的format。其次就是说它是创造出了虚拟的stream,因为srt是只能接受subtitle的stream才能工作的,而closed caption不像其他stream是一个subtitle,而是和video混在一起的,这样就让这个虚拟的subtitle stream让srt来工作了。不过细节还没有很明白。

六月十一日 等待变化等待机会

有时候也许奇技淫巧也是必须的,不过我不太确定这个算不算,其实只不过是今天碰巧用到的,我需要反复遍历服务器里所有的sensor,一开始写了一个只获得sensor的名字的函数,后来发现不足,希望获得所有sensor的SDR的记录,所以我就写了一个回调函数,但是需要使用的是类的成员函数作为回调,那么这里调用和实现的语法有些不太一样,首先调用的时候需要使用->*操作符,
(this->*callback)(*this, userData);
那么带入参数时候直接对于成员函数取地址是不行的,因为成员函数都是偏移量不是真正的指针,需要加上类的前缀或者好像namespace的样子吧?
// Author      : nick
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstdio>
using namespace std;

class Human;
typedef void (Human::*CollectCallbackType)(const Human& man, void* userData);
class Human
{
public:
    Human():MaxNameLength(6)
    {
    }
    void generate()
    {
        m_nAge = rand()%30 + 20;
        char buffer[32];
        for (size_t i = 0; i < MaxNameLength; i ++)
        {
            buffer[i] = rand()%26 +'a';
        }
        buffer[MaxNameLength] = '\0';
        m_strName = buffer;
    }
    operator int() const
    {
        return m_nAge;
    }
    operator string() const
    {
        return m_strName;
    }
    void collectAgeCallback(const Human& man, void* userData)
    {
        vector<int>* pVect = (vector<int>*) userData;
        pVect->push_back((int)man);
    }
    void collectNameCallback(const Human& man, void* userData)
    {
        vector<string>* pVect = (vector<string>*)userData;
        pVect->push_back((string)man);
    }
    void collectHelper(CollectCallbackType callback, void* userData)
    {
        generate();
        (this->*callback)(*this, userData);
    }
    void collectAge(vector<int>& vectAge)
    {
        collectHelper(&Human::collectAgeCallback, &vectAge);
    }

    void collectName(vector<string>& vectName)
    {
        collectHelper(&Human::collectNameCallback, &vectName);
    }

protected:
    const size_t MaxNameLength;
    int m_nAge;
    string m_strName;
};

int main()
{
    Human man;
    vector<int> ageVect;
    vector<string> nameVect;

    for (int i = 0; i < 15; i ++)
    {
        man.collectAge(ageVect);
        man.collectName(nameVect);
    }
    for (size_t i = 0; i < ageVect.size() && i < nameVect.size(); i ++)
    {
        cout << "name:" << nameVect[i]
             << "age:" << ageVect[i] << endl;
    }
	return 0;
}

七月十七日 等待变化等待机会

遇到一个小的不能再小的问题了,可能对于大多数人都是值得忽略的问题,就是set的iterator居然定义为const_iterator,换言之iterator和const_iterator时一样的,我当时试图修改set里的元素编译出错我一直想不到会有这个可能性因此对于编译器报出的错误视而不见总在想是什么其他原因,直到后来偶然看到stl里定义依然不知其所以然,后来google才明白这个是因为set和map的最重要的区别,set元素本身就是key,一旦修改导致排序失效,因此不能修改所以才声明成const,这个是多么简单的道理我却不知道,当年看侯杰的书怎么遗漏了这个东西呢?

七月十八日 等待变化等待机会

整个使用linux的过程就是在寻找解决问题的过程,这不是很好吗?一个小小的问题浮现了,我要用以下midnightcommander这个是我的最爱之一,可是在我目前喜爱的centos7上一直hang着,google才明白是因为我的Hostname的设定,因为这个有好几个层面的问题,我想起来我们工作中安装操作系统的时候hostname是installer设定的一个参数,其次这个是在/etc/hostname的一个设定,但是作为命令行hostname的设定似乎并不是修改这里,我在/etc/resolv.conf里设定是不对的因为它是自动产生的,核心的问题是我无法正确的ping `hostname`,这个是mc一直要等到timeout的原因。究竟要怎样修改呢?一方面hostname看起来仅仅是一个面子问题,我的terminal的prompt显示和设定不一致,但更要紧的是某些程序比如mc依赖它来获得ip地址,我修改/etc/sysconfig/network也是无意义的,这个正是我之前说的installer或者说anaconda设定的,实际上/etc/sysconfig/network-scripts/ifcfg-xxx里的东西才更重要。这里也有设定hostname,似乎这里才能让我的ping找到hostname的正确ip。唉,这个问题实在是不简单,也许对于有些过来人这个是不值一提,可是我至今还是在黑暗中,总之,当我的ping `hostname`能够正确返回的时候我的mc也能够立刻运行。BTW,mc是一个alias,他运行了一些环境设定,不过目前这个是无关的。值得学习的技巧是怎样用regex获得用户名:MC_USER=`id | sed 's/[^(]*(//;s/).*//'`
我使用firefox打印pdf文件不成功,直到看到一个小通知帖子才意识到是安全问题,google发现这个:setsebool -P unconfined_mozilla_plugin_transition 0

七月三十一日 等待变化等待机会

有两件事让我触动很大,一个是关于MegaRAC API的问题,我惊奇的发现S居然老早就已经写进代码里了,我却还是从W哪里闻到他的诀窍,使用cookie和CSRFtoken的http header来验证。CSRF(Cross-Site Request Forgery)。说老实话我并不是很理解这个问题,但是S居然不知道怎样明白使用特殊的http header来做的,等他回来要问他一下。另一个问题是对于BMC的firmware的形式的问题,我自己辖打瞎撞发现原来它是mtd之类的flash的image,于是有一个途径来mount它,就是首先使用mtdram/mtdblock两个内核模块然后dd这个image到/dev/mtdblock0这个设备,然后使用jffs2的类型mount,我看到了十分之一的image文件大小的文件系统,dd总是报错说设备out of space,我看了dmesg里面抱怨一些错误但是还是没有找到原因,只能尝试比较新一些的内核,或者使用什么mtd-util/tool之类的来实验,有人似乎说是这个内核模块的问题,有人说是内存分配不足。

八月二日 等待变化等待机会

centos还是要比ubuntu好多了,这个指的是作为专业开发者而言,对于普通用户ubuntu是很友好的,但是相对来说有很多的不稳定因素。比如我编译mtd-utils的时候因为我不断需要降级很多的依赖的包导致总是出现version mismatch的错误,这里有大仙指点但是也不奏效,不过这个步骤还是对的,只不过我的问题更复杂罢了:aclocal; libtoolize --force; autoheader; autoreconf --force --install
这个mtd我对于此所知甚少,这个是一个很好的入门,我想以后好好阅读存在这里。那么我在Ubuntu上虽然工具都有了,但是在dd的时候还是出现out of space的错误,我觉得这个也许是因为我的内核版本太高了的,和公司的正好相反,在我的centos7.4上就没有这个错误,它的内核版本是3.1x,顺便说一下我参考了别人的做法就是先安装内核模块modprobe mtdram mtdblock,然后把flash image用dd写到/dev/mtdblock0设备上,然后在使用mount -t jffs2来mount,可是在centos上没有mount.jffs2,我怀疑这个是需要内核支持的,因为我编译的mtd-utils是user-space tool而已,那么编译内核模块又是一个巨大的挑战,在centos上工具和Ubuntu完全不一样,这个是一个很好的指导,步骤如下:一种是之安装编译内核的环境工具:yum install kernel-devel然后可以编译单个的module,另一个是完全的下载全部内核代码重新编译,这个作者不赞成:首先是编译的rpm工具,mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS};echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros;yum install rpm-build redhat-rpm-config asciidoc hmaccalc perl-ExtUtils-Embed pesign xmlto;yum install audit-libs-devel binutils-devel elfutils-devel elfutils-libelf-devel java-devel;yum install ncurses-devel newt-devel numactl-devel pciutils-devel python-devel zlib-devel
然后是下载src rpm,这个地方建议直接去 http://vault.centos.org查看你的内核代码版本,者莪uname -r和lsb_release -d就不用说了。这里特别强调编译不要使用root,所以rpm -i kernel-$(uname -r).src.rpm那么编译怎么做呢?这个就是普通rpm编译的做法cd ~/rpmbuild/SPECS ; rpmbuild -bp --target=$(uname -m) kernel.spec然后你仅仅是生成了src rpm的内容,也就是内核的源代码在~/rpmbuild/BUILD/kernel*/linux*/这里。至于编译配置那就是另一个大的工作了。

八月三日 等待变化等待机会

这两天的一点点心得是这样的,我之前一直看到我dd myflashimage /dev/mtdblock0出错说out of space并不是什么内核版本的问题,至少不是在dd,这个是mtdram的问题,是这个设备的默认的total_size大小的问题,这个可以轻松的由module的参数来解决,比如我要写入的Image大小为30M,那么modprobe mtdram total_size=40000就差不多了因为单位的k,而真正的困难时第二步的mount出错了,不论我使用新版内核并不能解决jffs2的文件系统不匹配的问题,我google到的答案似乎是说这个错误可能是很多的原因,或者是flash的特性比如没有先erase的缘故,我使用mtd-utils来eraseall然后得到的错误是mtdblock0不是block device,这个时候我才想起mtdram是一个模拟的东西,所以另一个说法似乎更加有道理,他说dd不是正确的做法对于nand来说,要使用nandwrite,似乎有道理,可是同样的mtdblock0被我作为一个内存模拟的mtd设备并不是真正的mtd block设备,还有第三个可能就是说也许qct的bmc f/w也许不是真正的mtd image,或者另一个可能性是使用很古老的内核驱动?因为我实验的都是较新的内核比如3.10/4.x,我倾向于bmc是比较古老的内核版本。同时我对于这个mtdram的内核模块的功能有了进一步的怀疑,总之我的探索仅仅解决了dd out of space的谜团,而作为寻找答案的过程的收获是编译内核模块的体会。首先是内核版本的localversion的问题,我发现kernelversion是代码定义的和kernelrelease是两个概念,这个我以前记得现在又全忘了,之所以计较这个问题是因为我编译的模块在modprobe的时候总是抱怨exec format error,我一直怀疑是模块的版本不一致,因为Modules.sysver文件需要完全编译才会产生而似乎这个和原本的不一致,我不知道是否应该把我现在的内核版本拷贝过来,我即便把localversion按照我当前内核版本做成一模一样依然有modprobe的错误,后来我只好尝试-f来压制错误,也许这个是jffs2的mount的错误的来源?还有一条路线是使用mtd-utils的内核来编译模块,但是这个是所谓的external的编译我需要学习更多,也有很大的风险搞坏系统,也许不安装来实验是安全额做法。总之内核无小事,任何的细节都可能是关键的。

八月六日 等待变化等待机会

我依然想用2.6的内核来验证一下究竟发生jffs2读的错误是否是因为内核版本不同的原因,我已经知道4.x的最新内核不工作,那么到哪里去找2.6版的老内核呢?第一个想到的是使用那些用usb启动的linux,可是一时找不到合适的,后来发现还是我最心爱的ubuntu10.04的iso最容易启动。于是乎我对于这个好东西甚是欣赏想把它的wifi不工作的问题解决了,那么第一步是remaster iso,我记得以前用过工具,忘了就只好一步一步跟着这里

八月八日 等待变化等待机会

这个是由一个教训,我升级我的centos,然后突然之间很多内核模块部工作现象就是内核版本不同的表现,我很清楚的没有自己编译内核,在centos是确定的,也许吧,我的记忆力太差了,可是使用yum history rollback并不起作用而且undo/rollback都有错误,这让我感到困惑,然后我试图降级/升级内核版本不起作用才意识到我的grub是装在ubuntu的而在启动菜单上内核文件是写死的,难怪怎么改变都不起作用。

八月十四日 等待变化等待机会

关于centos的中文输入法的配置实在是很混乱,总之我折腾了很久但是并不知道绝对正确的设置方法。GTK_IM_MODULE=fcitx XMODIFIERS=@im=fcitx我加上了这些环境变量但是不知道是否有用,期间最后似乎起作用的是im-choose选定了fcitx因为默认的似乎是ibus。总之目前似乎是开始工作了,但是中英文切换并不是完全工作。也许fcitx.conf

八月十五日 等待变化等待机会

在阅读ipmi spec的过程中我有了一个误解,关于SEL的reserve的问题上我对于get的操作是否需要reserve有一个模糊认识,想当然认为应该也需要,实际上ipmitool的实现是遵循spec的,对于没有partial get的操作是不需要reserve的。那么是否真的不需要呢?我后来想还是加上为好,好处是可以不至于在有其他人修改记录的时候来读减少一些错误的机会,但是前提是不要把reserve的id作为参数传入get,这样子的效果是你可以打断正在进行中的修改操作逼迫他们再来一次。

八月二十三日 等待变化等待机会

我想能有一个可靠的centos7的iso来启动livecd以便实验rescue等等,那么官方的livecd是否可以工作呢?我搜索了很久才找到这个似乎是比较可信的,当然也是因为stackoverflow是比较靠谱的,这是我这几年的经验之谈,其他的所谓的linux的论坛都是业余入门级玩家的瞎扯谈,只有stackoverflow是真的专业人士,有一度我想把我的搜索只限制在哪里,当然只是一个想法。究竟行不行我这就实验一下,顺便唠叨一下我的白痴想法,我模仿ubuntu的grub配置自然是不行的因为init不同嘛,比如Ubuntu用casper,而其他人不是啊,参数传递的确是复杂,虽然最终都是给linux kernel传递最关键的root=参数,可是各有巧妙不同。使用cdlabel看来是最容易的?不过我要重启了才知道。用blkid发现iso的cdlabel。

八月二十五日 等待变化等待机会

只取得了部分成功,对于centos/rhel之类的我可以使用cdlabel来设定root,我对于live所指何物还是不清楚。以下是centos的gnome livecd iso的grub启动菜单

menuentry "CentOS 7.5 Live GNome ISO" {
	insmod ext2
	insmod squash4
	insmod iso9660
	insmod multiboot2
	insmod gzio
	insmod xzio
	insmod loopback
	set isofile="/media/CentOS-7-x86_64-LiveGNOME-1804.iso"
	set isolabel="CentOS-7-x86_64-LiveGNOME-1804"
	search --no-floppy --fs-uuid --set aba502d4-8460-48fb-b9d3-201256d5c410
	loopback loop (hd0,5)${isofile}
	linux (loop)/isolinux/vmlinuz0 root=live:CDLABEL=${isolabel} rootfstype=auto ro rd.live.image quiet rhgb rd.luks=0 rd.md=0 rd.dm=0 iso-scan/filename=${isofile}
	initrd (loop)/isolinux/initrd0.img 
}
menuentry "Debian 9.5 live gnome ISO" {
	insmod ext2
	insmod iso9660
	insmod loopback
	set isofile="/media/debian-live-9.5.0-amd64-gnome.iso"
	set isolabel="d-live 9.5.0 gn amd64"
	search --no-floppy --fs-uuid --set 3e00216e-8aa8-4080-a622-3f98ae5921d0
	loopback loop (hd0,5)$isofile
	linux (loop)/live/vmlinuz-4.9.0-7-amd64 boot=live components "findiso=${isofile}"
	initrd (loop)/live/initrd.img-4.9.0-7-amd64
}
核心部分是设定root使用cdlabel就是iso的cdlabel,这个可以使用loop mount然后blkid可以查看。debian应该是一样的。实际上这个是从isolinux的菜单里找到的?但是对于archlinux就不行了。我决定查看一下isolinux的代码看看参数是怎样传递给内核的。然后就眼高手低的发现在centos怎样下载源代码? 1.使用yumdownload下载src rpm,(这里要说明一下,就是怎么寻找一个package的src pkg,使用这个yumdownloader --source pkg-name) 2.安装src rpm: rpm -vih syslinux.rpm。3.编译这一点我喜欢胜过apt-get就是下载安装它的dependency。sudo yum-builddep syslinux.rpm。4.编译,这个比起ubuntu的确好多了,因为省掉了你的各种设置的烦恼,当然啊也少掉了你的自由:cd rpmbuild/SPECS && rpmbuild -bp syslinux.spec && ls ~/rpmbuild/BUILD

八月二十七日 等待变化等待机会

对于archlinux的确费了好大的精力才搞明白了一点点,这个看样子是有一点麻烦的。我发现简单查看syslinux的帮助的确不太大,代码我是没看懂,就看了文档帮助还有点明白吧。总之废话就是说你在使用一个不同的bootloader来做另一个bootloader的工作,这个是有些问题,比如当初设计是使用isolinux来启动cdrom,那么你非要使用iso image,那么不用isolinux你就直奔主题吧,可是你又用的是grub2来呼叫,那么当初所有为isolinux设定的菜单都永不成了,我只能直接呼叫kernel的启动部分,而这个archlinux的livecd的核心是它自己去解决root的问题,也就是说你只需要负责让isolinux把kernel和initrd加载,但是且慢,最重要的是要传入正确的参数因为initrd的核心任务就是正确加载root,这个root在哪里呢?我当然知道它的位置是一个airootfs.sfs的squashfs系统,可是它使用的overlay使得能够读和写,这个部分我始终不知道应该怎样直接面对,都是让initrd自己处理毕竟这个是加载Initrd的一个根本原因,如果那么简单的话还需要这么做嘛?可是说起来简单实际的参数要正确传入才行,我在这个问题折腾了许久,发现还是阅读一点Initrd的Hook代码有很大的帮助,另外这里给了我很大的帮助。你要相信我不是参数越多越好,因为使得其反的,因为参数有一定的依赖关系。

menuentry "Archlinux 2018 LiveCD ISO" {
	set isofile="/media/archlinux-2018.08.01-x86_64.iso"
	search --no-floppy --fs-uuid --set aba502d4-8460-48fb-b9d3-201256d5c410
	set imgdevpath="/dev/disk/by-uuid/aba502d4-8460-48fb-b9d3-201256d5c410"
	loopback loop (hd0,5)$isofile
	linux (loop)/arch/boot/x86_64/vmlinuz img_dev=$imgdevpath img_loop=$isofile
	initrd (loop)/arch/boot/x86_64/archiso.img
}
这里面的一个关键的地方就是要理解initrd的init脚本要把当前的cdrom重新加载如果没有的话,原本使用cdlabel,可是这个不行啊,因为我是iso image,是使用loopmount的,所以,改成了img_dev先去mount,这个是我的含有iso image的分区,hook先把这个分区mount,由于我把iso image文件赋予了img_loop逼迫init把它loopmount了,(你注意到我的烦恼了嘛?我已经Loopmount了iso image,可是在initrd里的init依然需要再次的loopmount,没有办法,可是也许内核很聪明没有重复loopmount吧?因为我在启动时候似乎没有看到两次mount?也许我眼花了?(的确没有连个loopmount因为losetup -a显示的就是一个iso和一个squashfs的mount)以前我还错误的先定义archisobase之类的这个会引起脚本的错误,总之,我折腾到了半夜一点了,有些困了。睡觉吧。
在centos里使用kickstart脚本来制作Image,类似于ubuntu里的bootstrap,不过感觉centos的做法更加好。

八月二十八日 等待变化等待机会

终于我似乎找到了我的前行者,早就有人在做我想做的,就是制作一系列的救援或者说实验性质的liveCD iso启动系统以便我可以使用grub菜单启动到各种livecd

九月三日 等待变化等待机会

redhat的livecd的机制应该是目前我所遇到的最复杂的,至今我也没有找到一个有效的解决办法,问题就是它使用的bootloader是syslinux的情况下,我要使用我的grub怎样做呢?对于isolinux.cfg的参数我现在看到了一点点的方向,首先,initrd.img在isolinux目录下是一个使用dracut制作的initrd,dracut根据其manpage就是专门用来制作initrd的工具,当然它本身就是其中的一部分它甚至可以直接制定live boot的Url,但是我觉得似乎不是这样直接的,因为kernel的命令行参数inst.stage2=是给ananconda的参数,这个是我今天唯一的一点点进展,这个是在liveCD目录下的squashfs里的ext3.img或者名字为rootfs.img的里面的anaconda的脚本里解析这个kernel的参数。为什么?它读取/proc/cmd_line的字串然后split。但是中间的initrd里的dracut是怎样被调用的呢?或者说这个initrd的Init是systemd而对于systemd我很不熟悉不明白它是怎样设定任务来通过dracut的设置来找到并传递root的。

九月九日 等待变化等待机会

对于initrd的理解还是太不够了,这里有一个简单的介绍。其实关于initrd的作用可以总结成一句话就是帮助找到正确的root,可以称之为“寻根之旅”。我找到了一个在线阅读的可是完全读不进一个字。

九月十一日 等待变化等待机会

对于initrd的结构你了解多少呢?我被cpio形式迷惑了好久,才找到这个明白了原来是有一个gzip的cpio接在第一个cpio之后。所以最好的命令是这样子的: (cpio -id; zcat | cpio -id) < /path/to/initrd.img
我不知道为什么写下的笔记丢失了,实际上以上的故事还没有完因为我在qemu模拟器里看到了livecd的iso的image: qemu-system-x86_64 -m 4096M -kernel vmlinuz0 -initrd initrd0.img 。结果我以上部分依旧不能找到microkernel.iso这个文件,于是仔细看看cpio已经创建的block数目和源文件还差得很远,于是我只能一步一步用dd来“扣”出最后的连续接在尾巴的microkernel.iso比如:dd if=actualImage of=micro.iso bs=512 skip=44735这里的44735block是上一次cpio -idv< /path/to/cpio最后显示的block数。第二部还必须先解压缩才能正确的dd取到正确的第三个cpio。

九月十四日 等待变化等待机会

对于redhat/centos的boot的过程的确是有一点点的复杂,因为我首先要明白它依然遵守kernel/initrd的主旋律,只不过这里的initrd都是所有复杂过程的开端,比如在initrd里init可以是systemd,而它是取代传统udev之类的新型启动机制,我正开始读了一个开头,这个就是我的只言片语:init要初始化如此众多而复杂的设备以及无数的服务包括mount各种各样的文件系统,每一个都有可能失败有各种各样的依赖性,而怎样能够加快这个复杂的过程而不是傻傻的等待呢?这就是多线程的大有作为的天地,因为这个本来就是为了加快众多速度缓慢等待的设备通讯而设计的。因此每个线程或者进程都使用socket来通讯,当然这里是否是Unix socket我还没看到,反正这个是最理想的剥离通讯者之间紧密联系的方法,我以前就想到只有这个才能防止通讯者之间crash而互相影响。这个再慢慢看吧。这里遇到的问题是redhat里的initrd里的init实际上是一个installer应该是anaconda或者dracut之类,那么你要通过SOL(serial over lan)来远程观察于是我在kernel的boot parameter加上了console=tty0 console=ttyS1,19200,n这样子kernel的boot message就可以透过SOL看到了,那么kernel结束之后你就看不到了因为init开始了,所以,在kickstart脚本里也添加同样的参数让anaconda也这样,但是这里说init刚开始运行的时候是没有tty0的只有ttyS1。这的确是一个复杂的过程。后来在使用razor的ipxe以及自带的microkernel安装redhat的时候遇到一个Ip分配的问题,就是我在dhcp上只分派一个ip,因为pxe/ipxe/microkernel/redhat都是要使用同一个ip在一串过程,结果发现ipxe的ip没有被释放结果microkernel就分配不到ip了,而microkernel里的systemd只会在启动的时候尝试一次就放弃再次dhcp了。我想这个是正常的。

九月二十一日 等待变化等待机会

我在工作中需要把linux安装的全过程记录下来,当然这个过程是相当复杂的,第一步当然是bootp/dhcp的请求过程,这个过程不用我操心在/var/log/message里通常可以看到dhcp的全过程,然后是ipxe的下载到baremetal的启动过程,它从dhcp服务器通过tftp下载的,然后它自己可以通过razor服务通过Http下载linux的image,使用http比tftp要快很多,当然在这一步之前是一个下载启动microkernel的步骤,它的目的是收集硬件信息,microkernel的启动过程在设定了kernel boot parameter使得console=ttyS1,19200n8,这个是第一步的serial端口输出启动信息。现代服务器有SOL技术可以把serial端口数据转而通过ethernet让你远程访问。当硬件数据收集完毕,razor服务选择正确的要安装的linux的iso image,当然这个是不准确的,实际是razor已经要事先把所谓的iso取出其中类似pxe boot的做法,这里当然就是kernel+initrd,所以这个是一个真正的模仿pxe的流程,应该是ipxe下载他们加载的过程,这个时候有至少两部分的信息,是kernel的部分和initrd的启动部分,因为Initrd很多时候是安装程序比如anaconda/casper之类的,他们使用自己控制的脚本比如kickstart,所以在其中也可以设置serial的参数,同样地服务器使用sol技术让你用ipmi协议可以远程访问这些log message,但是这一切有一个问题就是这些log使用了所谓的vt100之类的ansi-escape-sequence,这个是一个所谓的穷人的图形界面,可以通过这些特殊的escape字串达到控制键盘颜色光标的功能,但是对于我仅仅需要log message的目的他们是极大的噪音尤其如果将来我需要程序过滤监控安装流程的话就是极大的烦恼。所以,我需要过滤他们,我曾经花了很多时间试图从termios/ncurse之类找到接口或者api来解析这些字串,后来发现这是一个非常不值一提的工作难怪大侠们压根就没有想到有人需要如此莫名其妙的需求,所以,我发现最适合的使用regex来过滤。那么在读数据流的时候会遇到partial-match的问题,而这个是折磨我一两天的问题,因为不知道是不是我的boost版本还是表达式本身的歧义或者我设定的标志有问题partialmatch的结果并没有如预期出现。后来我找到一个简单的办法这个也是boost官方网站说明partialmatch的讲解,因为如果match发生在end of line你完全可以谨慎的当作他是partialmatch把它推迟到下一次的数据再来parse。以下就是一个示范因为我现在没有带有vt100的文件就用一个html文件来去除所有的"< >"以及其中的内容。所以这就是以下最重要的那行代码,它花了我一天多才悟出。
#include <string>
#include <iostream>
#include <boost/regex.hpp>

using namespace std;

int main(int argc, char** argv)
{
	if (argc != 2)
	{
		cout << "usage: " << argv[0] << " <filename>" << endl;
		return -1;
	}
	FILE* stream = fopen(argv[1], "r");
	if (stream != NULL)
	{
		string strFormat;
		string strPreviousPartialMatch;
		boost::regex e("<[^>]*>");
		while (!feof(stream))
		{
#define BUFFER_SIZE 255
			char buffer[BUFFER_SIZE+1];
			size_t size = fread(buffer, 1, BUFFER_SIZE, stream);
			if (size > 0)
			{
				buffer[size] = '\0';
				boost::match_results<std::string::const_iterator> what;
				string str = strPreviousPartialMatch + buffer;

				strPreviousPartialMatch.clear();
				string::const_iterator start = str.begin();
				string::const_iterator end = str.end();

				while (boost::regex_search(start, end, what, e, boost::match_default | boost::match_partial))
				{
					strFormat += string(start, what[0].first);
					start = what[0].second;
					if (what[0].second == end)
					{
						strPreviousPartialMatch = string(what[0].first, end);
					}

				}
				if (start != end)
				{
					strFormat += string(start, end);
				}
			}
		}
		fclose(stream);
		cout << strFormat;
	}
	return 0;
}


十月九日 等待变化等待机会

我在旅行中观看抖音下载的令人着迷的短视频,那一句经典的结尾音不断重复:抖音,记录美好生活。的确在当前波诡云谲的中美局势下有很多人很悲观,而我则对于美帝终将被征服充满信心,因为了解中国的现状也许通过这些app更准确。于是我有一个疑惑:我要怎样创建类似于视频文件浏览的那些thumnail呢?第一步还好,第二步是怎样使用这些缩微图呢?
这个是一个脚本创建所有的mp4的预览图for i in $(ls *.mp4); do file=${i%.*}; ffmpeg -y -i $i -vf "select=gte(n\,10)" -vframes 1 ${file}.png ; done
我实际上后来发现不知道为什么缩略图的功能根本不需要我来实现是原本就有的,只不过也许是我的usb存储限制了这个功能的呈现,这个应该是有安全的原因吧。

十月十四日 等待变化等待机会

我实际上至今不明白为什么我的html的video播放有什么错误。实际上html5的播放是依赖于系统的播放吧?我收集了一些yy.com的视频,现在又有点迷恋tiktok的短视频

十月十八日 等待变化等待机会

工作中我需要依靠pci的vendorid和deviceid来标识设备的生产商名称和设备名称于是就写了这么个小程序生成c++的pci-vendorid-deviceid的表。这个是pciid原始的表

十一月五日 等待变化等待机会

boost的确是博大精深,至少我是这样感觉的,单单一个property_tree就让我折腾了这么久,这个文档是很好的入门的学习,实际上我的使用是有一些问题的。我花了很长时间才明白get<string>("path.with.dot")是一个shortcut的这个形式get_child(ptree::path_type("path.with.dot"),'.').get_value<string>()。这个里面的path_type是可以使用不同的demilmiter,默认是'.'。

十一月六日 等待变化等待机会

对于ptree,你的任务无非就是这么几个,怎样创建,怎样遍历。对于前者我感觉更多的是利用它来替代libxml之类的额外的库,比如通过转换xml成一个ptree,你的代码就不需要再去处理究竟是json还是xml的格式了。其中的头文件是一个头疼的问题,我专门列出需要引用的boost头文件。

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <string>
#include <set>
#include <exception>
#include <iterator>
#include <iostream>
#include <vector>


using namespace std;
using namespace boost;
using namespace boost::property_tree;
using namespace boost::filesystem;
using namespace boost::posix_time;
怎样遍历目录结构并把文件系统输出成json形式呢?对于directory_iterator来说我还不明白它和DIR里的entry有什么区别呢?

void getFile(const string& strFileName, ptree& pt)
{
	filesystem::path p(strFileName);
    if (!filesystem::exists(p))
    {
    	return;
    }

    if (!filesystem::is_directory(p))
    {
    	return;
    }
    ptree subTree;
    filesystem::directory_iterator dir = filesystem::directory_iterator(p);
    for (filesystem::directory_iterator it = dir; it != filesystem::directory_iterator(); it ++)
    {
    	if (filesystem::is_directory(it->status()))
    	{
    		getFile(it->path().string(), pt);
    	}
    	else
    	{
    		ptree filenode;
    		if (filesystem::is_regular_file(it->status()))
    		{

    			//make_pair(it->path().filename().string()
    			filenode.push_back(make_pair("file_size", lexical_cast<std::string>(filesystem::file_size(it->path()))));
    			filenode.push_back(make_pair("hard_link_count", lexical_cast<std::string>(filesystem::hard_link_count(it->path()))));
    			ptime now = from_time_t(filesystem::last_write_time(it->path()));
    			//cout << now << endl;
    			filenode.push_back(make_pair("last_write_time", lexical_cast<std::string>(now)));

    			subTree.put_child(ptree::path_type(it->path().filename().string(), '/'), filenode);

    		}
    		else
    		{
    			if (filesystem::is_symlink(it->status()))
    			{
    				filenode.push_back(make_pair("read_symlink", filesystem::read_symlink(it->path()).string()));
    				subTree.put_child(ptree::path_type(it->path().filename().string(), '/'), filenode);
    			}
    			else
    			{
    				if (filesystem::is_other(it->status()))
					{
						// ???
					}
    			}
    		}
    	}
    }

	property_tree::path treePath(p.string(), '/');
	pt.add_child(treePath, subTree);
}
怎样遍历目ptree然后输出json形式呢?我参考了boost里write_json函数的做法。

void traverseTree(const ptree& pt, int indent = 0);


void traverseTree(const ptree& pt, int indent)
{
	if (pt.empty())
	{
		cout << "\"" << pt.get_value<string>() << "\"" << endl;
	}
	else
	{

		cout << "{" << endl;
		for (ptree::const_iterator it = pt.begin(); it != pt.end(); it ++)
		{
			const ptree& sub = it->second;
			cout << string(indent+4, ' ');
			cout << "\"" << it->first << "\":";

			traverseTree(sub, indent+4);
		}
		cout << string(indent, ' ');
		cout << "}" << endl;
	}
}

十一月十三日 等待变化等待机会

就如同读论文一样的道理,很多时候一篇好的论文更多的是通过她的引用带你进入更多的相关文献,我对于smbios至今也之停留在缩写的理解上,这个东西其实很基础很重要,回答了不少的基本问题。我现在更像一个计算机考古学的学生学习的大都是上个世纪就存在并成熟的文献资料。关于这个bis我看了一眼就不愿意再看了,因为这个太高级了如同tpm之类的不是我辈所能有条件玩的,安全是在轻松驾驭之后的课题。其实一切都归因于我对于不工作的bmc的拯救努力,这个是asus关于asmb4的文档,很多年前我甚至不明白它的用途就买回来。其实至今我对于他的工作原理依然不清楚需要读一下这个手册。(大致看了一下明白这个东西基本无用被骗了,当年完全不了解bmc的功能,这个就是一个kvm只不过让你可以远程使用的而已的ikvm,如果你有了bmc你是不需要这个的了。)因为我看到dmidecode --type 38的输出结果显示我有ipmi device,但是内核初始化失败,这个内容是smbios的信息,可是准确吗?
Handle 0x0062, DMI type 38, 18 bytes
IPMI Device Information
	Interface Type: KCS (Keyboard Control Style)
	Specification Version: 2.0
	I2C Slave Address: 0x00
	NV Storage Device: Not Present
	Base Address: 0x0000000000000CA2 (Memory-mapped)
	Register Spacing: Successive Byte Boundaries
我现在明白了一点的是所谓的/etc/modprobe.d/ipmi.conf里的配置仅仅是给内核模块一个提示不要去使用其他的探索模式,直接使用hardcoded address: options ipmi_si type=kcs ports=0xca2我原来以为这个是内核代码的hardcoded地址,现在明白不是的,他是smbios里的,那么这个地址是那里的呢?我google了,有人说ipmi_si: Could not set up I/O space是因为内存io地址冲突,查询/proc/ioports看不出有什么冲突,可是这里不明白的是明明是memory-mapped为什么说是io?我一直对于这个项目有种莫名的期待,也许原因是我的主板是他们的发展对象

十一月十四日 等待变化等待机会

对于这个ipmb地址分配表我感到很茫然,这个地址是内存映射的地址吗?

十一月十九日 等待变化等待机会

我使用一款老式的toolchain大约是gcc4.2版本的吧,在编译使用boost ptree的时候爆出warning of limited type,google之后也没有办法解决,猜想是编译器太老了这个warning的surpress的参数也不存在,不愿意去修改代码削足适履,后来终于找到使用-isystem来指明boost的directory是系统这样就压制了第三方的代码的warning。这个是一个非常有用的参数。

十一月二十八日 等待变化等待机会

我对于这个项目总是充满了好奇与期待,但是这个应该是非常复杂的一个领域。不过世界上的事情就是这样子的,当你在窗外翘望的时候总有很多不切实际的渴望,走进橱窗发现店里卖的东西确实另一个东西。不过它把我引导了coreboot这个才是真正的宝贝,于是我又开始了我的搬砖工作。我为了检查网页需要本地运行一个之简单的webserver,就选用lighttpd,但是这么简单的配置还让我头疼:
server.document-root = "BigDisk/diabloforum/public_html/"
server.port = 80
index-file.names            = ("index.htm")
mimetype.assign = (
  ".html" => "text/html",
  ".txt" => "text/plain",
  ".jpg" => "image/jpeg",
  ".png" => "image/png",
  ".htm" => "text/html"
)

十一月三十日 等待变化等待机会

为了编译coreboot我只好重新安装ubuntu16.04因为14.04很多库不支持了。总之,Linux就是折腾。那么所有的设置都要重新配置,其中观察到有firmware的Missing,这个其实也不难理解,无非是Ubuntu发行版遗漏了,当然我看到kernel.org官方也没有,所以不是ubuntu的错,那么参考supermicro客服给的ast的ast_dp501_fw.bin的下载链接拷贝到/lib/firmware下面就是了。安装输入法发现fcix能够支持googlepinyin的确好多了,所以升级还是有益处了。另外就是eclipse我决定使用neon最新版,当然需要安装默认的default-jre,在launcher上的图标没有那么我就要自己在~/.local/share/applications/eclipse.desktop里设定一个icon的路径。flashrom也是coreboot用到的一个工具,这个也是很值得学习的,不过我差的太远了,心有余而立不足矣。不过能够体验一下coreboot在qemu的-bios下模拟一下coreinfo作为payload我觉得今天的收获已经很大了。有了qemu这样的神器几乎就是如虎添翼的差别。

十二月六日 等待变化等待机会

关于怎样建立一个local的repo是这样子的:
  1. Mount the RHEL 7 installation ISO to a directory like /mnt, e.g.:
    
    # mount -o loop RHEL7.1.iso /mnt
    If you use DVD media , you can mount like below.
    # mount /dev/sr0  /mnt
  2. Copy the media.repo file from the root of the mounted directory to /etc/yum.repos.d/ and set the permissions to something sane, e.g.:
    
    # cp /mnt/media.repo /etc/yum.repos.d/rhel7dvd.repo
    # chmod 644 /etc/yum.repos.d/rhel7dvd.repo
  3. Edit the new repo file, changing the gpgcheck=0 setting to 1 and adding the following 3 lines
    
    enabled=1
    baseurl=file:///mnt/
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
    
    In the end, the new repo file could look like the following (though the mediaid will be different depending on the version of RHEL):
    [InstallMedia]
    name=DVD for Red Hat Enterprise Linux 7.1 Server
    mediaid=1359576196.686790
    metadata_expire=-1
    gpgcheck=1
    cost=500
    enabled=1
    baseurl=file:///mnt/
    gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
  4. clear the related caches by yum clean all and subscription-manager clean once
    
    # yum clean all
    # subscription-manager clean
  5. check whether you can get the packages list from the DVD repo
    
    # yum  --noplugins list
  6. if no problem , you will update
    
    # yum  --noplugins update

十二月十一日 等待变化等待机会

对于ftp的机制一直鲜有了解,直到因为扫描仪必须使用ftp上传图片需要配置才意识到实际上很多时候为了安全性ftp用户是chroot的一个jail。

十二月二十三日 等待变化等待机会

我终于解决了我对于windows的几乎是最后一个需求,就是HOMM3的难以舍弃,终于在升级到ubuntu19.04后发现winehq相当的稳定。不过这个所谓的“稳定”的结论是通过一个反例证明的,因为一开始我经常遇到屏幕死机的问题,并不是长时间待机造成的,而是运行一段时间出现显卡的错误:radeon VCE resume error. 我google后意识到这个不是wine本身的问题,而是显卡的问题,我的笔记本是radeon R7 M265,但是使用amd显卡下载界面只有Ubuntu14.04的支持,使用所谓opensource的ubuntu18.04的默认显卡驱动并不能解决这个问题,终于又google才找到这个,这里我保存一个拷贝吧。然后wine运行一直没有出现错误,接下去我可以测试一下wine在linux下运行和原生的window联网的成果。不过这个都不重要了。因为wine现在已经很成熟了,我衷心的感谢这些developer,kudos。在游戏战略里你常常观察到电脑玩家不停地派出小股部队来骚扰或者是抢占资源,每当你想要挥重兵直捣黄龙时都不得不回师救援,,这个其实就是战略的一部分,如果敌人不是如此不断的骚扰,我军已经可以直接消灭敌人巢穴了。与时间赛跑,现在就是中美的最后决战的前夜。

Smiley face