1 |
diff -urN xtables-addons-1.47.1-v6/geoip/Makefile.in xtables-addons-1.47.1-g2/geoip/Makefile.in |
2 |
--- xtables-addons-1.47.1-v6/geoip/Makefile.in 2012-10-15 23:29:29.000000000 +0400 |
3 |
+++ xtables-addons-1.47.1-g2/geoip/Makefile.in 2018-07-02 23:03:12.214541572 +0400 |
4 |
@@ -234,7 +234,7 @@ |
5 |
top_builddir = @top_builddir@ |
6 |
top_srcdir = @top_srcdir@ |
7 |
xtlibdir = @xtlibdir@ |
8 |
-pkglibexec_SCRIPTS = xt_geoip_build xt_geoip_dl |
9 |
+pkglibexec_SCRIPTS = xt_geoip_build xt_geoip_dl xt_geoip_fetch |
10 |
man1_MANS = xt_geoip_build.1 xt_geoip_dl.1 |
11 |
all: all-am |
12 |
|
13 |
diff -urN xtables-addons-1.47.1-v6/geoip/xt_geoip_build xtables-addons-1.47.1-g2/geoip/xt_geoip_build |
14 |
--- xtables-addons-1.47.1-v6/geoip/xt_geoip_build 2012-10-15 23:29:22.000000000 +0400 |
15 |
+++ xtables-addons-1.47.1-g2/geoip/xt_geoip_build 2018-06-28 22:45:21.000000000 +0400 |
16 |
@@ -1,10 +1,14 @@ |
17 |
#!/usr/bin/perl |
18 |
# |
19 |
# Converter for MaxMind CSV database to binary, for xt_geoip |
20 |
-# Copyright © Jan Engelhardt <jengelh@medozas.de>, 2008-2011 |
21 |
+# Copyright Jan Engelhardt, 2008-2011 |
22 |
+# Copyright Philip Prindeville, 2018 |
23 |
+# Added Twice output (BE, LE) for v1.x for SME9 - Mab974, 2018 |
24 |
# |
25 |
use Getopt::Long; |
26 |
-use IO::Handle; |
27 |
+use Net::CIDR::Lite; |
28 |
+use Socket qw(AF_INET AF_INET6 inet_pton); |
29 |
+use warnings; |
30 |
use Text::CSV_XS; # or trade for Text::CSV |
31 |
use strict; |
32 |
|
33 |
@@ -32,33 +36,208 @@ |
34 |
} |
35 |
} |
36 |
|
37 |
+my %countryId; |
38 |
+my %countryName; |
39 |
+ |
40 |
+my $dir = findVersion(); |
41 |
+ |
42 |
+&loadCountries(); |
43 |
+ |
44 |
&dump(&collect()); |
45 |
|
46 |
-sub collect |
47 |
+sub findVersion |
48 |
{ |
49 |
- my %country; |
50 |
- |
51 |
- while (my $row = $csv->getline(*ARGV)) { |
52 |
- if (!defined($country{$row->[4]})) { |
53 |
- $country{$row->[4]} = { |
54 |
- name => $row->[5], |
55 |
- pool_v4 => [], |
56 |
- pool_v6 => [], |
57 |
- }; |
58 |
+ my @dirs = (); |
59 |
+ my $filename; |
60 |
+ |
61 |
+ opendir(my $dh, '.') || die "Can't open .: $!\n"; |
62 |
+ |
63 |
+ while (($filename = readdir($dh))) { |
64 |
+ if ($filename =~ m/^GeoLite2-Country-CSV_\d{8}$/) { |
65 |
+ push(@dirs, $filename); |
66 |
} |
67 |
- my $c = $country{$row->[4]}; |
68 |
- if ($row->[0] =~ /:/) { |
69 |
- push(@{$c->{pool_v6}}, |
70 |
- [&ip6_pack($row->[0]), &ip6_pack($row->[1])]); |
71 |
+ } |
72 |
+ closedir $dh; |
73 |
+ |
74 |
+ @dirs = sort @dirs; |
75 |
+ return pop(@dirs); |
76 |
+} |
77 |
+ |
78 |
+sub loadCountries |
79 |
+{ |
80 |
+ my $file = "$dir/GeoLite2-Country-Locations-en.csv"; |
81 |
+ |
82 |
+ sub id; sub cc; sub long; sub ct; sub cn; |
83 |
+ |
84 |
+ %countryId = (); |
85 |
+ %countryName = (); |
86 |
+ |
87 |
+ open(my $fh, '<', $file) || die "Couldn't open list country names\n"; |
88 |
+ |
89 |
+ # first line is headers |
90 |
+ my $row = $csv->getline($fh); |
91 |
+ |
92 |
+ my %header = map { ($row->[$_], $_); } (0..$#{$row}); |
93 |
+ |
94 |
+ my %pairs = ( |
95 |
+ country_iso_code => 'ISO Country Code', |
96 |
+ geoname_id => 'ID', |
97 |
+ country_name => 'Country Name', |
98 |
+ continent_code => 'Continent Code', |
99 |
+ continent_name => 'Continent Name', |
100 |
+ ); |
101 |
+ |
102 |
+ # verify that the columns we need are present |
103 |
+ map { die "Table has no $pairs{$_} column\n" unless (exists $header{$_}); } keys %pairs; |
104 |
+ |
105 |
+ my %remapping = ( |
106 |
+ id => 'geoname_id', |
107 |
+ cc => 'country_iso_code', |
108 |
+ long => 'country_name', |
109 |
+ ct => 'continent_code', |
110 |
+ cn => 'continent_name', |
111 |
+ ); |
112 |
+ |
113 |
+ # now create a function which returns the value of that column # |
114 |
+ map { eval "sub $_ () { \$header{\$remapping{$_}}; }" ; } keys %remapping; |
115 |
+ |
116 |
+ while (my $row = $csv->getline($fh)) { |
117 |
+ if ($row->[cc] eq '' && $row->[long] eq '') { |
118 |
+ $countryId{$row->[id]} = $row->[ct]; |
119 |
+ $countryName{$row->[ct]} = $row->[cn]; |
120 |
} else { |
121 |
- push(@{$c->{pool_v4}}, [$row->[2], $row->[3]]); |
122 |
+ $countryId{$row->[id]} = $row->[cc]; |
123 |
+ $countryName{$row->[cc]} = $row->[long]; |
124 |
+ } |
125 |
+ } |
126 |
+ |
127 |
+ $countryName{A1} = 'Anonymous Proxy'; |
128 |
+ $countryName{A2} = 'Satellite Provider'; |
129 |
+ $countryName{O1} = 'Other Country'; |
130 |
+ |
131 |
+ close($fh); |
132 |
+ |
133 |
+ # clean up the namespace |
134 |
+ undef &id; undef &cc; undef &long; undef &ct; undef &cn; |
135 |
+} |
136 |
+ |
137 |
+sub lookupCountry |
138 |
+{ |
139 |
+ my ($id, $rid, $proxy, $sat) = @_; |
140 |
+ |
141 |
+ if ($proxy) { |
142 |
+ return 'A1'; |
143 |
+ } elsif ($sat) { |
144 |
+ return 'A2'; |
145 |
+ } |
146 |
+ $id ||= $rid; |
147 |
+ if ($id eq '') { |
148 |
+ return 'O1'; |
149 |
+ } |
150 |
+ die "Unknown id: $id line $.\n" unless (exists $countryId{$id}); |
151 |
+ return $countryId{$id}; |
152 |
+} |
153 |
+ |
154 |
+sub collect |
155 |
+{ |
156 |
+ my ($file, $fh, $row); |
157 |
+ my (%country, %header); |
158 |
+ |
159 |
+ sub net; sub id; sub rid; sub proxy; sub sat; |
160 |
+ |
161 |
+ my %pairs = ( |
162 |
+ network => 'Network', |
163 |
+ registered_country_geoname_id => 'Registered Country ID', |
164 |
+ geoname_id => 'Country ID', |
165 |
+ is_anonymous_proxy => 'Anonymous Proxy', |
166 |
+ is_satellite_provider => 'Satellite', |
167 |
+ ); |
168 |
+ |
169 |
+ foreach (sort keys %countryName) { |
170 |
+ $country{$_} = { |
171 |
+ name => $countryName{$_}, |
172 |
+ pool_v4 => Net::CIDR::Lite->new(), |
173 |
+ pool_v6 => Net::CIDR::Lite->new(), |
174 |
+ }; |
175 |
+ } |
176 |
+ |
177 |
+ $file = "$dir/GeoLite2-Country-Blocks-IPv4.csv"; |
178 |
+ |
179 |
+ open($fh, '<', $file) || die "Can't open IPv4 database\n"; |
180 |
+ |
181 |
+ # first line is headers |
182 |
+ $row = $csv->getline($fh); |
183 |
+ |
184 |
+ %header = map { ($row->[$_], $_); } (0..$#{$row}); |
185 |
+ |
186 |
+ # verify that the columns we need are present |
187 |
+ map { die "Table has no %pairs{$_} column\n" unless (exists $header{$_}); } keys %pairs; |
188 |
+ |
189 |
+ my %remapping = ( |
190 |
+ net => 'network', |
191 |
+ id => 'geoname_id', |
192 |
+ rid => 'registered_country_geoname_id', |
193 |
+ proxy => 'is_anonymous_proxy', |
194 |
+ sat => 'is_satellite_provider', |
195 |
+ ); |
196 |
+ |
197 |
+ # now create a function which returns the value of that column # |
198 |
+ map { eval "sub $_ () { \$header{\$remapping{$_}}; }" ; } keys %remapping; |
199 |
+ |
200 |
+ while ($row = $csv->getline($fh)) { |
201 |
+ my ($cc, $cidr); |
202 |
+ |
203 |
+ $cc = lookupCountry($row->[id], $row->[rid], $row->[proxy], $row->[sat]); |
204 |
+ $cidr = $row->[net]; |
205 |
+ $country{$cc}->{pool_v4}->add($cidr); |
206 |
+ |
207 |
+ if ($. % 4096 == 0) { |
208 |
+ print STDERR "\r\e[2K$. entries"; |
209 |
} |
210 |
+ } |
211 |
+ |
212 |
+ print STDERR "\r\e[2K$. entries total\n"; |
213 |
+ |
214 |
+ close($fh); |
215 |
+ |
216 |
+ # clean up the namespace |
217 |
+ undef &net; undef &id; undef &rid; undef &proxy; undef &sat; |
218 |
+ |
219 |
+ $file = "$dir/GeoLite2-Country-Blocks-IPv6.csv"; |
220 |
+ |
221 |
+ open($fh, '<', $file) || die "Can't open IPv6 database\n"; |
222 |
+ |
223 |
+ # first line is headers |
224 |
+ $row = $csv->getline($fh); |
225 |
+ |
226 |
+ %header = map { ($row->[$_], $_); } (0..$#{$row}); |
227 |
+ |
228 |
+ # verify that the columns we need are present |
229 |
+ map { die "Table has no %pairs{$_} column\n" unless (exists $header{$_}); } keys %pairs; |
230 |
+ |
231 |
+ # unlikely the IPv6 table has different columns, but just to be sure |
232 |
+ # create a function which returns the value of that column # |
233 |
+ map { eval "sub $_ () { \$header{\$remapping{$_}}; }" ; } keys %remapping; |
234 |
+ |
235 |
+ while ($row = $csv->getline($fh)) { |
236 |
+ my ($cc, $cidr); |
237 |
+ |
238 |
+ $cc = lookupCountry($row->[id], $row->[rid], $row->[proxy], $row->[sat]); |
239 |
+ $cidr = $row->[net]; |
240 |
+ $country{$cc}->{pool_v6}->add($cidr); |
241 |
+ |
242 |
if ($. % 4096 == 0) { |
243 |
print STDERR "\r\e[2K$. entries"; |
244 |
} |
245 |
} |
246 |
|
247 |
print STDERR "\r\e[2K$. entries total\n"; |
248 |
+ |
249 |
+ close($fh); |
250 |
+ |
251 |
+ # clean up the namespace |
252 |
+ undef &net; undef &id; undef &rid; undef &proxy; undef &sat; |
253 |
+ |
254 |
return \%country; |
255 |
} |
256 |
|
257 |
@@ -66,18 +245,23 @@ |
258 |
{ |
259 |
my $country = shift @_; |
260 |
|
261 |
- foreach my $iso_code (sort keys %$country) { |
262 |
+ foreach my $iso_code (sort keys %{$country}) { |
263 |
&dump_one($iso_code, $country->{$iso_code}); |
264 |
} |
265 |
} |
266 |
|
267 |
sub dump_one |
268 |
{ |
269 |
+# 2 sub-directories added pour big-endian and little-endian |
270 |
+ |
271 |
my($iso_code, $country) = @_; |
272 |
my($file, $fh_le, $fh_be); |
273 |
- |
274 |
+ my ($start, $end); |
275 |
+ my @ranges; |
276 |
+ |
277 |
+ @ranges = $country->{pool_v6}->list_range(); |
278 |
printf "%5u IPv6 ranges for %s %s\n", |
279 |
- scalar(@{$country->{pool_v6}}), |
280 |
+ scalar(@ranges), |
281 |
$iso_code, $country->{name}; |
282 |
|
283 |
$file = "$target_dir/LE/".uc($iso_code).".iv6"; |
284 |
@@ -90,15 +274,23 @@ |
285 |
print STDERR "Error opening $file: $!\n"; |
286 |
exit 1; |
287 |
} |
288 |
- foreach my $range (@{$country->{pool_v6}}) { |
289 |
- print $fh_be $range->[0], $range->[1]; |
290 |
- print $fh_le &ip6_swap($range->[0]), &ip6_swap($range->[1]); |
291 |
+ binmode($fh_be); |
292 |
+ binmode($fh_le); |
293 |
+ |
294 |
+ foreach my $range (@ranges) { |
295 |
+ ($start, $end) = split('-', $range); |
296 |
+ $start = inet_pton(AF_INET6, $start); |
297 |
+ $end = inet_pton(AF_INET6, $end); |
298 |
+ print $fh_be $start, $end; |
299 |
+ |
300 |
+ print $fh_le &ip6_swap($start), &ip6_swap($end); |
301 |
} |
302 |
close $fh_le; |
303 |
close $fh_be; |
304 |
|
305 |
+ @ranges = $country->{pool_v4}->list_range(); |
306 |
printf "%5u IPv4 ranges for %s %s\n", |
307 |
- scalar(@{$country->{pool_v4}}), |
308 |
+ scalar(@ranges), |
309 |
$iso_code, $country->{name}; |
310 |
|
311 |
$file = "$target_dir/LE/".uc($iso_code).".iv4"; |
312 |
@@ -111,31 +303,29 @@ |
313 |
print STDERR "Error opening $file: $!\n"; |
314 |
exit 1; |
315 |
} |
316 |
- foreach my $range (@{$country->{pool_v4}}) { |
317 |
- print $fh_le pack("VV", $range->[0], $range->[1]); |
318 |
- print $fh_be pack("NN", $range->[0], $range->[1]); |
319 |
+ binmode($fh_be); |
320 |
+ binmode($fh_le); |
321 |
+ |
322 |
+ foreach my $range (@ranges) { |
323 |
+ ($start, $end) = split('-', $range); |
324 |
+ my $start = inet_pton(AF_INET, $start); |
325 |
+ my $end = inet_pton(AF_INET, $end); |
326 |
+ print $fh_be $start, $end; |
327 |
+ print $fh_le ip4_swap($start), ip4_swap($end); |
328 |
} |
329 |
close $fh_le; |
330 |
close $fh_be; |
331 |
} |
332 |
|
333 |
-sub ip6_pack |
334 |
+sub ip6_swap |
335 |
{ |
336 |
- my $addr = shift @_; |
337 |
- $addr =~ s{::}{:!:}; |
338 |
- my @addr = split(/:/, $addr); |
339 |
- my @e = (0) x 8; |
340 |
- foreach (@addr) { |
341 |
- if ($_ eq "!") { |
342 |
- $_ = join(':', @e[0..(8-scalar(@addr))]); |
343 |
- } |
344 |
- } |
345 |
- @addr = split(/:/, join(':', @addr)); |
346 |
- $_ = hex($_) foreach @addr; |
347 |
- return pack("n*", @addr); |
348 |
+ my ($p1, $p2, $p3, $p4) = unpack 'a4 a4 a4 a4', shift @_; |
349 |
+ return pack "a4 a4 a4 a4", ip4_swap($p1), ip4_swap($p2), |
350 |
+ ip4_swap($p3), ip4_swap($p4); |
351 |
} |
352 |
|
353 |
-sub ip6_swap |
354 |
+sub ip4_swap |
355 |
{ |
356 |
- return pack("V*", unpack("N*", shift @_)); |
357 |
+ my ($b1, $b2, $b3, $b4) = unpack 'a a a a', shift @_; |
358 |
+ return pack "a a a a", $b4, $b3, $b2, $b1; |
359 |
} |
360 |
diff -urN xtables-addons-1.47.1-v6/geoip/xt_geoip_build.1 xtables-addons-1.47.1-g2/geoip/xt_geoip_build.1 |
361 |
--- xtables-addons-1.47.1-v6/geoip/xt_geoip_build.1 2012-10-15 23:29:22.000000000 +0400 |
362 |
+++ xtables-addons-1.47.1-g2/geoip/xt_geoip_build.1 2018-06-21 21:43:45.000000000 +0400 |
363 |
@@ -5,7 +5,7 @@ |
364 |
.SH Syntax |
365 |
.PP |
366 |
\fI/usr/libexec/xt_geoip/\fP\fBxt_geoip_build\fP [\fB\-D\fP |
367 |
-\fItarget_dir\fP] [\fIfile\fP...] |
368 |
+\fItarget_dir\fP] |
369 |
.SH Description |
370 |
.PP |
371 |
xt_geoip_build is used to build packed raw representations of the range |
372 |
@@ -16,7 +16,12 @@ |
373 |
also ordered, as xt_geoip relies on this property for its bisection approach to |
374 |
work. |
375 |
.PP |
376 |
-Input is processed from the listed files, or if none is given, from stdin. |
377 |
+It expects to find a directory named |
378 |
+.IR GeoLite2-Country-CSV_YYYYMMDD |
379 |
+in the current directory, and will select the most recent if multiple |
380 |
+instances are found. The |
381 |
+.IR xt_geoip_dl |
382 |
+script can be used to populate this directory. |
383 |
.PP |
384 |
Since the script is usually installed to the libexec directory of the |
385 |
xtables-addons package and this is outside $PATH (on purpose), invoking the |
386 |
@@ -29,7 +34,7 @@ |
387 |
.PP |
388 |
Shell commands to build the databases and put them to where they are expected: |
389 |
.PP |
390 |
-xt_geoip_build -D /usr/share/xt_geoip |
391 |
+xt_geoip_build \-D /usr/share/xt_geoip |
392 |
.SH See also |
393 |
.PP |
394 |
xt_geoip_dl(1) |
395 |
diff -urN xtables-addons-1.47.1-v6/geoip/xt_geoip_dl xtables-addons-1.47.1-g2/geoip/xt_geoip_dl |
396 |
--- xtables-addons-1.47.1-v6/geoip/xt_geoip_dl 2012-10-15 23:29:22.000000000 +0400 |
397 |
+++ xtables-addons-1.47.1-g2/geoip/xt_geoip_dl 2018-06-25 15:38:51.043475041 +0400 |
398 |
@@ -1,8 +1,7 @@ |
399 |
#!/bin/sh |
400 |
|
401 |
-rm -f GeoIPv6.csv{,.gz} GeoIPCountryCSV.zip GeoIPCountryWhois.csv; |
402 |
-wget \ |
403 |
- http://geolite.maxmind.com/download/geoip/database/GeoIPv6.csv.gz \ |
404 |
- http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip; |
405 |
-gzip -d GeoIPv6.csv.gz; |
406 |
-unzip GeoIPCountryCSV.zip; |
407 |
+rm -rf GeoLite2-Country-CSV_* |
408 |
+ |
409 |
+wget -q http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country-CSV.zip |
410 |
+unzip -q GeoLite2-Country-CSV.zip |
411 |
+rm -f GeoLite2-Country-CSV.zip |
412 |
diff -urN xtables-addons-1.47.1-v6/geoip/xt_geoip_fetch xtables-addons-1.47.1-g2/geoip/xt_geoip_fetch |
413 |
--- xtables-addons-1.47.1-v6/geoip/xt_geoip_fetch 1970-01-01 04:00:00.000000000 +0400 |
414 |
+++ xtables-addons-1.47.1-g2/geoip/xt_geoip_fetch 2018-06-22 19:15:47.000000000 +0400 |
415 |
@@ -0,0 +1,93 @@ |
416 |
+#!/usr/bin/perl |
417 |
+# |
418 |
+# Utility to query GeoIP database |
419 |
+# Copyright Philip Prindeville, 2018 |
420 |
+# |
421 |
+use Getopt::Long; |
422 |
+use Socket qw(AF_INET AF_INET6 inet_ntop); |
423 |
+use warnings; |
424 |
+use strict; |
425 |
+ |
426 |
+sub AF_INET_SIZE() { 4 } |
427 |
+sub AF_INET6_SIZE() { 16 } |
428 |
+ |
429 |
+my $target_dir = "."; |
430 |
+my $ipv4 = 0; |
431 |
+my $ipv6 = 0; |
432 |
+ |
433 |
+&Getopt::Long::Configure(qw(bundling)); |
434 |
+&GetOptions( |
435 |
+ "D=s" => \$target_dir, |
436 |
+ "4" => \$ipv4, |
437 |
+ "6" => \$ipv6, |
438 |
+); |
439 |
+ |
440 |
+if (!-d $target_dir) { |
441 |
+ print STDERR "Target directory $target_dir does not exit.\n"; |
442 |
+ exit 1; |
443 |
+} |
444 |
+ |
445 |
+# if neither specified, assume both |
446 |
+if (! $ipv4 && ! $ipv6) { |
447 |
+ $ipv4 = $ipv6 = 1; |
448 |
+} |
449 |
+ |
450 |
+foreach my $cc (@ARGV) { |
451 |
+ if ($cc !~ m/^([a-z]{2}|a[12]|o1)$/i) { |
452 |
+ print STDERR "Invalid country code '$cc'\n"; |
453 |
+ exit 1; |
454 |
+ } |
455 |
+ |
456 |
+ my $file = $target_dir . '/LE/' . uc($cc) . '.iv4'; |
457 |
+ |
458 |
+ if (! -f $file) { |
459 |
+ printf STDERR "Can't find data for country '$cc'\n"; |
460 |
+ exit 1; |
461 |
+ } |
462 |
+ |
463 |
+ my ($contents, $buffer, $bytes, $fh); |
464 |
+ |
465 |
+ if ($ipv4) { |
466 |
+ open($fh, '<', $file) || die "Couldn't open file for '$cc'\n"; |
467 |
+ |
468 |
+ binmode($fh); |
469 |
+ |
470 |
+ while (($bytes = read($fh, $buffer, AF_INET_SIZE * 2)) == AF_INET_SIZE * 2) { |
471 |
+ my $start = inet_ntop(AF_INET, substr($buffer, 0, AF_INET_SIZE)); |
472 |
+ my $end = inet_ntop(AF_INET, substr($buffer, AF_INET_SIZE)); |
473 |
+ print $start, '-', $end, "\n"; |
474 |
+ } |
475 |
+ close($fh); |
476 |
+ if (! defined $bytes) { |
477 |
+ printf STDERR "Error reading file for '$cc'\n"; |
478 |
+ exit 1; |
479 |
+ } elsif ($bytes != 0) { |
480 |
+ printf STDERR "Short read on file for '$cc'\n"; |
481 |
+ exit 1; |
482 |
+ } |
483 |
+ } |
484 |
+ |
485 |
+ substr($file, -1) = '6'; |
486 |
+ |
487 |
+ if ($ipv6) { |
488 |
+ open($fh, '<', $file) || die "Couldn't open file for '$cc'\n"; |
489 |
+ |
490 |
+ binmode($fh); |
491 |
+ |
492 |
+ while (($bytes = read($fh, $buffer, AF_INET6_SIZE * 2)) == AF_INET6_SIZE * 2) { |
493 |
+ my $start = inet_ntop(AF_INET6, substr($buffer, 0, AF_INET6_SIZE)); |
494 |
+ my $end = inet_ntop(AF_INET6, substr($buffer, AF_INET6_SIZE)); |
495 |
+ print $start, '-', $end, "\n"; |
496 |
+ } |
497 |
+ close($fh); |
498 |
+ if (! defined $bytes) { |
499 |
+ printf STDERR "Error reading file for '$cc'\n"; |
500 |
+ exit 1; |
501 |
+ } elsif ($bytes != 0) { |
502 |
+ printf STDERR "Short read on file for '$cc'\n"; |
503 |
+ exit 1; |
504 |
+ } |
505 |
+ } |
506 |
+} |
507 |
+ |
508 |
+exit 0; |