@(教程)[Bro]
Name | Description |
---|---|
module | 改变当前模块 |
export | 从当前模块导出定义 |
global | 声明一个全局变量 |
const | 声明一个全局常量 |
type | 声明一个一用户自定义类型 |
redef | 重新定义一个全局值或者扩展一个用户自定义类型 |
function/event/hook | 声明一个 function/event/hook |
Name | Description |
---|---|
local | 声明一个局部变量 |
add, delete | 添加或删除元素 |
打印到标准输出或文件 | |
for, while, next, break | 遍历所有容器的元素(for), 条件循环(while). |
if | 表达式bool条件判断执行 |
switch, break, fallthrough | 计算表达式和匹配值执行语句 |
when | 异步执行 |
event, schedule | 调用或调度的事件处理程序 |
return | 从 function, hook, 或event 处理程序返回 |
不能在 function, hook, event 处理函数中使用声明
必须在语句之前使用声明
module
关键字module 用于改变当前的module,这会影响任何随后全局标识符作用范围
例子:
module mymodule
如果一个全局的定义(global)声明在module的后面,那他的作用域在Bro脚本结束的地方或者在下一个module声明的位置,如果一个全局的声明在module的后面,但是在export 语句块中,那么它的作用域将扩展到最后一个load的bro脚本结束,但是在其他的module中它必须通过命名空间操作符(::)来进行引用
在一个bro脚本中,可以有多个module声明,同一个module也可以写在多个不同的bro脚本中
export
export 语句块将当前module中的一个或者多个声明(不能将语句块写在其中)导出,在其他module中,这些全局标识通过操作符”(::)”变得可见
例子:
export {
redef enum Log::ID += {LOG};
type Info: record {
ts: time &log;
uid: sting &log;
};
const conntime = 30sec &redef;
}
注意,在导出块中括号是必须的,而且这个语句块也不需要用分号结束
global
声明一个全局变量
如果在声明的时候不指定类型,那么一定要赋一个初值,bro会实现类型推断,否则就一定要指定类型,或者在类型不能够准确推断的时候,也需要指定类型,例:
global pi = 3.14;
global host: set[addr];
global ciphers:table[sting] of sting = table();
在function ,event ,hook 外面的变量通常都需要用global 关键字(或者使用const 关键字);
在function ,event,hook 处理程序里面则不允许使用这个关键字,另外function 类型(不是函数体)也是可以通过global 关键字声明的.
global 关键字声明的变量作用域从声明的地方开始,到载入的脚本结束(同时,查看module 一节中的描述,当mudule改变的时候,对变量生命周期的改变)
const
const 用于声明一个常量
要求在声明的时候赋初值,同样的,类型可以被推断也可以准确指定
const pi = 3.14;
const ssh_port: port = 22/tcp;
常量的值不可以被改变,唯一的例外是当它是一个全局的常量同时含有 &redef 属性的时候,但是及时这样,他的值只能用 redef 来修改
常量如果定义在function,event, hook中,那么他的作用域就在处理函数内,否则它就是全局的。
注意到const 不能再被local 或者 global 修饰
type
type 关键字用于声明一个用户自定义的类型,新的类型将拥有全局的作用域,也可以用在任何内建类型出现的地方
type 关键字通常被用于定义 record 或者 enum , 当然在处理复杂类型的时候也很有用
例子:
type mytype:table[count] of table[addr,port] of string;
global myvar: mytype;
redef
有三种方式来使用redef :
用于改变全局变量的值(含有&redef 属性的变量)
用来扩展 record 类型或者 enum 类型
用来明确指定一个新的event处理函数替代之前所有的处理函数
如果你使用 “redef” 来改变一个全局变量(global 或者 const ),那么这个变量需要有&redef 属性,如果要修改的变量是 table,set,或者是pattern,就必须使用 +=操作符来添加新成员,否则直接用=号的话将会分配一个新的值,如果操作的对象是 table 或者set,可以使用-=操作符来移除已有的元素,其他情况下,你要通过=号来重新赋值,例:
redef pi = 3.14;
如果你用redef 来扩展 record 或者 enum,那么你必须使用 +=操作符,对于enum来说,你可以添加更多的枚举常量,对于record ,你可以添加更多的record字段,这时候,通过redef添加的字段,必须含有 &optional 或者 &default 属性,例:
redef enum color += {Blue,Red};
redef record MyRecord += {n2:int &optional;s2:string &optional;};
如果你使用redef 来替换event的处理函数,之前的函数将被完全移除(这个语句后的子event处理函数不会受到影响),语法和常规的event 处理函数的定义完全一样除了前面加了 ”redef“:
redef event myevent(s:string) {print "Redefined",s;}
function/event/hook
参考数据类型一节
除了在 function , hook ,event 处理函数中,Statement 只可以出现在所有级联载入(loaded)的Bro 脚本的所有全局声明的后面
所有的Statement必须使用分号结束,单独的语句可以分多行;
Bro 支持的 Statement
add
添加元素到set中
local muset:set[string];
add myset["test"];
break
用于 switch, for, while 语句中
delete
用于从 set 或者 table 中移除元素,或者移除record中具有&optional 属性的字段
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"];
delete myrec$b;
event
用于立即触发一个event处理函数
event myevent("test",5);
fallthrough
用于在swith语句中的case块的尾部,指示执行下一个case 或者 跳到default 标签
for
迭代 string,set,vector,或者table 中的每一个元素并循环执行语句(注意set或者table 中的元素顺序是不确定的),如果元素为空,则不会循环
对于每个迭代对象,如果迭代对象是string,或者是set会分配一个循环变量保存循环中的元素,如果是vector或者是table 的话,将会是一个索引(index).
如果表达式含有多个索引的 table或者是set,,那么循环变量需要是括号内用分号分割的列表
注意 for中的循环变量不能是全局的,而且也不需要在for语句之前提前声明,他的类型会自动推断
同时,也不要在循环语句中添加或删除元素,不然会发生未定义的行为
break 语句 可以立即终止for 循环,next 语句可以用于跳过继续执行
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] in mytable) {
if(mytable[i,j] == "done")
break;
if(mytable[i,j] == "skip")
next;
print i,j;
}
if
判读一个返回bool的表达式,执行语句
if (x == 2) print "x is 2";
同时,支持 else 语法
if (x == 2)
print "x is 2";
else
print "x is not 2";
local
声明一个本地变量,可以通过初始化值自动推断类型,否则要明确指定类型
local x1 = 5.7;
local x2:double ;
local x3:double = 5.3;
hook ,event,function 处理函数中,要求使用local 关键字来声明变量(除了const 声明和 for 语句中的隐式声明)
local 变量的作用域就仅限于声明的function,event,hook 处理函数中
next
用于 for 和 while 循环中,跳过语句继续执行循环
print
带一个逗号分割的列表表达式,所有表达式将会转换为stirng 并被打印
print 3.14;
print "Results:",x,y;
默认是打印到标准输出,如果第一个表达式是 file 类型,那么就会写到文件中去
如果string 中含有不可打印的字符,那么会被转换成对应的转义字符打印
对于string 的格式化,参考内建函数 fmt (base/bif/bro.bif.bro)
return
用于立即退出function,hook或者event处理函数,在function中如果存在返回值,将明确返回一个表达式或值,在hook和event中不会返回任何值,
function my_func(): string
{
return "done";
}
event my_event(n:count)
{
if(n == 0) return;
print n;
}
return 语句和 when 语句搭配使用,延时return , 当某个条件满足的时候才return ,或者超时return
global X:table[string] of count;
function a() :count
{
return when ("a " in X)
{
return X["a"];
}
timeout 30 sec
{
return 0;
}
}
event bro_init()
{
when ( a() == 42)
print "expected result";
print "Waiting for a() to return....";
X["a"] = 42;
}
schedule
通过 指定 interval类型的时长延时触发event
schedule 30sec {myevent(x,y,z)};
注意这里大括号是必须的
schedule 表达式会返回一个timer类型的数据,但是一般不用
switch
跳转和匹配语句,匹配不到执行default,没有default 则什么也不执行
所有标签的类型必须和表达式的返回值一致
例(假设get_day_of_week返回一个string)
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标签可以匹配多个用逗号分割的表达式
标签必须是常量表达式
每个标签都必须使用 break,fallthrough,return 中的一个结束(return 只能用于function,hook,event中)
大括号是必须的
when
计算一个返回bool的表达式,当返回真或者表达式有效的时候,执行指定的语句
when ( (local x = foo()) && x == 42)
print x;
如果指定了timeout,那么在指定时间内不能返回T,则执行timeout 语句块
when ( (local x = foo()) && x == 42 )
print x;
timeout 5sec {
print "timeout";
}
timeout 语句块中大括号是必须的
when 语句中可以进行变量的声明
when 语句中可以包含异步函数调用比如lookup_hostname(事实上,这也是这种函数唯一可以被调用的地方),也可以包含普通的函数,当一个异步函数被调用,Bro会继续执行when下面的语句块,当函数返回的时候,Bro才会计算when 语句块中的结果,return 章节介绍了如何创建一个异步函数
while
while 条件循环语句
break 语句可以随时终止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;
}
compound statement
复合语句
通过大括号封装一个或多个语句
大括号语句都需要用分号来结束,但是括号外不需要分号
复合语句通过for,while,if, when 执行多条语句
if ( x == 2 ) {
print "x is 2";
++x;
}
null statement
空语句,只有一个分号的语句
if ( x == 2 )
;