Skip to content

Declarations and Statements

Declarations

注:Declarations不能在function、hook、或者handler中使用,所有并行加载的zeek脚本Declarations必须在任何语句之前使用,function、hook、或者handler中的语句除外

名字 描述
module 改变当前的module
export 从当前模块到处标识符
global 声明一个全局变量
const 声明一个常量
option 声明一个配置项
type 声明一个用户定义类型
redef 重新定义一个全局值或者一个用户定义类型
function/event/hook 声明一个function、event处理程序或一个hook
  1. module

module关键字用来改变当前的模块,这么做会影响后面声明的全局标识符,例:

module mumodule;
如果一个全局标识符声明在一个module后面,那么他的作用域在zeek脚本的末尾(这里一般是一个插件声明一个module,他只负责处理一个事情)或者下一个模块结束(有点像来说就是在一个方法里面声明了一个值,然后出了这个方法就不让用了,但是要是就这一个方法的话就是执行到程序结束),这东西有个特例,就是在export 的语句块中他的作用域就大了,是最后一个加载的插件结束,但是其他的引用的时候必须通过 :: 这个符号进行引用,在zeek的脚本中,可以有多个module声明,同一个module也可以下载多个不同的bro脚本之中(这个说写到多个脚本之中可以参考c# 的particle类,差不多是一个意思)

  1. export export块中可以写当前module要导出的一个或者多个变量值,其他的module直接通过:: 进行引用即可,例:

    export {
        redef enum Log::ID += { LOG };
    
        type Info: record {
            ts: time &log;
            uid: string &log;
        };
    
        const conntime = 30sec &redef;
    }
    

  2. global 使用global关键字的变量具有全局作用域,如果变量未指定类型,那就需要初始化程序,以便可以推断该类型。同样,如果未提供初始化程序则必须指定变量类型。在某些情况下,当类型不能够准确的推断出来时,即使存在初始化程序也要指定变量类型,例:

    global pi = 3.14;
    global hosts: set[addr];
    global ciphers: table[string] of string = table();
    
    必须在任何function、hook、event之外的变量声明时才能使用这个关键字(除非使用const声明过),不允许使用global关键字定义function、hooks和event,但是未写主体只有声明的function可以用。全局变量的范围是从他声明的地方开始到所有已加载的zeek脚本

  3. const 在zeek的脚本语法中const的用法和c的const的普通使用方式是有很大相似之处的,都是作为常量的标识符。变量被const修饰时需要在声明时进行初始化,通常从初始化程序中推断类型,也可以手动指定类型,例:

    const pi = 3.14;
    const ssh_port: port = 22/tcp;
    
    常量值一旦被声明名就不能更改,但是有例外,就是全局常量并且用& 属性声明(这里有一个要注意,全局的常量不是需要用global声明而是不在方法体内声明的就是全局的),但是只能使用&redef来进行更改。常量值在function、hook、event中声明时,他的作用域就是声明的方法体内,要不然就是全局的。 注意:const和local及global关键字不能一起使用,意思就是const可以代替他们两个中的任意一个

  4. option 用option修饰的变量是配置项,配置项需要在声明的时候进行初始化。一般来说,配置项的类型是由构造器推断的,但是也支持明确指定类型,例:

    option hostname = "host-1";
    option peers: set[addr] = {};
    
    初始值能够使用redef关键字进行重新赋值,选项的值不能被赋值语句更改,但是能够使用Config::set_value函数或Config::config_files中指定的配置进行修改。option的作用域是全局 注意:option关键字不能和local、global、const关键字一起使用,即互斥

  5. type type关键字用来声明一个用户自定义的类型,type声明的类型具有全局的作用域,他可能会出现在任何可能出现内置类型的地方。type关键字一般在定义record或者枚举类型时使用。例:

    type mytype: table[count] of table[addr, port] of string;
    global myvar: mytype;
    

  6. redef 使用redef关键字的几个方法: (1) 对全局变量或者运行时配置重新定义初始值 (2) 扩展record或枚举类型 (3) 新定义一个event主体,代替原本的event主体 如果使用redef去重新定义全局变量的初始值,那么该变量必须有&redef属性。可以改变没有&redef属性的运行时配置项。如果使用redef去重新定义各种集合类型(table、set、vector、parrtern)可以使用+=添加元素或者使用=指定一个新值,使用-=删除指定的 元素,如果要更改的变量不是集合类元素则必须用=。例:

    redef pi = 3.14;
    redef set_of_ports += { 22/tcp, 53/udp };
    
    如果使用redef扩展record或者enum类型则必须使用+=赋值运算符。对于枚举,可以添加多个枚举常数,对于record可以添加多个记录字段(但是redef中的每个记录字段必须具有&option或&default属性),例:
    redef enum color += { Blue, Red };
    redef record MyRecord += { n2:int &optional; s2:string &optional; };
    
    如果使用redef去指定一个新的事件处理程序体用以替代以前定义的所有事件处理程序体(就是后弄的会把前面的给覆盖,就像js的var一样),则语法与常规的事件处理程序一样,但是存在redef关键字。例:
    redef event myevent(s:string){print "Redefind",s;}
    

Statements

statements(function、hook、event中包含的语句除外)只能在所有已加载的zeek脚本的所有全局变量声明之后出现。每一个语句都必须以分号终止(有一些例外),单个语句可以跨越多行。zeek脚本语言支持的语句如下: |名称|描述| |:--:|:--:| |local|声明一个本地变量| |add,delete|添加或删除元素| |print|打印到标准输出或文件| |for、while、next、break|就是正常的循环| |if|| |switch、break、fallthrough|评估表达式并执行带有匹配值的语句| |when|异步执行 |event,scheme|调用事件处理程序| |return||

  1. add add语句用于给set添加一个元素,如果值已经存在于集合中,那将什么都不会发生。例:

    local myset: set[string];
    add myset["test"];
    

  2. break 和其他语言一样就是为了跳出循环。

  3. delete delete语句用于给set或table移除元素,或者从record中移除一个有 &optional 属性的值。在set或table中,如果要移除的目标不存在则什么也不会发生。如果要从一个有 &optional属性的record中移除的值不存在则什么也不会发生。例:

    local myset = set("this", "test");
    local mytable = table(["key1"] = 80/tcp, ["key2"] = 53/udp);
    local myrec = MyRecordType($a = 1, $b = 2);
    
    delete myset["test"];
    delete mytable["key1"];
    
    # In this example, "b" must have the "&optional" attribute
    delete myrec$b;
    

  4. event

事件语句立即将事件处理程序的调用排队(没看出了和之前的event有什么区别,也没看出来有什么不同),例:

event myevent("test", 5);
5. fallthrough 加了这玩意就是case执行了之后接着往下执行,而不是直接跳出

  1. for 和其他语言的差不多就是table类型的不太一样,举个例子:
    local myset = set(80/tcp, 81/tcp);
    local mytable = table([10.0.0.1, 80/tcp]="s1", [10.0.0.2, 81/tcp]="s2");
    
    for ( p in myset )
        print p;
    
    for ( [i,j], val in mytable )
        {
        if (val == "done")
            break;
        if (val == "skip")
            next;
        print i,j;
        }
    
  2. if 和其他语言的一样

  3. local 使用local修饰的变量是局部变量,如果类型未被指定则需要初始化程序,以便于推断该类型。同样的,如果未指定初始化程序则必须指定类型,例:

    local x1 = 5.7;
    local x2: double;
    local x3: double = 5.7;
    
    此关键字只能在function、hook、event内部声明变量(用const声明的变量和for中隐式声明的变量除外),此变量的作用域就是普通的局部变量作用域,在方法体内

  4. next 和其他语言的continue差不多

  5. print 正常的打印语句

  6. return 和其他语言的也差不多

  7. schedule 就是个定时器,例:

    schedule 30sec { myevent(x, y, z) };
    
    注意:

  8. 花括号必须写,里面也不能写多条。
  9. schedule执行后返回的是一个timer类型,但是实际上不使用这个返回值

在zeek_init中尽量不要使用schedule,因为这个时候network_time 还没有进行初始化,可以在处理第一个网络数据包时进行调度,这能使网络时间从零更新为与捕获该数据包的时间相关的时间。

  1. switch 和其他语言的基本上差不多,这里的fallthrough就是执行完当前的case继续往下执行,没有default的话会跳出,例:
    switch get_day_of_week() {
        case "Sa", "Su":
            print "weekend";
            fallthrough;
        case "Mo", "Tu", "We", "Th", "Fr":
            print "valid result";
            break;
        default:
            print "invalid result";
            break;
    }
    
    这里的case可以同时匹配多项,用逗号分隔,一个case可以是表达式但是必须是常量表达式。每一个case和default结尾必须是break、fallthrough、return(return必须在function、event、hook中才能用)。必须使用大花括号给他括上,末尾不需要分号。 zeek的switch还支持通过类型进行判断:
  2. case type t:如果switch的表达式能够强转成t就用这个case
  3. case type t as x: 和上面一样,但是能够通过x获得switch1表达式转过来的值 例:

    function example(v: any)
        {
        switch (v) {
        case type count as c:
                print "It's a count", c;
                break;
    
        case type bool, type addr:
                print "It's a bool or address";
                break;
        }
        }
    
    switch虽然可以多值,但是要么使用常量表达式,要么就是type不能两个一起用;如果是基于类型的switch,那么就必须得有一个是匹配的,要不然就会报错(runtime error),也不能使用fallthrough关键字,因为这个值能转换两个不同的类型(按我的想法,1可以转换为string,也可以转为int或者count,当然,正常人不会这么写)

  4. when when关键字的作用看文档特别像if,就是判断括号里的表达式是true还是false然后执行对应的代码,例:

    when ( (local x = foo()) && x == 42 )
        {
        print x;
        }
    
    这里开始不一样了,有个超时的关键字:timeout。如果指定了超时,并且表达式在指定的超时时间内仍未完成计算,那么就会执行timeout下面的语句,例:
    when ( (local x = foo()) && x == 42 )
        {
        print x;
        }
    timeout 5sec
        {
        print "timeout";
        }
    
    when语句的表达式可以包含一个局部变量的声明,但是只能用local *var* = *init*这种方式(例:local x = myfunction())这种的声明实际上是一个表达式,结果始终是true值 when语句中的表达式也可以包含一个异步调用的声明例如lookup_hostname (就这一个方法能调用此功能),也可以包含一个普通的方法进行回调。当一个异步脚本在表达式中被调用后,zeek会继续处理脚本中的语句,然后当 函数调用的结果可用时,zeek将在when语句中完成对表达式的求值

  5. while 这里的while和其他语言中的while一样,但是如果想要跳过本次循环继续下一次循环的关键字是next,例:

    local i = 0;
    
    while ( i < 5 )
        print ++i;
    
    while ( some_cond() )
        {
        local finish_up = F;
    
        if ( skip_ahead() )
            next;
    
        if ( finish_up )
            break;
        }
    

  6. Compound Statement 复合语句由在一个大括号{}中的一条或多条语句进行创建。大括号中的语句之间以分号结束,在大括号的末尾不需要分号,其实就是正常的往下写语句。

  7. Null Statement 这玩意就是高级语言中的一行语句只写一个分号,一般没人用这东西

Comments