line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::DNS::RR::SVCB; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
26
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
58
|
|
4
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
91
|
|
5
|
|
|
|
|
|
|
our $VERSION = (qw$Id: SVCB.pm 1930 2023-08-21 14:10:10Z willem $)[2]; |
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
32
|
use base qw(Net::DNS::RR); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
170
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Net::DNS::RR::SVCB - DNS SVCB resource record |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=cut |
15
|
|
|
|
|
|
|
|
16
|
2
|
|
|
2
|
|
14
|
use integer; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
11
|
|
17
|
|
|
|
|
|
|
|
18
|
2
|
|
|
2
|
|
65
|
use Net::DNS::DomainName; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
63
|
|
19
|
2
|
|
|
2
|
|
930
|
use Net::DNS::RR::A; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
53
|
|
20
|
2
|
|
|
2
|
|
869
|
use Net::DNS::RR::AAAA; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
61
|
|
21
|
2
|
|
|
2
|
|
890
|
use Net::DNS::Text; |
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
5132
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my %keybyname = ( |
25
|
|
|
|
|
|
|
mandatory => 'key0', |
26
|
|
|
|
|
|
|
alpn => 'key1', |
27
|
|
|
|
|
|
|
'no-default-alpn' => 'key2', |
28
|
|
|
|
|
|
|
port => 'key3', |
29
|
|
|
|
|
|
|
ipv4hint => 'key4', |
30
|
|
|
|
|
|
|
ech => 'key5', |
31
|
|
|
|
|
|
|
ipv6hint => 'key6', |
32
|
|
|
|
|
|
|
dohpath => 'key7', # draft-schwartz-svcb-dns |
33
|
|
|
|
|
|
|
ohttp => 'key8', # draft-pauly-ohai-svcb-config |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub _decode_rdata { ## decode rdata from wire-format octet string |
38
|
14
|
|
|
14
|
|
32
|
my ( $self, $data, $offset ) = @_; |
39
|
|
|
|
|
|
|
|
40
|
14
|
|
|
|
|
37
|
my $rdata = substr $$data, $offset, $self->{rdlength}; |
41
|
14
|
|
|
|
|
36
|
$self->{SvcPriority} = unpack( 'n', $rdata ); |
42
|
|
|
|
|
|
|
|
43
|
14
|
|
|
|
|
25
|
my $index; |
44
|
14
|
|
|
|
|
37
|
( $self->{TargetName}, $index ) = Net::DNS::DomainName->decode( \$rdata, 2 ); |
45
|
|
|
|
|
|
|
|
46
|
14
|
|
|
|
|
39
|
my $params = $self->{SvcParams} = []; |
47
|
14
|
|
|
|
|
20
|
my $limit = length($rdata) - 3; |
48
|
14
|
|
|
|
|
29
|
while ( $index < $limit ) { |
49
|
13
|
|
|
|
|
36
|
my ( $key, $size ) = unpack( "\@$index n2", $rdata ); |
50
|
13
|
|
|
|
|
30
|
push @$params, ( $key, substr $rdata, $index + 4, $size ); |
51
|
13
|
|
|
|
|
26
|
$index += ( $size + 4 ); |
52
|
|
|
|
|
|
|
} |
53
|
14
|
100
|
|
|
|
32
|
die $self->type . ': corrupt RDATA' unless $index == length($rdata); |
54
|
13
|
|
|
|
|
26
|
return; |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _encode_rdata { ## encode rdata as wire-format octet string |
59
|
12
|
|
|
12
|
|
21
|
my $self = shift; |
60
|
|
|
|
|
|
|
|
61
|
12
|
|
|
|
|
31
|
my @packed = pack 'n a*', $self->{SvcPriority}, $self->{TargetName}->encode; |
62
|
12
|
|
100
|
|
|
45
|
my $params = $self->{SvcParams} || []; |
63
|
12
|
|
|
|
|
24
|
my @params = @$params; |
64
|
12
|
|
|
|
|
108
|
while (@params) { |
65
|
5
|
|
|
|
|
11
|
my $key = shift @params; |
66
|
5
|
|
|
|
|
68
|
my $val = shift @params; |
67
|
5
|
|
|
|
|
25
|
push @packed, pack( 'n2a*', $key, length($val), $val ); |
68
|
|
|
|
|
|
|
} |
69
|
12
|
|
|
|
|
41
|
return join '', @packed; |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub _format_rdata { ## format rdata portion of RR string. |
74
|
22
|
|
|
22
|
|
31
|
my $self = shift; |
75
|
|
|
|
|
|
|
|
76
|
22
|
|
|
|
|
34
|
my $priority = $self->{SvcPriority}; |
77
|
22
|
|
|
|
|
53
|
my $target = $self->{TargetName}->string; |
78
|
22
|
|
100
|
|
|
76
|
my $params = $self->{SvcParams} || []; |
79
|
22
|
100
|
|
|
|
60
|
return ( $priority, $target ) unless scalar @$params; |
80
|
|
|
|
|
|
|
|
81
|
16
|
|
|
|
|
38
|
my $encode = $self->{TargetName}->encode(); |
82
|
16
|
|
|
|
|
31
|
my $length = 2 + length $encode; |
83
|
16
|
|
|
|
|
78
|
my @target = grep {length} split /(\S{32})/, unpack 'H*', $encode; |
|
42
|
|
|
|
|
74
|
|
84
|
16
|
|
|
|
|
47
|
my @rdata = unpack 'H4', pack 'n', $priority; |
85
|
16
|
|
|
|
|
41
|
push @rdata, "\t; priority: $priority\n"; |
86
|
16
|
|
|
|
|
25
|
push @rdata, shift @target; |
87
|
16
|
|
|
|
|
41
|
push @rdata, join '', "\t; target: ", substr( $target, 0, 50 ), "\n"; |
88
|
16
|
|
|
|
|
31
|
push @rdata, @target; |
89
|
|
|
|
|
|
|
|
90
|
16
|
|
|
|
|
36
|
my @params = @$params; |
91
|
16
|
|
|
|
|
54
|
while (@params) { |
92
|
31
|
|
|
|
|
49
|
my $key = shift @params; |
93
|
31
|
|
|
|
|
45
|
my $val = shift @params; |
94
|
31
|
|
|
|
|
42
|
push @rdata, "\n"; |
95
|
31
|
100
|
|
|
|
64
|
push @rdata, "; key$key=...\n" if $key > 15; |
96
|
31
|
|
|
|
|
78
|
push @rdata, unpack 'H4H4', pack( 'n2', $key, length $val ); |
97
|
31
|
|
|
|
|
88
|
push @rdata, split /(\S{32})/, unpack 'H*', $val; |
98
|
31
|
|
|
|
|
80
|
$length += 4 + length $val; |
99
|
|
|
|
|
|
|
} |
100
|
16
|
|
|
|
|
115
|
return ( "\\# $length", @rdata ); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub _parse_rdata { ## populate RR from rdata in argument list |
105
|
32
|
|
|
32
|
|
88
|
my ( $self, @argument ) = @_; |
106
|
|
|
|
|
|
|
|
107
|
32
|
|
|
|
|
85
|
$self->svcpriority( shift @argument ); |
108
|
32
|
|
|
|
|
87
|
$self->targetname( shift @argument ); |
109
|
|
|
|
|
|
|
|
110
|
32
|
|
|
1
|
|
183
|
local $SIG{__WARN__} = sub { die @_ }; |
|
1
|
|
|
|
|
15
|
|
111
|
32
|
|
|
|
|
83
|
while ( my $svcparam = shift @argument ) { |
112
|
42
|
|
|
|
|
86
|
for ($svcparam) { |
113
|
42
|
|
|
|
|
57
|
my @value; |
114
|
42
|
100
|
|
|
|
198
|
if (/^key\d+=(.*)$/i) { |
|
|
100
|
|
|
|
|
|
115
|
9
|
100
|
|
|
|
38
|
push @value, length($1) ? $1 : shift @argument; |
116
|
|
|
|
|
|
|
} elsif (/^[^=]+=(.*)$/) { |
117
|
21
|
100
|
|
|
|
70
|
local $_ = length($1) ? $1 : shift @argument; |
118
|
21
|
|
|
|
|
48
|
s/^"([^"]*)"$/$1/; # strip enclosing quotes |
119
|
21
|
|
|
|
|
64
|
push @value, split /,/; |
120
|
|
|
|
|
|
|
} else { |
121
|
12
|
100
|
|
|
|
40
|
push @value, '' unless $keybyname{lc $_}; # empty | Boolean |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
42
|
|
|
|
|
84
|
s/[-]/_/g; # extract identifier |
125
|
42
|
|
|
|
|
110
|
m/^([^=]+)/; |
126
|
42
|
|
|
|
|
195
|
$self->$1(@value); |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
} |
129
|
17
|
|
|
|
|
92
|
return; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub _post_parse { ## parser post processing |
134
|
30
|
|
|
30
|
|
50
|
my $self = shift; |
135
|
|
|
|
|
|
|
|
136
|
30
|
|
100
|
|
|
77
|
my $paramref = $self->{SvcParams} || []; |
137
|
30
|
100
|
|
|
|
104
|
my %svcparam = scalar(@$paramref) ? @$paramref : return; |
138
|
|
|
|
|
|
|
|
139
|
22
|
|
|
|
|
121
|
$self->key0(undef); # ruse to force sorting of SvcParams |
140
|
22
|
100
|
|
|
|
50
|
if ( defined $svcparam{0} ) { |
141
|
6
|
|
|
|
|
10
|
my %unique; |
142
|
6
|
|
|
|
|
16
|
foreach ( grep { !$unique{$_}++ } unpack 'n*', $svcparam{0} ) { |
|
9
|
|
|
|
|
32
|
|
143
|
8
|
100
|
|
|
|
19
|
die( $self->type . qq[: unexpected "key0" in mandatory list] ) if $unique{0}; |
144
|
7
|
100
|
|
|
|
21
|
die( $self->type . qq[: duplicate "key$_" in mandatory list] ) if --$unique{$_}; |
145
|
6
|
100
|
|
|
|
16
|
die( $self->type . qq[: mandatory "key$_" not present] ) unless defined $svcparam{$_}; |
146
|
|
|
|
|
|
|
} |
147
|
3
|
|
|
|
|
35
|
$self->mandatory( keys %unique ); # restore mandatory key list |
148
|
|
|
|
|
|
|
} |
149
|
19
|
100
|
100
|
|
|
67
|
die( $self->type . qq[: expected alpn="..." not present] ) if defined( $svcparam{2} ) && !$svcparam{1}; |
150
|
18
|
|
|
|
|
51
|
return; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub _defaults { ## specify RR attribute default values |
155
|
2
|
|
|
2
|
|
4
|
my $self = shift; |
156
|
|
|
|
|
|
|
|
157
|
2
|
|
|
|
|
9
|
$self->_parse_rdata(qw(0 .)); |
158
|
2
|
|
|
|
|
7
|
return; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub svcpriority { |
163
|
38
|
|
|
38
|
0
|
421
|
my ( $self, @value ) = @_; # uncoverable pod |
164
|
38
|
|
|
|
|
76
|
for (@value) { $self->{SvcPriority} = 0 + $_ } |
|
35
|
|
|
|
|
106
|
|
165
|
38
|
|
100
|
|
|
114
|
return $self->{SvcPriority} || 0; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub targetname { |
170
|
39
|
|
|
39
|
0
|
1395
|
my ( $self, @value ) = @_; # uncoverable pod |
171
|
|
|
|
|
|
|
|
172
|
39
|
|
|
|
|
63
|
for (@value) { $self->{TargetName} = Net::DNS::DomainName->new($_) } |
|
35
|
|
|
|
|
113
|
|
173
|
|
|
|
|
|
|
|
174
|
39
|
100
|
|
|
|
133
|
my $target = $self->{TargetName} ? $self->{TargetName}->name : return; |
175
|
37
|
100
|
|
|
|
93
|
return $target unless $self->{SvcPriority}; |
176
|
31
|
100
|
|
|
|
84
|
return ( $target eq '.' ) ? $self->owner : $target; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub mandatory { ## mandatory=key1,port,... |
181
|
11
|
|
|
11
|
1
|
389
|
my ( $self, @value ) = @_; |
182
|
11
|
100
|
|
|
|
22
|
my @list = map { $keybyname{lc $_} || $_ } map { split /,/ } @value; |
|
13
|
|
|
|
|
62
|
|
|
13
|
|
|
|
|
32
|
|
183
|
11
|
100
|
|
|
|
21
|
my @keys = map { /(\d+)$/ ? $1 : die( $self->type . qq[: unexpected "$_"] ) } @list; |
|
13
|
|
|
|
|
64
|
|
184
|
10
|
|
|
|
|
30
|
return $self->key0( _integer16( sort { $a <=> $b } @keys ) ); |
|
4
|
|
|
|
|
17
|
|
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub alpn { ## alpn=h3,h2,... |
188
|
5
|
|
|
5
|
1
|
386
|
my ( $self, @value ) = @_; |
189
|
5
|
|
|
|
|
11
|
return $self->key1( _string(@value) ); |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub no_default_alpn { ## no-default-alpn (Boolean) |
193
|
4
|
|
|
4
|
0
|
10
|
my ( $self, @value ) = @_; # uncoverable pod |
194
|
4
|
100
|
|
|
|
25
|
return $self->key2( ( defined(wantarray) ? () : '' ), @value ); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub port { ## port=1234 |
198
|
7
|
|
|
7
|
1
|
386
|
my ( $self, @value ) = @_; |
199
|
7
|
|
|
|
|
18
|
return $self->key3( map { _integer16($_) } @value ); |
|
6
|
|
|
|
|
13
|
|
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub ipv4hint { ## ipv4hint=192.0.2.1,... |
203
|
3
|
|
|
3
|
1
|
352
|
my ( $self, @value ) = @_; |
204
|
3
|
|
|
|
|
8
|
return $self->key4( _ipv4(@value) ); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub ech { ## Format not specified |
208
|
4
|
|
|
4
|
1
|
373
|
my ( $self, @value ) = @_; |
209
|
4
|
|
|
|
|
22
|
return $self->key5(@value); # RESERVED |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub ipv6hint { ## ipv6hint=2001:DB8::1,... |
213
|
5
|
|
|
5
|
1
|
401
|
my ( $self, @value ) = @_; |
214
|
5
|
|
|
|
|
13
|
return $self->key6( _ipv6(@value) ); |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub dohpath { ## dohpath=/dns-query{?dns} |
218
|
2
|
|
|
2
|
0
|
367
|
my ( $self, @value ) = @_; # uncoverable pod |
219
|
2
|
|
|
|
|
24
|
return $self->key7(@value); |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub ohttp { ## ohttp (Boolean) |
223
|
2
|
|
|
2
|
0
|
366
|
my ( $self, @value ) = @_; # uncoverable pod |
224
|
2
|
100
|
|
|
|
16
|
return $self->key8( ( defined(wantarray) ? () : '' ), @value ); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
######################################## |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub _presentation { ## render octet string(s) in presentation format |
232
|
58
|
|
|
58
|
|
122
|
my @arg = @_; |
233
|
58
|
100
|
|
|
|
185
|
my $raw = scalar(@arg) ? join( '', @arg ) : return (); |
234
|
50
|
|
|
|
|
157
|
return Net::DNS::Text->decode( \$raw, 0, length($raw) )->string; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub _integer16 { |
238
|
16
|
|
|
16
|
|
29
|
my @arg = @_; |
239
|
16
|
|
|
|
|
23
|
return _presentation( map { pack( 'n', $_ ) } @arg ); |
|
18
|
|
|
|
|
95
|
|
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub _ipv4 { |
243
|
3
|
|
|
3
|
|
7
|
my @arg = @_; |
244
|
3
|
|
|
|
|
8
|
return _presentation( map { Net::DNS::RR::A::address( {}, $_ ) } @arg ); |
|
1
|
|
|
|
|
5
|
|
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub _ipv6 { |
248
|
5
|
|
|
5
|
|
11
|
my @arg = @_; |
249
|
5
|
|
|
|
|
10
|
return _presentation( map { Net::DNS::RR::AAAA::address( {}, $_ ) } @arg ); |
|
4
|
|
|
|
|
14
|
|
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
sub _string { |
253
|
5
|
|
|
5
|
|
10
|
my @arg = @_; |
254
|
5
|
|
|
|
|
13
|
local $_ = join ',', @arg; # reassemble argument string |
255
|
5
|
|
|
|
|
8
|
s/\\,/\\044/g; # disguise (RFC1035) escaped comma |
256
|
5
|
100
|
|
|
|
44
|
die <<"QQ" if /\\092,|\\092\\092/; |
257
|
|
|
|
|
|
|
SVCB: Please use standard RFC1035 escapes |
258
|
|
|
|
|
|
|
draft-ietf-dnsop-svcb-https double-escape nonsense not implemented |
259
|
|
|
|
|
|
|
QQ |
260
|
3
|
|
|
|
|
12
|
return _presentation( map { Net::DNS::Text->new($_)->encode() } split /,/ ); |
|
2
|
|
|
|
|
5
|
|
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
sub AUTOLOAD { ## Dynamic constructor/accessor methods |
265
|
77
|
|
|
77
|
|
541
|
my ( $self, @argument ) = @_; |
266
|
|
|
|
|
|
|
|
267
|
77
|
|
|
|
|
99
|
our $AUTOLOAD; |
268
|
77
|
|
|
|
|
274
|
my ($method) = reverse split /::/, $AUTOLOAD; |
269
|
|
|
|
|
|
|
|
270
|
77
|
|
|
|
|
173
|
my $super = "SUPER::$method"; |
271
|
77
|
100
|
|
|
|
387
|
return $self->$super(@argument) unless $method =~ /^key[0]*(\d+)$/i; |
272
|
75
|
|
|
|
|
158
|
my $key = $1; |
273
|
|
|
|
|
|
|
|
274
|
75
|
|
100
|
|
|
230
|
my $paramsref = $self->{SvcParams} || []; |
275
|
75
|
|
|
|
|
178
|
my %svcparams = @$paramsref; |
276
|
|
|
|
|
|
|
|
277
|
75
|
100
|
|
|
|
141
|
if ( scalar @argument ) { |
278
|
60
|
|
|
|
|
90
|
my $arg = shift @argument; # keyNN($value); |
279
|
60
|
100
|
|
|
|
133
|
delete $svcparams{$key} unless defined $arg; |
280
|
60
|
100
|
|
|
|
136
|
die( $self->type . qq[: duplicate SvcParam "key$key"] ) if defined $svcparams{$key}; |
281
|
59
|
100
|
|
|
|
131
|
die( $self->type . qq[: invalid SvcParam "key$key"] ) if $key > 65534; |
282
|
58
|
100
|
|
|
|
177
|
$svcparams{$key} = Net::DNS::Text->new("$arg")->raw if defined $arg; |
283
|
58
|
|
|
|
|
235
|
$self->{SvcParams} = [map { ( $_, $svcparams{$_} ) } sort { $a <=> $b } keys %svcparams]; |
|
125
|
|
|
|
|
321
|
|
|
130
|
|
|
|
|
201
|
|
284
|
58
|
100
|
|
|
|
168
|
die( $self->type . qq[: unexpected number of values for "key$key"] ) if scalar @argument; |
285
|
|
|
|
|
|
|
} else { |
286
|
15
|
100
|
|
|
|
40
|
die( $self->type . qq[: no value specified for "key$key"] ) unless defined wantarray; |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
64
|
|
|
|
|
103
|
my $value = $svcparams{$key}; |
290
|
64
|
100
|
|
|
|
191
|
return defined($value) ? _presentation($value) : $value; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
######################################## |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
1; |
297
|
|
|
|
|
|
|
__END__ |