line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Snort::Rule;
|
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Snort::Rule - Perl extension for dynamically building snort rules
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Snort::Rule;
|
10
|
|
|
|
|
|
|
$rule = Snort::Rule->new(
|
11
|
|
|
|
|
|
|
-action => 'alert',
|
12
|
|
|
|
|
|
|
-proto => 'tcp',
|
13
|
|
|
|
|
|
|
-src => 'any',
|
14
|
|
|
|
|
|
|
-sport => 'any',
|
15
|
|
|
|
|
|
|
-dir => '->',
|
16
|
|
|
|
|
|
|
-dst => '192.188.1.1',
|
17
|
|
|
|
|
|
|
-dport => '44444',
|
18
|
|
|
|
|
|
|
);
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
$rule->opts('msg','Test Rule"');
|
21
|
|
|
|
|
|
|
$rule->opts('threshold','type limit,track by_src,count 1,seconds 3600');
|
22
|
|
|
|
|
|
|
$rule->opts('sid','500000');
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
print $rule->string()."\n";
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
OR
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$rule = 'alert tcp $SMTP_SERVERS any -> $EXTERNAL_NET 25 (msg:"BLEEDING-EDGE POLICY SMTP US Top Secret PROPIN"; flow:to_server,established; content:"Subject|3A|"; pcre:"/(TOP\sSECRET|TS)//[\s\w,/-]*PROPIN[\s\w,/-]*(?=//(25)?X[1-9])/ism"; classtype:policy-violation; sid:2002448; rev:1;)';
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$rule = Snort::Rule->new(-parse => $rule);
|
31
|
|
|
|
|
|
|
print $rule->string()."\n";
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
This is a very simple snort rule object. It was developed to allow for scripted dynamic rule creation. Ideally you could dynamically take a list of bad hosts and build an array of snort rule objects from that list. Then write that list using the string() method to a snort rules file.
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=cut
|
38
|
|
|
|
|
|
|
|
39
|
1
|
|
|
1
|
|
46336
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
40
|
1
|
|
|
1
|
|
5
|
use warnings;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1745
|
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
our $VERSION = '1.07';
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Put any options in here that require quotes around them
|
45
|
|
|
|
|
|
|
my @QUOTED_OPTIONS = ('MSG','URICONTENT','CONTENT','PCRE');
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 OBJECT METHODS
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=head2 new
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Reads in the initial headers to generate a rule and constructs the snort::rule object around it.
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Accepts:
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
-action => [string] ? [alert|log|pass|...] : 'alert'
|
56
|
|
|
|
|
|
|
-proto => [string] ? [ip|udp|tcp|...] : 'IP'
|
57
|
|
|
|
|
|
|
-src => [string] ? [$strIp] : 'any'
|
58
|
|
|
|
|
|
|
-sport => [int] ? [$sport] : 'any'
|
59
|
|
|
|
|
|
|
-dir => [string] ? [->|<-|<>] : '->'
|
60
|
|
|
|
|
|
|
-dst => [string] ? [$strIp] : 'any'
|
61
|
|
|
|
|
|
|
-dport => [int] ? [$dport] : 'any'
|
62
|
|
|
|
|
|
|
-opts => [hashref] ? [hashref] : '';
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
-parse => $strRule # for parsing an existing rule into the object
|
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Returns: OBJECTREF
|
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=cut
|
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub new {
|
71
|
0
|
|
|
0
|
1
|
|
my ($class, %parms) = @_;
|
72
|
0
|
|
|
|
|
|
my $self = {};
|
73
|
0
|
|
|
|
|
|
bless($self,$class);
|
74
|
0
|
|
|
|
|
|
$self->init(%parms);
|
75
|
0
|
0
|
|
|
|
|
$self->parseRule($parms{-parse}) if($parms{-parse});
|
76
|
0
|
|
|
|
|
|
return ($self);
|
77
|
|
|
|
|
|
|
}
|
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# INIT
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub init {
|
82
|
0
|
|
|
0
|
0
|
|
my ($self,%parms) = @_;
|
83
|
0
|
0
|
|
|
|
|
$parms{-action} = $parms{-action} ? $parms{-action} : 'alert';
|
84
|
0
|
0
|
|
|
|
|
$parms{-proto} = $parms{-proto} ? $parms{-proto} : 'IP';
|
85
|
0
|
0
|
|
|
|
|
$parms{-src} = $parms{-src} ? $parms{-src} : 'any';
|
86
|
0
|
0
|
|
|
|
|
$parms{-sport} = $parms{-sport} ? $parms{-sport} : 'any';
|
87
|
0
|
0
|
|
|
|
|
$parms{-dir} = $parms{-dir} ? $parms{-dir} : '->';
|
88
|
0
|
0
|
|
|
|
|
$parms{-dst} = $parms{-dst} ? $parms{-dst} : 'any';
|
89
|
0
|
0
|
|
|
|
|
$parms{-dport} = $parms{-dport} ? $parms{-dport} : 'any';
|
90
|
|
|
|
|
|
|
|
91
|
0
|
0
|
|
|
|
|
$parms{-opts} = '' if(!(ref($parms{-opts}) eq 'HASH'));
|
92
|
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
|
$self->action( $parms{-action});
|
94
|
0
|
|
|
|
|
|
$self->proto( $parms{-proto});
|
95
|
0
|
|
|
|
|
|
$self->src( $parms{-src});
|
96
|
0
|
|
|
|
|
|
$self->sport( $parms{-sport});
|
97
|
0
|
|
|
|
|
|
$self->dir( $parms{-dir});
|
98
|
0
|
|
|
|
|
|
$self->dst( $parms{-dst});
|
99
|
0
|
|
|
|
|
|
$self->dport( $parms{-dport});
|
100
|
0
|
|
|
|
|
|
$self->opts( $parms{-opts});
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
}
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head2 string
|
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
Outputs the rule in string form.
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
print $sr->string()."\n";
|
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Prints "options only" string:
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
print $sr->string(-optionsOnly => 1)."\n";
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=cut
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub string {
|
117
|
0
|
|
|
0
|
1
|
|
my ($self,%parms) = @_;
|
118
|
0
|
|
|
|
|
|
my $rule = '';
|
119
|
|
|
|
|
|
|
|
120
|
0
|
0
|
|
|
|
|
$rule = $self->action().' '.$self->proto().' '.$self->src().' '.$self->sport().' '.$self->dir().' '.$self->dst().' '.$self->dport().' (' unless($parms{-optionsOnly});
|
121
|
0
|
|
|
|
|
|
my @sort = sort { $a <=> $b } keys %{$self->opts()};
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
122
|
0
|
|
|
|
|
|
foreach my $key (@sort) {
|
123
|
0
|
0
|
|
|
|
|
if ($self->opts->{$key}->{opt}) {
|
124
|
0
|
|
|
|
|
|
$rule .= ' '.$self->opts->{$key}->{opt};
|
125
|
0
|
0
|
0
|
|
|
|
$rule .= ':'.$self->opts->{$key}->{val} if($self->opts->{$key}->{val} && $self->opts->{$key}->{val} ne '""');
|
126
|
0
|
|
|
|
|
|
$rule .= ';';
|
127
|
|
|
|
|
|
|
}
|
128
|
|
|
|
|
|
|
}
|
129
|
0
|
0
|
|
|
|
|
$rule .= ' )' unless($parms{-optionsOnly});
|
130
|
0
|
0
|
|
|
|
|
$rule =~ s/^ // if($parms{-optionsOnly});
|
131
|
0
|
|
|
|
|
|
return $rule;
|
132
|
|
|
|
|
|
|
}
|
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head2 action
|
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Sets and returns the rule action [alert,log,pass,...]
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
$rule->action('alert');
|
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut
|
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub action {
|
143
|
0
|
|
|
0
|
1
|
|
my ($self,$v) = @_;
|
144
|
0
|
0
|
|
|
|
|
$self->{_action} = $v if(defined($v));
|
145
|
0
|
|
|
|
|
|
return $self->{_action};
|
146
|
|
|
|
|
|
|
}
|
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=head2 proto
|
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Sets and returns the protocol used in the rule [tcp,icmp,udp]
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
$rule->proto('tcp');
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=cut
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub proto {
|
157
|
0
|
|
|
0
|
1
|
|
my ($self,$v) = @_;
|
158
|
0
|
0
|
|
|
|
|
$self->{_proto} = $v if(defined($v));
|
159
|
0
|
|
|
|
|
|
return $self->{_proto};
|
160
|
|
|
|
|
|
|
}
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=head2 src
|
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
Sets and returns the source used in the rule. Make sure you use SINGLE QUOTES for variables!!!
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$rule->src('$EXTERNAL_NET');
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=cut
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub src {
|
171
|
0
|
|
|
0
|
1
|
|
my ($self,$v) = @_;
|
172
|
0
|
0
|
|
|
|
|
$self->{_src} = $v if(defined($v));
|
173
|
0
|
|
|
|
|
|
return $self->{_src};
|
174
|
|
|
|
|
|
|
}
|
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head2 sport
|
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Sets and returns the source port used in the rule
|
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
$rule->sport(80);
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=cut
|
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub sport {
|
185
|
0
|
|
|
0
|
1
|
|
my ($self,$v) = @_;
|
186
|
0
|
0
|
|
|
|
|
$self->{_sport} = $v if(defined($v));
|
187
|
0
|
|
|
|
|
|
return $self->{_sport};
|
188
|
|
|
|
|
|
|
}
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head2 dir
|
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Sets and returns the direction operator used in the rule, -> <- or <>
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
$rule->dir('->');
|
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=cut
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub dir {
|
199
|
0
|
|
|
0
|
1
|
|
my ($self,$v) = @_;
|
200
|
0
|
0
|
|
|
|
|
$self->{_dir} = $v if(defined($v));
|
201
|
0
|
|
|
|
|
|
return $self->{_dir};
|
202
|
|
|
|
|
|
|
}
|
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head2 dst
|
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Sets and returns the destination used in the rule
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
$rule->dst('$HOME_NET');
|
209
|
|
|
|
|
|
|
$rule->dst('192.168.1.1');
|
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut
|
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub dst {
|
214
|
0
|
|
|
0
|
1
|
|
my ($self,$v) = @_;
|
215
|
0
|
0
|
|
|
|
|
$self->{_dst} = $v if(defined($v));
|
216
|
0
|
|
|
|
|
|
return $self->{_dst};
|
217
|
|
|
|
|
|
|
}
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head2 dport
|
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Sets and returns the destination port used in the rule
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
$rule->dport(6667);
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=cut
|
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub dport {
|
228
|
0
|
|
|
0
|
1
|
|
my ($self,$v) = @_;
|
229
|
0
|
0
|
|
|
|
|
$self->{_dport} = $v if(defined($v));
|
230
|
0
|
|
|
|
|
|
return $self->{_dport};
|
231
|
|
|
|
|
|
|
}
|
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head2 opts
|
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Sets an option and a value used in the rule. This currently can only be done one set at a time, and is printed in the order it was set.
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
$rule->opts(option,value);
|
238
|
|
|
|
|
|
|
$rule->opts('msg','this is a test rule');
|
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
This will return a hashref: $hashref->{$keyOrderValue}->{option} and $hashref->{$keyOrderValue}->{value}
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
my $hashref = $rule->opts();
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
There is a fixQuotes() function that reads through this information before setting it, just to ensure the right options are sane. It's a very very basic function, but it seems to get the job done.
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
This method will also accept HASHREF's for easier use:
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
$rule->opts({
|
249
|
|
|
|
|
|
|
msg => 'test1',
|
250
|
|
|
|
|
|
|
rev => '222',
|
251
|
|
|
|
|
|
|
content => 'Subject|3A|',
|
252
|
|
|
|
|
|
|
nocase => '',
|
253
|
|
|
|
|
|
|
});
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
By passing an option => '', the parser will set its value to "''". When $self->string() is called, the option will be written as: option;
|
256
|
|
|
|
|
|
|
ex: nocase => '', will result in an option output of: ...., nocase; ...
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=cut
|
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub opts {
|
261
|
0
|
|
|
0
|
1
|
|
my ($self,$opt,$v) = @_;
|
262
|
0
|
0
|
|
|
|
|
if (defined($opt)) {
|
263
|
0
|
0
|
|
|
|
|
if(ref($opt) eq 'HASH'){
|
264
|
0
|
|
|
|
|
|
foreach my $x (keys %$opt){
|
265
|
0
|
|
|
|
|
|
$opt->{$x} = fixQuotes($x,$opt->{$x});
|
266
|
0
|
|
|
|
|
|
my $pri = (keys %{$self->{_opts}}) + 1;
|
|
0
|
|
|
|
|
|
|
267
|
0
|
|
|
|
|
|
$self->{_opts}->{$pri}->{opt} = $x;
|
268
|
0
|
|
|
|
|
|
$self->{_opts}->{$pri}->{val} = $opt->{$x};
|
269
|
|
|
|
|
|
|
}
|
270
|
|
|
|
|
|
|
}
|
271
|
|
|
|
|
|
|
else {
|
272
|
0
|
|
|
|
|
|
$v = fixQuotes($opt,$v);
|
273
|
0
|
|
|
|
|
|
my $pri = (keys %{$self->{_opts}}) + 1;
|
|
0
|
|
|
|
|
|
|
274
|
0
|
|
|
|
|
|
$self->{_opts}->{$pri}->{opt} = $opt;
|
275
|
0
|
|
|
|
|
|
$self->{_opts}->{$pri}->{val} = $v;
|
276
|
|
|
|
|
|
|
}
|
277
|
|
|
|
|
|
|
}
|
278
|
0
|
|
|
|
|
|
return $self->{_opts};
|
279
|
|
|
|
|
|
|
}
|
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head2 opt
|
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Gets the value of the first option with a given name.
|
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
$rule->opt(option);
|
286
|
|
|
|
|
|
|
print $rule->opt('sid') . ': ' . $rule->opt('msg');
|
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=cut
|
289
|
|
|
|
|
|
|
sub opt {
|
290
|
0
|
|
|
0
|
1
|
|
my ($self,$opt) = @_;
|
291
|
0
|
0
|
|
|
|
|
if (defined($opt)) {
|
292
|
0
|
|
|
|
|
|
my @sort = sort { $a <=> $b } keys %{$self->opts()};
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
293
|
0
|
|
|
|
|
|
foreach my $key (@sort) {
|
294
|
0
|
0
|
|
|
|
|
return $self->opts->{$key}->{val} if($self->opts->{$key}->{opt} eq $opt);
|
295
|
|
|
|
|
|
|
}
|
296
|
|
|
|
|
|
|
}
|
297
|
0
|
|
|
|
|
|
return undef;
|
298
|
|
|
|
|
|
|
}
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# INTERNAL FUNCTIONS ( for now )
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub fixQuotes {
|
303
|
0
|
|
|
0
|
0
|
|
my ($opt, $v) = @_;
|
304
|
0
|
|
|
|
|
|
foreach my $option (@QUOTED_OPTIONS) {
|
305
|
0
|
0
|
0
|
|
|
|
if (defined($v) && (uc($opt) eq $option)) {
|
|
|
0
|
|
|
|
|
|
306
|
0
|
0
|
|
|
|
|
if (!($v =~ /^\"\S+|\s+\"$/)) { # do we have the appropriate quotes? (anchored for pcre)
|
307
|
0
|
|
|
|
|
|
$v =~ s/^\"|\"$//g; # strip the quotes
|
308
|
0
|
|
|
|
|
|
$v = "\"$v\""; # fix em
|
309
|
|
|
|
|
|
|
}
|
310
|
0
|
|
|
|
|
|
last;
|
311
|
|
|
|
|
|
|
}
|
312
|
0
|
|
|
|
|
|
elsif(!defined($v)) { $v = "\"\""; }
|
313
|
|
|
|
|
|
|
}
|
314
|
0
|
|
|
|
|
|
return $v;
|
315
|
|
|
|
|
|
|
}
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub parseRule {
|
318
|
0
|
|
|
0
|
0
|
|
my ($self, $rule) = @_;
|
319
|
0
|
|
|
|
|
|
my @r = split(/\(/,$rule,2);
|
320
|
0
|
|
|
|
|
|
$r[1] =~ s/\)$//;
|
321
|
|
|
|
|
|
|
|
322
|
0
|
|
|
|
|
|
my @meta = split(/\s+/,$r[0]);
|
323
|
0
|
|
|
|
|
|
my @opts = split(/\s*;\s*/,$r[1]);
|
324
|
|
|
|
|
|
|
|
325
|
0
|
|
|
|
|
|
$self->action( $meta[0]);
|
326
|
0
|
|
|
|
|
|
$self->proto( $meta[1]);
|
327
|
0
|
|
|
|
|
|
$self->src( $meta[2]);
|
328
|
0
|
|
|
|
|
|
$self->sport( $meta[3]);
|
329
|
0
|
|
|
|
|
|
$self->dir( $meta[4]);
|
330
|
0
|
|
|
|
|
|
$self->dst( $meta[5]);
|
331
|
0
|
|
|
|
|
|
$self->dport( $meta[6]);
|
332
|
|
|
|
|
|
|
|
333
|
0
|
|
|
|
|
|
foreach my $x (@opts) {
|
334
|
0
|
|
|
|
|
|
my ($opt, $v) = split(/\s*:\s*/, $x, 2);
|
335
|
0
|
|
|
|
|
|
$self->opts($opt, $v);
|
336
|
|
|
|
|
|
|
}
|
337
|
|
|
|
|
|
|
}
|
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
1;
|
340
|
|
|
|
|
|
|
__END__
|