"SfR Fresh" - the SfR Freeware/Shareware Archive 
Member "wormscan-1.6.1-src/net/websoup/wormscan/Program.java" of archive wormscan-1.6.1-src.tar.gz:
As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) Java source code syntax highlighting with prefixed line numbers.
Alternatively you can here view or download the uninterpreted source code file.
That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
1 package net.websoup.wormscan;
2
3 /*
4 * This file is part of WormScan.
5 *
6 * WormScan is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 import java.io.*;
22 import java.net.InetAddress;
23 import java.text.SimpleDateFormat;
24 import java.util.Locale;
25 import java.util.Date;
26 import java.util.Vector;
27 import java.util.Map;
28 import java.util.TreeMap;
29 import java.util.Enumeration;
30 import java.util.Collection;
31 import java.util.Iterator;
32 import java.util.Properties;
33 import java.util.Stack;
34
35 import org.apache.oro.text.perl.Perl5Util;
36 import org.apache.oro.text.regex.Perl5Compiler;
37 import org.apache.oro.text.regex.Perl5Matcher;
38 import org.apache.oro.text.regex.Pattern;
39 import org.apache.oro.text.regex.MatchResult;
40 import org.apache.tools.bzip2.CBZip2InputStream;
41 import org.apache.velocity.VelocityContext;
42 import org.apache.velocity.Template;
43 import org.apache.velocity.app.Velocity;
44
45 import java.util.zip.GZIPInputStream;
46 import java.util.zip.GZIPOutputStream;
47
48 import net.websoup.wormscan.*;
49 import net.websoup.utility.FilenameResolver;
50
51 import org.jfree.chart.*;
52 import org.jfree.chart.plot.*;
53 import org.jfree.chart.ui.*;
54 import org.jfree.data.*;
55 import org.jfree.data.time.*;
56 import org.jfree.ui.*;
57
58 /**
59 * Title: WormScan
60 * Description: Extendable system to parse web server log files and output reports on attack attempts by various known internet worms.
61 * Copyright: Copyright (c) 2001-2004 Andriy Rozeluk <arozeluk@websoup.net>
62 * @author Andriy Rozeluk
63 * @version 1.6.1
64 */
65
66 public class Program extends Thread {
67 private static int bufferSize = 32768;
68
69 private static final int GZIP_BUFFER = 8192;
70
71 /* how many parser threads to run */
72 private static int NUM_PARSE_THREADS = 1;
73
74 /* current line being processed by this thread */
75 private String currentLine;
76
77 /* identify this thread by a unique number */
78 private int threadID;
79
80 /* our logfile reader */
81 private BufferedReader reader;
82
83 /* the imported worms */
84 private static Worm[] worms;
85
86 /* store our list of attacks here */
87 private static Vector attackStorage;
88
89 /* what size to initialize our attackStorage to */
90 private static int INITIAL_ATTACK_STORAGE_SIZE = 10000;
91
92 /* store our list of hosts here */
93 private static Map sourceStorage;
94
95 /* what size to initialize our sourceStorage to */
96 private static int INITIAL_SOURCE_STORAGE_SIZE = 4000;
97
98 /* save DNS information in file cache [Y/N]*/
99 private static boolean cacheDNS = true;
100
101 /* all filenames of reports will start with this */
102 private static String BASE_FILENAME = "wormreport";
103
104 /* all filenames of reports will end with this */
105 private static String BASE_EXTENSION = ".html";
106
107 /* template to load for detailed report generation */
108 private static String REPORT_TEMPLATE_NAME = "wormreport.vm";
109
110 /* template to load for summary report generation */
111 private static String SUMMARY_TEMPLATE_NAME = "wormsummary.vm";
112
113 /* filename to store number of attacks in */
114 private static String NUM_ATTACKS_FILE = null;
115
116 /* filename to store DNS cache in */
117 private static String DNS_CACHE_FILENAME = "dnscache";
118
119 /* show only latest attack by host in generated reports */
120 private static boolean SHOW_LATEST_ATTACK_ONLY = true;
121
122 /* output GZIP-compressed version of reports while we're at it */
123 private static boolean WRITE_GZIP = true;
124
125 /* go ahead and check to see if log files are GZIP-compressed before parsing */
126 private static boolean CHECK_GZIP = true;
127
128 /* go ahead and check to see if log files are BZIP2-compressed before parsing */
129 private static boolean CHECK_BZIP2 = true;
130
131 /* go ahead and resolve hostnames */
132 static boolean RESOLVE_DNS = true;
133
134 /* how much information should we give the console? */
135 static int OUTPUT_LEVEL = 3;
136
137 /* how many sort threads to run simultaneously */
138 private static int NUM_RUNNING_SORT_THREADS = 1;
139
140 private static String CHART_BASE_FILENAME = "chart";
141 private static int CHART_WIDTH = 640;
142 private static int CHART_HEIGHT = 480;
143 private static boolean CHARTS_PRODUCE = false;
144
145 private Perl5Matcher matcher;
146
147 /* how many resolver threads to run */
148 private static int NUM_RESOLVER_THREADS = 1;
149
150 private static Resolver[] resolverThreads;
151
152 private static Perl5Compiler p5 = new Perl5Compiler();
153 private static Pattern pattern;
154 private static int patternHostIndex = 1;
155 private static int patternDateIndex = 3;
156 private static Locale LOG_LOCALE = Locale.US;
157 private static String defaultDatePattern = "d/MMM/yyy:H:mm:ss";
158 private static SimpleDateFormat patternDateFormat = new SimpleDateFormat(defaultDatePattern, LOG_LOCALE);
159 private static int patternUriIndex = 6;
160 private static boolean patternPrependFilenameToDate = false;
161
162 /* get a bunch of threads ready to resolve hostnames. They'll wait */
163 private static boolean resolveStarted = false;
164
165 private static boolean enableReportByWorm = true;
166 private static boolean enableReportByNumattacks = true;
167 private static boolean enableReportByDate = true;
168 private static boolean enableReportByIP = true;
169 private static boolean enableReportByHostname = true;
170
171 private String filename;
172
173 static {
174 //try it just in case JRE 1.4 is installed for charts
175 System.setProperty("java.awt.headless", "true");
176
177 try {
178 pattern = p5.compile( "^(.*?)\\s+(.*?)\\[(.*?)\\](.*?)(GET|POST|HEAD|SEARCH)\\s+(.*?)\\s+(.*?)$" );
179 }
180 catch ( Exception e){
181 System.err.println( "Exception thrown on init: " + e.getMessage() );
182 }
183 }
184
185 /**
186 * Compress a file with GZIP
187 */
188 private static void writeGZIP( String inputFilename, String outputFilename ) throws Exception {
189 BufferedInputStream fis = new BufferedInputStream( new FileInputStream( inputFilename ) );
190 BufferedOutputStream gos = new BufferedOutputStream( new GZIPOutputStream( new FileOutputStream( outputFilename ) ) );
191 byte[] buffer = new byte[GZIP_BUFFER];
192 for(;;){
193 int numBytes = fis.read( buffer, 0, GZIP_BUFFER );
194 if ( numBytes < 0 ) break;
195 gos.write( buffer, 0, numBytes );
196 }
197 fis.close();
198 gos.close();
199 }
200
201 /**
202 * Some static initialization stuff. Read in our DNS cache and create some of our storage Vectors
203 */
204 private static void initialize(){
205 attackStorage = new Vector( INITIAL_ATTACK_STORAGE_SIZE );
206
207 if ( cacheDNS ){
208 try {
209 ObjectInputStream ois = new ObjectInputStream( new FileInputStream(DNS_CACHE_FILENAME) );
210 sourceStorage = (TreeMap)(ois.readObject());
211 ois.close();
212 if ( OUTPUT_LEVEL > 2 ) System.out.println( "DNS Cache loaded" );
213 }
214 catch( Exception e ){
215 sourceStorage = new TreeMap();
216 if ( OUTPUT_LEVEL > 1 ) System.err.println("DNS Cache couldn't be loaded: " + e.getMessage());
217 }
218
219 try {
220 //do some fixup processing of previously-stored objects
221 Iterator i = sourceStorage.values().iterator();
222 while( i.hasNext() ){
223 AttackSource source = (AttackSource)(i.next());
224 source.convertIP();
225 }
226 }
227 catch( Exception e ){
228 if ( OUTPUT_LEVEL > 1 ) System.err.println("Error performing fixup: " + e.getMessage());
229 e.printStackTrace();
230 }
231 }
232 else {
233 sourceStorage = new TreeMap();
234 }
235 }
236
237 /**
238 * IP/Hostname should only be stored once. This method controls that.
239 */
240 private static AttackSource getAttackSource( String ip ){
241 AttackSource as;
242 //changes 3.26.2002 to not synchronize if only one thread
243 //synchronization too expensive if not needed
244 if ( NUM_PARSE_THREADS > 1 ){
245 synchronized( sourceStorage ){
246 as = (AttackSource)(sourceStorage.get(ip));
247 }
248
249 if ( as == null ){
250 as = new AttackSource( ip );
251 synchronized( sourceStorage ){
252 sourceStorage.put( ip, as );
253
254 Resolver.addSource( as );
255 if ( !resolveStarted ){
256 for ( int i = 0; i < NUM_RESOLVER_THREADS; i++ ) {
257 resolverThreads[i] = new Resolver();
258 resolverThreads[i].setPriority(Thread.MIN_PRIORITY);
259 resolverThreads[i].start();
260 }
261 resolveStarted = true;
262 }
263 }
264 }
265 }
266 else {
267 as = (AttackSource)(sourceStorage.get(ip));
268 if ( as == null ){
269 as = new AttackSource( ip );
270 sourceStorage.put( ip, as );
271 Resolver.addSource( as );
272 if ( !resolveStarted ){
273 for ( int i = 0; i < NUM_RESOLVER_THREADS; i++ ) {
274 resolverThreads[i] = new Resolver();
275 resolverThreads[i].setPriority(Thread.MIN_PRIORITY);
276 resolverThreads[i].start();
277 }
278 resolveStarted = true;
279 }
280 }
281 }
282
283 return as;
284 }
285
286 /**
287 * Constructs a parse thread.
288 */
289 public Program( int id, BufferedReader bf, String filename ) {
290 threadID = id;
291 reader = bf;
292 this.filename = filename;
293 matcher = new Perl5Matcher();
294 }
295
296 /**
297 * Begins a parse thread. This will loop until no more lines can be read from file
298 */
299 public void run(){
300 /* read lines until there's nothing left to read */
301 for(;;){
302 try {
303 currentLine = reader.readLine();
304 }
305 catch (Exception e){
306 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: couldn't read a line: " + e.getMessage() );
307 return;
308 }
309 if ( currentLine == null ) return;
310
311 /* check each line against each worm. Do stuff if attack found. */
312 if ( matcher.matches( currentLine, pattern ) ) {
313 MatchResult lineMatch = matcher.getMatch();
314 String uri = lineMatch.group( patternUriIndex );
315 for( int i = 0; i < worms.length; i++ ){
316 if ( matcher.contains( uri, worms[i].getPattern() ) ){
317 String host = lineMatch.group( patternHostIndex );
318 String tempdate = lineMatch.group( patternDateIndex );
319
320 /* try to parse the date */
321 Date date = null;
322 try {
323 if ( patternPrependFilenameToDate ){
324 tempdate = filename + tempdate;
325 }
326 synchronized( patternDateFormat ){
327 date = patternDateFormat.parse( tempdate );
328 }
329 }
330 catch ( Exception e ){
331 if ( OUTPUT_LEVEL > 1 ) System.err.println("cannot interpret date in log file:" + e.getMessage());
332 }
333
334 /* increment attack counter for worm */
335 worms[i].addAttack();
336 AttackSource as = getAttackSource( host );
337
338 /* increment attack counter for host */
339 as.addAttack();
340
341 Attack attack = new Attack( date, worms[i], as );
342 if ( NUM_PARSE_THREADS > 1 ){
343 synchronized( attackStorage ){
344 attackStorage.add( attack );
345 /* check to see if this is the first or last attack by this host.
346 Update if necessary */
347 as.checkAttackDates( attack );
348 }
349 }
350 else {
351 attackStorage.add( attack );
352 as.checkAttackDates( attack );
353 }
354
355 if ( OUTPUT_LEVEL > 3 )
356 System.out.println( "Thread " + threadID + " found " + attack.getAttackingWorm().getShortName() + " attack" );
357
358 /* no need to continue on this line - we've marked it as an attack already */
359 break;
360 }
361 }
362
363 currentLine = null;
364 }
365 /* screw the yield - it's taking up too much of our time to be polite
366 try {
367 this.yield();
368 }
369 catch ( Exception e ){
370 }
371 */
372 }
373 }
374
375 /**
376 * Return a BufferedReader which can read in the log file. Can also detect and handle GZIP/BZIP2 compression
377 */
378 private static BufferedReader getLogfileReader( String filename ) throws Exception{
379 if ( CHECK_GZIP ){
380 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Detecting GZIP... in " + filename );
381 byte[] test = new byte[2];
382 FileInputStream fis = new FileInputStream( filename );
383 int read = fis.read( test, 0, 2 );
384 if ( read == 2 ){
385 if ( test[0] == new Integer( 31 ).byteValue() && test[1] == new Integer( 139 ).byteValue() ){
386 fis.close();
387 fis = new FileInputStream( filename );
388 GZIPInputStream gis = new GZIPInputStream( fis );
389 InputStreamReader isr = new InputStreamReader( gis );
390 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Log file " + filename + " was compressed with GZIP. Decompression will take place automatically." );
391 return new BufferedReader( isr, bufferSize );
392 }
393 }
394 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Log file was NOT compressed with GZIP" );
395 }
396 if ( CHECK_BZIP2 ){
397 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Detecting BZIP2... in " + filename );
398 byte[] test = new byte[4];
399 FileInputStream fis = new FileInputStream( filename );
400 int read = fis.read( test, 0, 2 );
401 if ( read == 2 ){
402 if ( test[0] == 'B' && test[1] == 'Z' ){
403 CBZip2InputStream bis = new CBZip2InputStream( fis );
404 InputStreamReader isr = new InputStreamReader( bis );
405 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Log file " + filename + " was compressed with BZIP2. Decompression will take place automatically." );
406 return new BufferedReader( isr, bufferSize );
407 }
408 }
409 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Log file was NOT compressed with BZIP2" );
410 }
411 FileReader fileInput = new FileReader( filename );
412 return new BufferedReader( fileInput, bufferSize );
413 }
414
415 public static void main(String[] args) {
416 /* the user entered this as our list of logfile(s) to parse */
417 String LOGFILE = null;
418
419 /* this mess reads in the properties file and sets values accordingly */
420 Properties p = new Properties();
421 try {
422 p.load( new FileInputStream("wormscan.properties") );
423 String temp = p.getProperty( "outputLevel" );
424 if ( temp != null ){
425 try {
426 OUTPUT_LEVEL = Integer.parseInt( temp );
427 }
428 catch ( Exception e){
429 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: outputLevel property was not an integer. Using default" );
430 }
431 }
432 else {
433 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: outputLevel property not specified. Using default" );
434 }
435 temp = p.getProperty("reportFilename");
436 if ( temp != null ) BASE_FILENAME = temp;
437 else {
438 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: reportFilename property not specified. Using default" );
439 }
440 temp = p.getProperty("reportExtension");
441 if ( temp != null ) BASE_EXTENSION = temp;
442 else {
443 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: reportExtension property not specified. Using default" );
444 }
445 temp = p.getProperty( "logFile" );
446 if ( temp != null ) LOGFILE = temp;
447 else {
448 if ( OUTPUT_LEVEL > 0 ) System.err.println( "logFile property not specified in wormscan.properties - unable to proceed" );
449 System.exit(1);
450 }
451 temp = p.getProperty( "numAttacksFile" );
452 if ( temp != null ) NUM_ATTACKS_FILE = temp;
453 else {
454 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numAttacksFile property not specified. File won't be written" );
455 }
456 temp = p.getProperty( "initialAttackStorageSize" );
457 if ( temp != null ){
458 try {
459 INITIAL_ATTACK_STORAGE_SIZE = Integer.parseInt( temp );
460 }
461 catch ( Exception e){
462 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: initialAttackStorageSize property was not an integer. Using default" );
463 }
464 }
465 else {
466 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: initialAttackStorageSize property not specified. Using default" );
467 }
468 temp = p.getProperty( "initialSourceStorageSize" );
469 if ( temp != null ){
470 try {
471 INITIAL_SOURCE_STORAGE_SIZE = Integer.parseInt( temp );
472 }
473 catch ( Exception e){
474 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: initialSourceStorageSize property was not an integer. Using default" );
475 }
476 }
477 else {
478 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: initialSourceStorageSize property not specified. Using default" );
479 }
480 temp = p.getProperty( "numParseThreads" );
481 if ( temp != null ){
482 try {
483 NUM_PARSE_THREADS = Integer.parseInt( temp );
484 }
485 catch ( Exception e){
486 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numParseThreads property was not an integer. Using default" );
487 }
488 }
489 else {
490 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numParseThreads property not specified. Using default" );
491 }
492 temp = p.getProperty( "numResolverThreads" );
493 if ( temp != null ){
494 try {
495 NUM_RESOLVER_THREADS = Integer.parseInt( temp );
496 }
497 catch ( Exception e){
498 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numResolverThreads property was not an integer. Using default" );
499 }
500 }
501 else {
502 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numResolverThreads property not specified. Using default" );
503 }
504 temp = p.getProperty( "maxResolverQueue" );
505 if ( temp != null ){
506 try {
507 Resolver.MAX_WAITING = Integer.parseInt( temp );
508 }
509 catch ( Exception e){
510 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: maxResolverQueue property was not an integer. Using default" );
511 }
512 }
513 else {
514 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numResolverThreads property not specified. Using default" );
515 }
516 temp = p.getProperty( "numSortThreads" );
517 if ( temp != null ){
518 try {
519 NUM_RUNNING_SORT_THREADS = Integer.parseInt( temp );
520 }
521 catch ( Exception e){
522 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numSortThreads property was not an integer. Using default" );
523 }
524 }
525 else {
526 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: numSortThreads property not specified. Using default" );
527 }
528 temp = p.getProperty( "cacheDNS" );
529 if ( temp != null ){
530 if ( temp.equals("1") ){
531 cacheDNS = true;
532 temp = p.getProperty( "cacheFilename" );
533 if ( temp != null ){
534 DNS_CACHE_FILENAME = temp;
535 }
536 else if ( OUTPUT_LEVEL > 1 ){
537 System.err.println( "Warning: cacheFilename property not specified. Using default" );
538 }
539 }
540 else if ( temp.equals("0") ){
541 cacheDNS = false;
542 }
543 else if ( OUTPUT_LEVEL > 1 ){
544 System.err.println( "Warning: cacheDNS property is not 1 or 0. Using default" );
545 }
546 }
547 else {
548 if ( OUTPUT_LEVEL > 1 ){
549 System.err.println( "Warning: numResolverThreads property not specified. Using default" );
550 }
551 }
552 temp = p.getProperty( "showLatestAttackOnly" );
553 if ( temp != null ){
554 if ( temp.equals("1") ){
555 SHOW_LATEST_ATTACK_ONLY = true;
556 }
557 else if ( temp.equals("0") ){
558 SHOW_LATEST_ATTACK_ONLY = false;
559 }
560 else if ( OUTPUT_LEVEL > 1 ){
561 System.err.println( "Warning: showLatestAttackOnly property is not 1 or 0. Using default" );
562 }
563 }
564 else {
565 if ( OUTPUT_LEVEL > 1 ){
566 System.err.println( "Warning: showLatestAttackOnly property not specified. Using default" );
567 }
568 }
569 temp = p.getProperty( "resolveDNS" );
570 if ( temp != null ){
571 if ( temp.equals("1") ){
572 RESOLVE_DNS = true;
573 }
574 else if ( temp.equals("0") ){
575 RESOLVE_DNS = false;
576 }
577 else if ( OUTPUT_LEVEL > 1 ){
578 System.err.println( "Warning: resolveDNS property is not 1 or 0. Using default" );
579 }
580 }
581 else {
582 if ( OUTPUT_LEVEL > 1 ){
583 System.err.println( "Warning: resolveDNS property not specified. Using default" );
584 }
585 }
586 temp = p.getProperty( "logCheckGZIP" );
587 if ( temp != null ){
588 if ( temp.equals("1") ){
589 CHECK_GZIP = true;
590 }
591 else if ( temp.equals("0") ){
592 CHECK_GZIP = false;
593 }
594 else if ( OUTPUT_LEVEL > 1 ){
595 System.err.println( "Warning: logCheckGZIP property is not 1 or 0. Using default" );
596 }
597 }
598 else {
599 if ( OUTPUT_LEVEL > 1 ){
600 System.err.println( "Warning: logCheckGZIP property not specified. Using default" );
601 }
602 }
603 temp = p.getProperty( "logCheckBZIP2" );
604 if ( temp != null ){
605 if ( temp.equals("1") ){
606 CHECK_BZIP2 = true;
607 }
608 else if ( temp.equals("0") ){
609 CHECK_BZIP2 = false;
610 }
611 else if ( OUTPUT_LEVEL > 1 ){
612 System.err.println( "Warning: logCheckBZIP2 property is not 1 or 0. Using default" );
613 }
614 }
615 else {
616 if ( OUTPUT_LEVEL > 1 ){
617 System.err.println( "Warning: logCheckBZIP2 property not specified. Using default" );
618 }
619 }
620 temp = p.getProperty( "writeGZIP" );
621 if ( temp != null ){
622 if ( temp.equals("1") ){
623 WRITE_GZIP = true;
624 }
625 else if ( temp.equals("0") ){
626 WRITE_GZIP = false;
627 }
628 else if ( OUTPUT_LEVEL > 1 ){
629 System.err.println( "Warning: writeGZIP property is not 1 or 0. Using default" );
630 }
631 }
632 else {
633 if ( OUTPUT_LEVEL > 1 ){
634 System.err.println( "Warning: writeGZIP property not specified. Using default" );
635 }
636 }
637 temp = p.getProperty( "disableReportByWorm" );
638 if ( temp != null && temp.equals("1") ){
639 enableReportByWorm = false;
640 }
641 temp = p.getProperty( "disableReportByNumattacks" );
642 if ( temp != null && temp.equals("1") ){
643 enableReportByNumattacks = false;
644 }
645 temp = p.getProperty( "disableReportByDate" );
646 if ( temp != null && temp.equals("1") ){
647 enableReportByDate = false;
648 }
649 temp = p.getProperty( "disableReportByIP" );
650 if ( temp != null && temp.equals("1") ){
651 enableReportByIP = false;
652 }
653 temp = p.getProperty( "disableReportByHostname" );
654 if ( temp != null && temp.equals("1") ){
655 enableReportByHostname = false;
656 }
657 temp = p.getProperty( "produceCharts" );
658 if ( temp != null ){
659 if ( temp.equals("1") ) {
660 CHARTS_PRODUCE = true;
661 temp = p.getProperty( "chartFilename" );
662 if ( temp != null && !temp.equals("") ){
663 CHART_BASE_FILENAME = temp;
664 }
665 else if ( OUTPUT_LEVEL > 1 ){
666 System.err.println( "Warning: chartFilename property not specified. Using default" );
667 }
668 temp = p.getProperty( "chartWidth" );
669 if ( temp != null && !temp.equals("") ){
670 try {
671 int tempInt = Integer.parseInt( temp );
672 if ( tempInt >= 50 ){
673 CHART_WIDTH = tempInt;
674 }
675 else if ( OUTPUT_LEVEL > 1 ){
676 System.err.println( "Warning: chartWidth property is too small. Using default" );
677 }
678 }
679 catch ( Exception ex ){
680 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: chartWidth property is not an integer. Using default" );
681 }
682 }
683 else if ( OUTPUT_LEVEL > 1 ){
684 System.err.println( "Warning: chartWidth property not specified. Using default" );
685 }
686 temp = p.getProperty( "chartHeight" );
687 if ( temp != null && !temp.equals("") ){
688 try {
689 int tempInt = Integer.parseInt( temp );
690 if ( tempInt >= 50 ){
691 CHART_HEIGHT = tempInt;
692 }
693 else if ( OUTPUT_LEVEL > 1 ){
694 System.err.println( "Warning: chartHeight property is too small. Using default" );
695 }
696 }
697 catch ( Exception ex ){
698 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: chartHeight property is not an integer. Using default" );
699 }
700 }
701 else if ( OUTPUT_LEVEL > 1 ){
702 System.err.println( "Warning: chartHeight property not specified. Using default" );
703 }
704 }
705 else if ( temp.equals("0") ){
706 CHARTS_PRODUCE = false;
707 }
708 else if ( OUTPUT_LEVEL > 1 ) {
709 System.err.println( "Warning: produceCharts property is not 1 or 0. Using default" );
710 }
711 }
712 else {
713 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: produceCharts property not specified. Charts will not be created" );
714 }
715
716 String locLanguage = "en";
717 String locCountry = "US";
718 temp = p.getProperty( "serverLocaleLanguage" );
719 if ( temp != null && !temp.equals("")){
720 locLanguage = temp;
721 }
722 else {
723 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: serverLocaleLanguage property not specified. Using default" );
724 }
725 temp = p.getProperty( "serverLocaleCountry" );
726 if ( temp != null && !temp.equals("")){
727 locCountry = temp;
728 }
729 else {
730 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: serverLocaleCountry property not specified. Using default" );
731 }
732 if ( !locLanguage.equals("en") || !locCountry.equals("US") ){
733 try {
734 Locale tempLocale = new Locale( locLanguage, locCountry );
735 LOG_LOCALE = tempLocale;
736 patternDateFormat = new SimpleDateFormat(defaultDatePattern, LOG_LOCALE);
737 }
738 catch( Exception e ){
739 System.err.println( "Warning: unable to create Locale for " + locLanguage + ", " + locCountry + ": " + e.getMessage() );
740 }
741 }
742 temp = p.getProperty( "serverType" );
743 if ( temp == null ){
744 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: serverType property not specified. Using default" );
745 temp = "Apache";
746 }
747 String serverType = temp;
748 boolean caseSens = true;
749 temp = p.getProperty( serverType + "_caseSensitive" );
750 if ( temp != null && temp.trim().equals("0") ){
751 caseSens = false;
752 }
753 temp = p.getProperty( serverType + "_pattern" );
754 if ( temp != null ){
755 try{
756 if ( caseSens ){
757 pattern = p5.compile( temp );
758 }
759 else {
760 pattern = p5.compile( temp, p5.CASE_INSENSITIVE_MASK );
761 }
762 }
763 catch ( Exception e ){
764 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Error: could not process pattern: " + e.getMessage() );
765 }
766 }
767 else{
768 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_pattern property not specified. Using default" );
769 }
770 temp = p.getProperty( serverType + "_hostIndex" );
771 if ( temp != null ){
772 try {
773 patternHostIndex = Integer.parseInt(temp);
774 }
775 catch( Exception e ){
776 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_hostIndex property is not an integer. Using default" );
777 }
778 }
779 else {
780 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_hostIndex property not specified. Using default" );
781 }
782 temp = p.getProperty( serverType + "_dateIndex" );
783 if ( temp != null ){
784 try {
785 patternDateIndex = Integer.parseInt(temp);
786 }
787 catch( Exception e ){
788 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_dateIndex property is not an integer. Using default" );
789 }
790 }
791 else {
792 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_dateIndex property not specified. Using default" );
793 }
794 temp = p.getProperty( serverType + "_dateFormat" );
795 if ( temp != null ) patternDateFormat = new SimpleDateFormat( temp, LOG_LOCALE );
796 else {
797 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: dateFormat property not specified. Using default" );
798 }
799 temp = p.getProperty( serverType + "_uriIndex" );
800 if ( temp != null ){
801 try {
802 patternUriIndex = Integer.parseInt(temp);
803 }
804 catch( Exception e ){
805 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_uriIndex property is not an integer. Using default" );
806 }
807 }
808 else {
809 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_uriIndex property not specified. Using default" );
810 }
811 temp = p.getProperty( serverType + "_prependFilenameToDate" );
812 if ( temp != null ){
813 if ( temp.equals("1") ){
814 patternPrependFilenameToDate = true;
815 }
816 else if ( temp.equals("0") ){
817 patternPrependFilenameToDate = false;
818 }
819 else if ( OUTPUT_LEVEL > 1 ){
820 System.err.println( "Warning: " + serverType + "_prependFilenameToDate property is not 1 or 0. Using default" );
821 }
822 }
823 else {
824 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Warning: " + serverType + "_prependFilenameToDate property not specified. Using default" );
825 }
826 temp = p.getProperty( "numWorms" );
827 if ( temp != null ){
828 int numWorms = 0;
829 try {
830 numWorms = Integer.parseInt( temp );
831 }
832 catch ( Exception e){
833 if ( OUTPUT_LEVEL > 0 ) System.err.println( "numWorms property is not an integer. Unable to continue" );
834 System.exit(1);
835 }
836 if ( numWorms > 0 ){
837 Vector wormStorage = new Vector( numWorms );
838 for ( int i = 0; i < numWorms; i++ ){
839 Worm worm = new Worm();
840 worm.setName( p.getProperty("worm" + i + "_name") );
841 worm.setShortName( p.getProperty("worm" + i + "_shortName") );
842 worm.setColour( p.getProperty("worm" + i + "_colour") );
843 temp = p.getProperty( "worm" + i + "_caseSensitive" );
844 if ( temp != null && temp.trim().equals("0") ){
845 caseSens = false;
846 }
847 else{
848 caseSens = true;
849 }
850
851 try {
852 worm.setPatternString( p.getProperty("worm" + i + "_pattern"), caseSens );
853 wormStorage.add( worm );
854 }
855 catch ( Exception e ){
856 if ( OUTPUT_LEVEL > 1 ) System.err.println( "Worm " + i + ": " + worm.getName() + " has an invalid pattern (unable to scan for this worm): " + e.getMessage() );
857 }
858 }
859 worms = (Worm[])( wormStorage.toArray( new Worm[ wormStorage.size() ] ));
860 if ( OUTPUT_LEVEL > 2 ) System.out.println( "" + worms.length + " worms loaded for scanning" );
861 }
862 if ( worms == null || worms.length <= 0 ){
863 if ( OUTPUT_LEVEL > 0 ) System.err.println( "Unable to continue. No worms to scan for" );
864 System.exit(1);
865 }
866 }
867 else {
868 if ( OUTPUT_LEVEL > 0 ) System.err.println( "numWorms property not specified. Unable to continue" );
869 System.exit(1);
870 }
871 }
872 catch( Exception e ){
873 if ( OUTPUT_LEVEL > 0 ) System.err.println( "Unable to load properties file: wormscan.properties - " + e.getMessage() );
874 e.printStackTrace();
875 System.exit(1);
876 }
877
878 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Started: " + new Date() );
879 initialize();
880
881 Program[] threads = new Program[ NUM_PARSE_THREADS ];
882
883 if ( !RESOLVE_DNS ){
884 NUM_RESOLVER_THREADS = 0;
885 cacheDNS = false;
886 }
887 resolverThreads = new Resolver[ NUM_RESOLVER_THREADS ];
888
889 try {
890 /* locate all log files matching to specified properties */
891 String[] logFilenames = new FilenameResolver().resolve( LOGFILE );
892
893 if ( logFilenames.length <= 0 ) {
894 if ( OUTPUT_LEVEL > 0 ) System.err.println( "No log files were found with pattern: " + LOGFILE );
895 System.exit(1);
896 }
897
898 /* loop through, read each log file one by one until complete */
899 for ( int logIndex = 0; logIndex < logFilenames.length; logIndex++ ){
900 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Parsing log file " + logFilenames[ logIndex ] );
901 BufferedReader input = null;
902 try {
903 input = getLogfileReader( logFilenames[ logIndex ] );
904 }
905 catch ( Exception e ){
906 if ( OUTPUT_LEVEL > 0 ) System.err.println( "Warning - error reading file " + logFilenames[ logIndex ] + ": " + e.getMessage() );
907 continue;
908 }
909
910 for( int i = 0; i < NUM_PARSE_THREADS; i++ ){
911 String fileComponent;
912 fileComponent = logFilenames[ logIndex ].substring( logFilenames[ logIndex ].lastIndexOf( File.separator ) + 1 );
913 threads[i] = new Program( i, input, fileComponent );
914 threads[i].start();
915 }
916
917 Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
918
919 for( int i = 0; i < NUM_PARSE_THREADS; i++ ){
920 threads[i].join();
921 threads[i] = null;
922 }
923 input.close();
924
925 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Completed parsing log file " + logFilenames[ logIndex ] );
926 System.gc();
927 }
928
929 /* signal to Resolver that no more hostnames will be added to Stack */
930 Resolver.areDone();
931
932 if ( resolveStarted ){
933 for ( int i = 0; i < NUM_RESOLVER_THREADS; i++ ) {
934 if ( resolverThreads[i].isAlive() ){
935 try {
936 resolverThreads[i].setPriority(Thread.NORM_PRIORITY);
937 }
938 catch( Exception e ){
939 }
940 }
941 }
942 }
943
944 /* if hosts are cached, we can't rely on the number of unique hosts
945 to tell us how many unique hosts attacked us in this run.
946 Instead, count how many hosts attacked us this time around. */
947 int numhosts = 0;
948 Collection numhosts_c = sourceStorage.values();
949 Iterator numhosts_i = numhosts_c.iterator();
950 while ( numhosts_i.hasNext() ) {
951 if (((AttackSource)(numhosts_i.next())).getAttackCount() > 0 ){
952 numhosts ++;
953 }
954 }
955
956 int totalAttacks = attackStorage.size();
957 Vector fullAttackStorage = attackStorage;
958 Sorter fullAttackDateSorter = null;
959 if ( SHOW_LATEST_ATTACK_ONLY || !enableReportByDate ){
960 attackStorage = new Vector( numhosts );
961 Iterator i = fullAttackStorage.iterator();
962 while ( i.hasNext() ){
963 try {
964 Attack a = (Attack)(i.next());
965 if ( a.getSourceOfAttack().isLastAttack( a ) ){
966 attackStorage.add( a );
967 }
968 }
969 catch( Exception ex ){
970 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Error removing attack: " + ex.getMessage() );
971 }
972 }
973 fullAttackDateSorter = new Sorter( fullAttackStorage, Sorter.SORT_DATE, "date of ALL attacks" );
974 }
975
976 /* begin sorting of data - we can sort everything but hostname so far */
977 Stack sorterThreadStack = new Stack();
978 Sorter[] sorterThreads = new Sorter[ 5 ];
979
980 Sorter[] runningSorterThreads = new Sorter[NUM_RUNNING_SORT_THREADS];
981 for( int i = 0; i < NUM_RUNNING_SORT_THREADS; i++ ){
982 if ( !sorterThreadStack.empty() ){
983 runningSorterThreads[i] = (Sorter)(sorterThreadStack.pop());
984 runningSorterThreads[i].start();
985 }
986 else {
987 break;
988 }
989 }
990
991 /* loop until finished resolving hostnames */
992 for(;;){
993 boolean allDone = true;
994
995 /* scan through the number of sorters waiting to be run in the queue */
996 for ( int j = 0; j < NUM_RUNNING_SORT_THREADS; j++ ){
997 /* one of sorter threads is done? start another one if needed */
998 if ( runningSorterThreads[j] != null && runningSorterThreads[j].isFinished() ){
999 runningSorterThreads[j].join();
1000 if ( OUTPUT_LEVEL > 2 ) System.err.println( "Sorting by " + runningSorterThreads[j].getSortMethodName() + " complete" );
1001
1002 if ( !sorterThreadStack.empty() ){
1003 runningSorterThreads[j] = (Sorter)(sorterThreadStack.pop());
1004 runningSorterThreads[j].start();
1005 }
1006 else {
1007 runningSorterThreads[j] = null;
1008 }
1009 }
1010 }
1011
1012 /* check to see if we're done resolving */
1013 if ( resolveStarted ){
1014 for ( int i = 0; i < NUM_RESOLVER_THREADS; i++ ){
1015 /* if we're done resolving then join up */
1016 if ( resolverThreads[i] != null && !resolverThreads[i].isAlive() ){
1017 resolverThreads[i].join();
1018 resolverThreads[i] = null;
1019 }
1020 else if ( resolverThreads[i] != null ){
1021 allDone = false;
1022 }
1023 }
1024 }
1025
1026 if ( allDone ) break;
1027
1028 Thread.sleep( 5000 );
1029 }
1030
1031 resolverThreads = null;
1032 System.gc();
1033
1034 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Completed resolving hostnames" );
1035
1036 /* write out DNS cache using Serialization mechanism */
1037 if ( cacheDNS ){
1038 try {
1039 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream( DNS_CACHE_FILENAME ) );
1040 oos.writeObject( sourceStorage );
1041 oos.close();
1042 if ( OUTPUT_LEVEL > 2 ) System.out.println( "DNS Cache written to disk" );
1043 }
1044 catch ( Exception e ){
1045 if ( OUTPUT_LEVEL > 1 ) System.err.println("DNS Cache couldn't be saved: " + e.getMessage());
1046 }
1047 }
1048
1049 /* write out file containing number of attacks */
1050 if ( NUM_ATTACKS_FILE != null ){
1051 try {
1052 FileOutputStream fos = new FileOutputStream( NUM_ATTACKS_FILE );
1053 fos.write( Integer.toString( totalAttacks ).getBytes() );
1054 fos.close();
1055 }
1056 catch ( Exception e ){
1057 if ( OUTPUT_LEVEL > 1 ) System.err.println("Couldn't write out number of attacks to file: " + e.getMessage());
1058 }
1059 }
1060
1061 if (enableReportByWorm){
1062 sorterThreads[0] = new Sorter( attackStorage, Sorter.SORT_WORM, "_worm" );
1063 sorterThreads[0].setPriority(Thread.MIN_PRIORITY);
1064 sorterThreadStack.push( sorterThreads[0] );
1065 }
1066 if (enableReportByNumattacks){
1067 sorterThreads[1] = new Sorter( attackStorage, Sorter.SORT_NUMATTACKS, "_numattacks" );
1068 sorterThreads[1].setPriority(Thread.MIN_PRIORITY);
1069 sorterThreadStack.push( sorterThreads[1] );
1070 }
1071 if (enableReportByDate){
1072 sorterThreads[2] = new Sorter( attackStorage, Sorter.SORT_DATE, "_date" );
1073 sorterThreads[2].setPriority(Thread.MIN_PRIORITY);
1074 sorterThreadStack.push( sorterThreads[2] );
1075 }
1076
1077 if ( fullAttackDateSorter == null ){
1078 fullAttackDateSorter = sorterThreads[2];
1079 }
1080 else {
1081 sorterThreadStack.push( fullAttackDateSorter );
1082 }
1083
1084 /* now is a safe time to sort by hostname and IP */
1085 if ( enableReportByIP ){
1086 sorterThreads[3] = new Sorter( attackStorage, Sorter.SORT_IP, "_ip" );
1087 sorterThreadStack.push( sorterThreads[3] );
1088 }
1089 if ( enableReportByHostname ){
1090 sorterThreads[4] = new Sorter( attackStorage, Sorter.SORT_HOSTNAME, "_hostname" );
1091 sorterThreadStack.push( sorterThreads[4] );
1092 }
1093
1094 for ( int i = 0; i < sorterThreads.length; i++ ){
1095 if ( sorterThreads[i] != null ){
1096 sorterThreads[i].setPriority(Thread.NORM_PRIORITY);
1097 }
1098 }
1099
1100 /* initialize Velocity */
1101 Velocity.init();
1102
1103 /* begin output */
1104 VelocityContext context = new VelocityContext();
1105
1106 /* find the base filename to be used for relative paths */
1107 String fileComponent;
1108 fileComponent = BASE_FILENAME.substring( BASE_FILENAME.lastIndexOf( File.separator ) + 1 );
1109
1110 /* prepare the info which will be visible from within the templates */
1111 if ( CHARTS_PRODUCE ){
1112 context.put( "chartFilename", CHART_BASE_FILENAME.substring( CHART_BASE_FILENAME.lastIndexOf( File.separator ) + 1) );
1113 }
1114
1115 boolean detailedEnabled = false;
1116 if ( enableReportByWorm ){
1117 context.put( "enableReportByWorm", "yes" );
1118 detailedEnabled = true;
1119 }
1120 if ( enableReportByNumattacks ){
1121 context.put( "enableReportByNumattacks", "yes" );
1122 detailedEnabled = true;
1123 }
1124 if ( enableReportByDate ){
1125 context.put( "enableReportByDate", "yes" );
1126 detailedEnabled = true;
1127 }
1128 if ( enableReportByIP ){
1129 context.put( "enableReportByIP", "yes" );
1130 detailedEnabled = true;
1131 }
1132 if ( enableReportByHostname ){
1133 context.put( "enableReportByHostname", "yes" );
1134 detailedEnabled = true;
1135 }
1136 if ( detailedEnabled ){
1137 context.put( "enableDetailedReports", "yes" );
1138 }
1139
1140 context.put( "worms", worms );
1141 context.put( "numattacks", new Integer( totalAttacks ) );
1142 context.put( "hosts", sourceStorage );
1143 context.put( "numhosts", new Integer( numhosts ));
1144 context.put( "timestamp", new Date() );
1145 context.put( "basename", fileComponent );
1146 context.put( "baseext", BASE_EXTENSION);
1147 context.put( "showLatestAttackOnly", new Boolean( SHOW_LATEST_ATTACK_ONLY ) );
1148
1149 Template template = null;
1150
1151 /* write out the summary report */
1152 try{
1153 template = Velocity.getTemplate( SUMMARY_TEMPLATE_NAME );
1154 FileWriter fw = new FileWriter( BASE_FILENAME + BASE_EXTENSION );
1155 template.merge( context, fw );
1156 fw.close();
1157 if ( WRITE_GZIP ){
1158 try {
1159 writeGZIP( BASE_FILENAME + BASE_EXTENSION, BASE_FILENAME + BASE_EXTENSION + ".gz" );
1160 }
1161 catch ( Exception e ){
1162 if ( OUTPUT_LEVEL > 0 ) System.out.println( "Unable to write GZIP report: " + e.getMessage() );
1163 }
1164 }
1165 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Report summary written to disk" );
1166 }
1167 catch( Exception e ){
1168 if ( OUTPUT_LEVEL > 0 ) System.err.println( "Unable to produce report summary: " + e.getMessage() );
1169 }
1170
1171 /* run until all sorts are either done or running (not waiting) */
1172 while( !sorterThreadStack.empty() ){
1173 /* scan through the number of sorters waiting to be run in the queue */
1174 for ( int j = 0; j < NUM_RUNNING_SORT_THREADS; j++ ){
1175 /* if empty slot, fill it */
1176 if ( runningSorterThreads[j] == null ){
1177 if ( !sorterThreadStack.empty() ){
1178 runningSorterThreads[j] = (Sorter)(sorterThreadStack.pop());
1179 runningSorterThreads[j].start();
1180 }
1181 }
1182 /* if one is done, join up with it and fill slot if possible */
1183 else if ( runningSorterThreads[j].isFinished() ){
1184 runningSorterThreads[j].join();
1185 if ( OUTPUT_LEVEL > 2 ) System.err.println( "Sorting by " + runningSorterThreads[j].getSortMethodName() + " complete" );
1186
1187 if ( !sorterThreadStack.empty() ){
1188 runningSorterThreads[j] = (Sorter)(sorterThreadStack.pop());
1189 runningSorterThreads[j].start();
1190 }
1191 else {
1192 runningSorterThreads[j] = null;
1193 }
1194 }
1195 }
1196
1197 if ( sorterThreadStack.empty() ) break;
1198
1199 Thread.sleep( 5000 );
1200 }
1201
1202 System.gc();
1203
1204 /* wait until all sorts done */
1205 for( int i = 0; i < runningSorterThreads.length; i++ ){
1206 if( runningSorterThreads[i] != null ) {
1207 runningSorterThreads[i].join();
1208 if ( OUTPUT_LEVEL > 2 ) System.err.println( "Sorting by " + runningSorterThreads[i].getSortMethodName() + " complete" );
1209 runningSorterThreads[i] = null;
1210 }
1211 }
1212
1213 runningSorterThreads = null;
1214
1215 Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
1216
1217 /* write out all of the reports */
1218 try {
1219 template = Velocity.getTemplate( REPORT_TEMPLATE_NAME );
1220
1221 writeChart( CHART_BASE_FILENAME, fullAttackDateSorter.getResults() );
1222 for ( int i = 0; i < sorterThreads.length; i++ ){
1223 System.gc();
1224 if ( sorterThreads[i] != null ){
1225 writeReport( template, context, sorterThreads[i].getSortMethodName(), sorterThreads[i].getResults(), true );
1226 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Detailed reports sorted by " + sorterThreads[i].getSortMethodName() + " written to disk" );
1227 }
1228 }
1229
1230 }
1231 catch( Exception e ){
1232 if ( OUTPUT_LEVEL > 0 ) System.err.println( "Unable to produce detailed reports: " + e.getMessage() );
1233 e.printStackTrace();
1234 }
1235
1236
1237 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Total attacks: " + totalAttacks );
1238 for ( int i = 0; i < worms.length; i++ ){
1239 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Total " + worms[i].getName() + " attacks: " + worms[i].getNumAttacks() );
1240 }
1241 }
1242 catch ( Exception e ){
1243 if ( OUTPUT_LEVEL > 1 ) System.err.println("Exception: " + e.getMessage());
1244 if ( OUTPUT_LEVEL > 3 ) e.printStackTrace();
1245 }
1246 if ( OUTPUT_LEVEL > 2 ) System.out.println( "Finished: " + new Date() );
1247 }
1248
1249 private static void writeChart( String filename, Object[] results ){
1250 int cd = -1;
1251 int cm = -1;
1252 int cy = -1;
1253
1254 Worm[] wormsToday = new Worm[ worms.length ];
1255 Worm[] wormsTotal = new Worm[ worms.length ];
1256 Worm[] attacksLastDay = new Worm[ worms.length ];
1257 java.util.Calendar yesterday = java.util.Calendar.getInstance();
1258 yesterday.add( java.util.Calendar.DATE, -1 );
1259
1260 TimeSeries[] wormGraph = new TimeSeries[ worms.length ];
1261 TimeSeries[] wormGraphTotal = new TimeSeries[ worms.length ];
1262
1263 //create new worm counters and graphs for each worm
1264 for ( int i = 0; i < worms.length; i++ ){
1265 wormsToday[i] = new Worm();
1266 wormsToday[i].setName( worms[i].getName() );
1267 wormsToday[i].setShortName( worms[i].getShortName() );
1268
1269 wormsTotal[i] = new Worm();
1270 wormsTotal[i].setName( worms[i].getName() );
1271 wormsTotal[i].setShortName( worms[i].getShortName() );
1272
1273 attacksLastDay[i] = new Worm();
1274 attacksLastDay[i].setName( worms[i].getName() );
1275 attacksLastDay[i].setShortName( worms[i].getShortName() );
1276
1277 wormGraph[i] = new TimeSeries( worms[i].getName() );
1278 wormGraphTotal[i] = new TimeSeries( worms[i].getName() );
1279 }
1280
1281 //loop through attacks and count number of attacks by worm per day
1282 boolean onLastDay = false;
1283 java.util.Calendar workingCalendar = java.util.Calendar.getInstance();
1284 for( int i = 0; i < results.length; i++ ){
1285 if ( results[i] instanceof Attack ){
1286 Attack attack = (Attack)(results[i]);
1287 Date doa = attack.getDateOfAttack();
1288 workingCalendar.setTime( doa );
1289 int d = workingCalendar.get( java.util.Calendar.DAY_OF_MONTH );
1290 int m = workingCalendar.get( java.util.Calendar.MONTH ) + 1;
1291 int y = workingCalendar.get( java.util.Calendar.YEAR );
1292
1293 //last day means last 24 hours, NOT last calendar date
1294 if ( !onLastDay && workingCalendar.after( yesterday ) ){
1295 onLastDay = true;
1296 }
1297
1298 //this only happens the first time through
1299 if ( cd == -1 && cm == -1 && cy == -1 ){
1300 cd = d;
1301 cm = m;
1302 cy = y;
1303 }
1304 //the date changed. deal with it.
1305 else if ( cd != d || cm != m || cy != y ){
1306 for ( int j = 0; j < wormsToday.length; j++ ){
1307 try {
1308 wormGraph[j].add(new Day(cd,cm,cy), new Integer( wormsToday[j].getNumAttacks() ));
1309 wormGraphTotal[j].add(new Day(cd,cm,cy), new Integer( wormsTotal[j].getNumAttacks() ));
1310 }
1311 catch( SeriesException sex ){
1312 System.err.println( sex.getMessage() );
1313 }
1314 wormsToday[j].resetNumAttacks();
1315 }
1316 cd = d;
1317 cm = m;
1318 cy = y;
1319 }
1320
1321 //so who attacked us just now?
1322 for( int j = 0; j < wormsToday.length; j++ ){
1323 if ( wormsToday[j].getName().equals( attack.getAttackingWorm().getName() ) ){
1324 wormsToday[j].addAttack();
1325 wormsTotal[j].addAttack();
1326 if ( onLastDay ){
1327 attacksLastDay[j].addAttack();
1328 }
1329 break;
1330 }
1331 }
1332
1333 }//end if
1334 }//end for
1335
1336 //finish up the last of it and add to TimeSeriesCollection
1337 TimeSeriesCollection allData = new TimeSeriesCollection();
1338 TimeSeriesCollection allDataTotal = new TimeSeriesCollection();
1339 for ( int j = 0; j < wormGraph.length; j++ ){
1340 if ( workingCalendar != null ){
1341 allData.addSeries( wormGraph[j] );
1342 allDataTotal.addSeries( wormGraphTotal[j] );
1343 }
1344 }
1345
1346 try {
1347 JFreeChart dailyChart = ChartFactory.createTimeSeriesChart("Daily Attacks by Worm", "Date", "Number of Attacks Daily", allData, true, false, false);
1348 JFreeChart totalChart = ChartFactory.createTimeSeriesChart("Total Attacks by Worm", "Date", "Total Number of Attacks", allDataTotal, true, false, false);
1349
1350 BufferedOutputStream fos = new BufferedOutputStream( new FileOutputStream(filename + "_daily.jpg") );
1351 ChartUtilities.writeChartAsJPEG(fos, dailyChart, CHART_WIDTH, CHART_HEIGHT);
1352 fos.close();
1353
1354 BufferedOutputStream fos2 = new BufferedOutputStream( new FileOutputStream(filename + "_total.jpg") );
1355 ChartUtilities.writeChartAsJPEG(fos2, totalChart, CHART_WIDTH, CHART_HEIGHT);
1356 fos2.close();
1357
1358 //since chart doesn't handle 0-values well, we need to count non-zero categories
1359 int numActiveWormsToday = 0;
1360 for ( int i = 0; i < attacksLastDay.length; i++ ){
1361 if ( attacksLastDay[i].getNumAttacks() > 0 )
1362 numActiveWormsToday++;
1363 }
1364
1365 //now set up the raw data for the pie chart for last 24 hours
1366 Number[][] pieRawData = new Integer[1][ numActiveWormsToday ];
1367 String[] series = new String[1];
1368 series[0] = "Worms";
1369 String[] pieWormNames = new String[ numActiveWormsToday ];
1370 int activeWormIndex = 0;
1371 for ( int i = 0; i < attacksLastDay.length; i++ ){
1372 if ( attacksLastDay[i].getNumAttacks() > 0 ){
1373 pieRawData[0][activeWormIndex] = new Integer(attacksLastDay[i].getNumAttacks());
1374 pieWormNames[activeWormIndex] = attacksLastDay[i].getName() + " " + attacksLastDay[i].getNumAttacks();
1375 activeWormIndex++;
1376 }
1377 }
1378 //create the datasets
1379 DefaultPieDataset pieExtracted = new DefaultPieDataset();
1380 for( int i = 0; i < pieRawData[0].length; i++ ){
1381 pieExtracted.setValue( pieWormNames[i], pieRawData[0][i] );
1382 }
1383 JFreeChart pieChart = ChartFactory.createPie3DChart("Attacks in Last 24 Hours", pieExtracted, true, false, false);
1384
1385 PiePlot pie = (PiePlot)pieChart.getPlot();
1386
1387 pie.setSectionLabelType(PiePlot.PERCENT_LABELS);
1388
1389 //output the pie chart for last 24 hours
1390 OutputStream fos3 = new BufferedOutputStream( new FileOutputStream( filename + "_today.jpg" ) );
1391 ChartUtilities.writeChartAsJPEG(fos3, pieChart, CHART_WIDTH, CHART_HEIGHT);
1392 fos3.close();
1393
1394 //count number of active worms in all attacks
1395 numActiveWormsToday = 0;
1396 for ( int i = 0; i < wormsTotal.length; i++ ){
1397 if ( wormsTotal[i].getNumAttacks() > 0 )
1398 numActiveWormsToday++;
1399 }
1400
1401 //now set up the raw data for the pie chart
1402 pieRawData = new Integer[1][ numActiveWormsToday ];
1403 pieWormNames = new String[ numActiveWormsToday ];
1404 activeWormIndex = 0;
1405 for ( int i = 0; i < wormsTotal.length; i++ ){
1406 if ( wormsTotal[i].getNumAttacks() > 0 ){
1407 pieRawData[0][activeWormIndex] = new Integer(wormsTotal[i].getNumAttacks());
1408 pieWormNames[activeWormIndex] = wormsTotal[i].getName() + " " + wormsTotal[i].getNumAttacks();
1409 activeWormIndex++;
1410 }
1411 }
1412
1413 //create the datasets
1414 pieExtracted = new DefaultPieDataset();
1415 for( int i = 0; i < pieRawData[0].length; i++ ){
1416 pieExtracted.setValue( pieWormNames[i], pieRawData[0][i] );
1417 }
1418 pieChart = ChartFactory.createPie3DChart("Total Attacks by Worm", pieExtracted, true, false, false);
1419
1420 pie = (PiePlot)pieChart.getPlot();
1421 pie.setSectionLabelType(PiePlot.PERCENT_LABELS);
1422
1423 //output the all attacks pie chart
1424 OutputStream fos4 = new BufferedOutputStream( new FileOutputStream( filename + "_allattacks.jpg" ) );
1425 ChartUtilities.writeChartAsJPEG(fos4, pieChart, CHART_WIDTH, CHART_HEIGHT);
1426 fos4.close();
1427 }
1428 catch( RuntimeException ex ){
1429 System.err.println( "Runtime ex making chart: " + ex.getMessage() );
1430 ex.printStackTrace();
1431 }
1432 catch( Exception ex ){
1433 System.err.println( "Normal ex making chart: " + ex.getMessage() );
1434 }
1435 catch ( java.lang.Error error ){
1436 System.err.println( "Unable to produce charts (no AWT available): " + error.getMessage() );
1437 }
1438 }
1439
1440 private static void writeReport( Template template, VelocityContext context, String fileExt, Object[] results, boolean reverseAlso ) throws Exception {
1441 template = Velocity.getTemplate( REPORT_TEMPLATE_NAME );
1442 context.put( "attacks", results );
1443
1444 Writer fw = new BufferedWriter( new FileWriter( BASE_FILENAME + fileExt + BASE_EXTENSION ) );
1445 template.merge( context, fw );
1446 fw.close();
1447
1448 if ( WRITE_GZIP ){
1449 try {
1450 writeGZIP( BASE_FILENAME + fileExt + BASE_EXTENSION, BASE_FILENAME + fileExt + BASE_EXTENSION + ".gz" );
1451 }
1452 catch ( Exception e ){
1453 if ( OUTPUT_LEVEL > 0 ) System.out.println( "Unable to write GZIP report: " + e.getMessage() );
1454 }
1455 }
1456
1457 if ( reverseAlso ){
1458 writeReport ( template, context, fileExt + "_desc", Sorter.reverseArray( results ), false );
1459 }
1460 }
1461 }