001: package snow.sys;
002:
003: import snow.utils.StreamGobbler;
004: import snow.utils.gui.Icons;
005: import snow.texteditor.SimpleDocument;
006: import snow.utils.SysUtils;
007: import java.awt.Font;
008: import java.awt.Insets;
009: import java.awt.event.*;
010: import java.awt.FlowLayout;
011: import java.util.concurrent.*;
012: import java.util.concurrent.Executors;
013: import java.net.InetAddress;
014: import java.awt.EventQueue;
015: import javax.swing.*;
016: import java.awt.BorderLayout;
017: import snow.sortabletable.*;
018: import java.util.*;
019: import java.text.SimpleDateFormat;
020: import java.io.*;
021:
022: /** Analyses Windows XP firewall log and give access to some utilities.
023: */
024: public final class NetExplorer {
025: final static SimpleDateFormat df = new SimpleDateFormat(
026: "yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
027:
028: String localhost = "localhost";
029:
030: // DNS Resolving...
031: //
032:
033: // Must be cached for efficiency across multiple calls => static cache.
034: // subtle: dhcp ips from yesterday may have today other names !
035: final static Map<String, String> hostForIP = new HashMap<String, String>();
036:
037: public static String getHostNameForIP(String ip) {
038: // no sense...
039: if (ip.length() == 0 || !Character.isDigit(ip.charAt(0)))
040: return ip;
041:
042: if (hostForIP.containsKey(ip))
043: return hostForIP.get(ip);
044:
045: try {
046: InetAddress inet = InetAddress.getByName(ip);
047:
048: if (inet.isMulticastAddress()) {
049: System.out.println("Is multicast: " + ip);
050: return ip; // don't resolve ! (??)
051: }
052:
053: String hn = inet.getHostName();
054: if (hn == null)
055: return ip;
056:
057: hostForIP.put(ip, hn);
058: return hn;
059: } catch (Exception e) {
060: e.printStackTrace();
061: return null;
062: }
063: }
064:
065: public NetExplorer(boolean standalone) {
066: JFrame f = new JFrame("Network analysis");
067: if (standalone)
068: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
069:
070: JTabbedPane tp = new JTabbedPane();
071: f.add(tp, BorderLayout.CENTER);
072:
073: f.setSize(900, 600);
074: f.setVisible(true);
075:
076: if (SysUtils.is_Windows_OS()) {
077: File fw = new File("c:/windows/pfirewall.log");
078: if (fw.exists()) {
079: tp
080: .addTab("Win Firewall",
081: createFWindowsFirewallTab(fw));
082: }
083: // tp.addTab("Windows Firewall log", new JLabel("file not found"));
084:
085: File fi = new File(System.getenv("ProgramFiles"),
086: "/Kerio/Personal Firewall/filter.log");
087: if (fi.exists()) {
088: tp.addTab("Kerio Firewall", KerioExplorer
089: .createKerioTab(fi));
090: }
091:
092: tp.addTab("Netstat -ao", createCommandPanel("netstat -ao"));
093: //SLOW! tp.addTab("Netstat -bov", createCommandPanel("netstat -bov"));
094: tp.addTab("Netstat -n", createCommandPanel("netstat -n"));
095: tp.addTab("Netstat -res",
096: createCommandPanel("netstat -res"));
097: tp.addTab("Netstat --help",
098: createCommandPanel("netstat --help"));
099: tp.addTab("ipconfig", createCommandPanel("ipconfig /all"));
100: tp.addTab("route print", createCommandPanel("route print"));
101: tp.addTab("net help", createCommandPanel("net help"));
102: tp
103: .addTab(
104: "mtrmgr",
105: createCommandPanel("type c:\\windows\\system32\\CCM\\Logs\\mtrmgr.log"));
106: } else {
107: // TODO: all all commands from chapter 13 suse admin book...
108:
109: tp.addTab("whois google.ch",
110: createCommandPanel("whois google.ch"));
111: tp.addTab("/etc/hosts",
112: createCommandPanel("cat /etc/hosts"));
113: tp.addTab("/etc/networks",
114: createCommandPanel("cat /etc/networks"));
115: tp.addTab("/etc/host.conf",
116: createCommandPanel("cat /etc/host.conf"));
117: tp.addTab("/etc/resolv.conf",
118: createCommandPanel("cat /etc/resolv.conf"));
119: tp
120: .addTab(
121: "/etc/sysconfig/network/ifcfg-*",
122: createCommandPanel("cat /etc/sysconfig/network/ifcfg-*"));
123: tp
124: .addTab(
125: "/etc/sysconfig/network/config",
126: createCommandPanel("cat /etc/sysconfig/network/config"));
127: tp
128: .addTab(
129: "/etc/sysconfig/network/dhcp",
130: createCommandPanel("cat /etc/sysconfig/network/dhcp"));
131: tp
132: .addTab(
133: "/etc/sysconfig/network/wireless",
134: createCommandPanel("cat /etc/sysconfig/network/wireless"));
135:
136: }
137: }
138:
139: /** With search tab (CTRL+F)
140: */
141: private static JPanel createCommandPanel(final String cmd) {
142: JPanel pan = new JPanel(new BorderLayout());
143: final JTextField tf = new JTextField(cmd, 25);
144: JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT, 3, 2));
145: pan.add(cp, BorderLayout.NORTH);
146: cp.add(new JLabel("Command: "));
147: cp.add(tf);
148: JButton refresh = new JButton("refresh");
149: refresh.setMargin(new Insets(0, 0, 0, 0));
150: cp.add(refresh);
151:
152: final SimpleDocument doc = new SimpleDocument();
153: final JTextPane ta = new JTextPane(doc);
154: final snow.texteditor.SearchPanel cop = new snow.texteditor.SearchPanel(
155: ta, doc);
156: ta.setFont(new Font("Lucida Sans Typewriter", Font.PLAIN, 10));
157: ta.setEditable(false);
158: doc.append("Please wait...");
159: pan.add(new JScrollPane(ta), BorderLayout.CENTER);
160:
161: pan.add(cop, BorderLayout.SOUTH);
162:
163: JButton search = new JButton(Icons.sharedSearch);
164: search.setMargin(new Insets(0, 0, 0, 0));
165: search.addActionListener(new ActionListener() {
166: public void actionPerformed(ActionEvent ae) {
167: cop.CTRL_F_pressed();
168: }
169: });
170: cp.add(search);
171:
172: final ActionListener act = new ActionListener() {
173: public void actionPerformed(ActionEvent ae) {
174: Thread t = new Thread() {
175: public void run() {
176:
177: List<String> cmd = SysUtils.is_Windows_OS() ? new ArrayList<String>(
178: Arrays.asList("cmd", "/c"))
179: : new ArrayList<String>(Arrays.asList(
180: "/bin/sh", "-c"));
181:
182: // no need to split...
183: cmd.add(tf.getText().trim());
184:
185: //System.out.println(""+cmd);
186: try {
187: // TODO : gobble as it come, for example for "netstat -baov" that is extremely slow !
188: doc.clearDocument();
189:
190: ProcessBuilder pb = new ProcessBuilder(cmd);
191: Process proc = pb.start();
192:
193: StreamGobbler sg2 = new StreamGobbler(
194: proc.getInputStream(),
195: doc
196: .createWriterForThisDocument(false),
197: "");
198: sg2.start();
199:
200: StreamGobbler sg4 = new StreamGobbler(proc
201: .getErrorStream(), doc
202: .createWriterForThisDocument(true),
203: "");
204: sg4.start();
205:
206: //
207: //doc.append( ProcessUtils.readWholeProcessStack(cmd, 120000L) );
208: sg2.join();
209: sg4.join();
210:
211: ta.setCaretPosition(0);
212: } catch (Exception e) {
213: ta.setText("" + e.getMessage());
214: }
215:
216: }
217: };
218: t.setPriority(Thread.MIN_PRIORITY);
219: t.start();
220:
221: }
222: };
223:
224: refresh.addActionListener(act);
225: tf.addActionListener(act);
226:
227: // initial action
228: act.actionPerformed(null);
229:
230: return pan;
231: }
232:
233: /** An analysis focuses on the various IPs found in the firewall log.
234: * TODO: add a stat function in the sortable table header context menu. (count, categorize, ...)
235: */
236: private JPanel createFWindowsFirewallTab(final File lf) {
237: // final boolean group = false;
238:
239: final JPanel f = new JPanel(new BorderLayout());
240:
241: final STableMod sm = new STableMod();
242: SortableTableModel stm = new SortableTableModel(sm);
243: JTable t = new JTable(stm);
244: stm.installGUI(t);
245:
246: f.add(new JScrollPane(t), BorderLayout.CENTER);
247:
248: MultiSearchPanel asp = new MultiSearchPanel("", null, stm);
249: f.add(asp, BorderLayout.NORTH);
250:
251: JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT, 3, 2));
252: f.add(cp, BorderLayout.SOUTH);
253:
254: final JButton upd = new JButton("update");
255: upd.setMargin(new Insets(0, 0, 0, 0));
256: cp.add(upd);
257:
258: final JButton resolveIPs = new JButton("resolve names");
259: resolveIPs.setMargin(new Insets(0, 0, 0, 0));
260: cp.add(resolveIPs);
261: resolveIPs.addActionListener(new ActionListener() {
262: public void actionPerformed(ActionEvent ae) {
263: resolveIPs.setEnabled(false);
264: resolveNamesTr(sm);
265: }
266: });
267:
268: final JComboBox cbTime = new JComboBox(new String[] {
269: "last 5 min", "last hour", "all" });
270: final JCheckBox groupSames = new JCheckBox(" group same", false);
271: cp.add(groupSames);
272:
273: //final JCheckBox cb5m = new JCheckBox("Last 5 minutes only", true);
274: cp.add(cbTime);
275:
276: final JLabel status = new JLabel("");
277: cp.add(status);
278:
279: final ActionListener updater = new ActionListener() {
280: public void actionPerformed(ActionEvent ae) {
281: upd.setEnabled(false);
282: cbTime.setEnabled(false);
283: resolveIPs.setEnabled(false);
284:
285: final int selTime = cbTime.getSelectedIndex();
286:
287: final Thread th = new Thread() {
288: public void run() {
289: final List<Entry> entries = new ArrayList<Entry>();
290: Map<String, Entry> quickID1 = new HashMap<String, Entry>();
291: try {
292: final BufferedReader fr = new BufferedReader(
293: new FileReader(lf));
294:
295: try {
296: localhost = InetAddress.getLocalHost()
297: .getHostAddress();
298: } catch (Exception e) {
299: e.printStackTrace();
300: }
301:
302: String line = null;
303: String lastLine = null;
304:
305: long count = 0;
306: long firstTime = -1;
307: long lastTime = -1;
308:
309: Set<String> ips1 = new TreeSet<String>();
310: Set<String> ips2 = new TreeSet<String>();
311: Set<String> ports1 = new TreeSet<String>();
312: Set<String> ports2 = new TreeSet<String>();
313:
314: Map<String, TreeSet<String>> portsForIp = new HashMap<String, TreeSet<String>>();
315: Map<String, TreeSet<String>> ipsForPort = new HashMap<String, TreeSet<String>>();
316:
317: while ((line = fr.readLine()) != null) {
318: //System.out.println(""+line);
319: if (line.startsWith("#")) {
320: System.out.println("" + line);
321: continue;
322: }
323:
324: line = line.trim();
325: if (line.length() == 0)
326: continue;
327:
328: count++;
329: if (count % 1000 == 0) {
330: status.setText(" " + count
331: + " lines read...");
332: }
333:
334: if (firstTime < 0) {
335: firstTime = df.parse(
336: line.substring(0, 19))
337: .getTime();
338: }
339:
340: // always add the new entry, but keep the old data
341: Entry ei = new Entry(line);
342:
343: long age = System.currentTimeMillis()
344: - ei.time;
345: if (age < 1000 * 3600 * 24 * 365
346: && age > 0) {
347: if (selTime == 0
348: && age > 1000 * 60 * 5)
349: continue; // because the entries are sorted
350: if (selTime == 1
351: && age > 1000 * 3600)
352: continue;
353: }
354:
355: if (groupSames.isSelected()) {
356:
357: Entry old = quickID1.get(ei
358: .uniqueID1());
359: if (old != null) {
360: entries.remove(old);
361: ei.count += old.count;
362: ei.size += old.size;
363: }
364: quickID1.put(ei.uniqueID1(), ei);
365: }
366:
367: entries.add(ei);
368:
369: //System.out.println(""+line.substring(20));
370: Scanner sc = new Scanner(line
371: .substring(20));
372: String type = sc.next(); // DROP | OPEN |
373: String prot = sc.next(); // UDP
374: String ip1 = sc.next(); // IP (local)
375: String ip2 = sc.next(); // IP (remote)
376: String port1 = sc.next(); // Port1 (local port?)
377: String port2 = sc.next(); // Port2
378:
379: if (!type.equalsIgnoreCase("OPEN"))
380: continue;
381:
382: ips1.add(ip1);
383: ips2.add(ip2);
384: ports1.add(port1); //LOCAL
385: ports2.add(port2);
386:
387: if (!ipsForPort.containsKey(port2))
388: ipsForPort.put(port2,
389: new TreeSet<String>());
390: ipsForPort.get(port2).add(ip2);
391:
392: if (!portsForIp.containsKey(ip2))
393: portsForIp.put(ip2,
394: new TreeSet<String>());
395: portsForIp.get(ip2).add(port2);
396:
397: lastLine = line;
398: }
399:
400: if (lastLine != null) {
401: lastTime = df.parse(
402: lastLine.substring(0, 19))
403: .getTime();
404: }
405:
406: System.out.println("\nDone. Time from "
407: + df.format(firstTime) + " to "
408: + df.format(lastTime));
409:
410: //System.out.println("IPS1: "+ips1);
411: //System.out.println("IPS2: "+ips2);
412: //Local ports:
413: //System.out.println("Ports1: "+ports1.size()+" differents");
414:
415: System.out.println("\nPorts2: " + ports2);
416:
417: System.out.println("\nIPs for port: "
418: + ipsForPort);
419: System.out.println("\nports for ip: "
420: + portsForIp);
421:
422: fr.close();
423:
424: System.out.println("" + count
425: + " log lines");
426:
427: EventQueue.invokeLater(new Runnable() {
428: public void run() {
429: sm.setEntries(entries);
430:
431: /* if(cbTime.isSelected())
432: {
433: keepOnlyLast5Minutes(sm);
434: }*/
435:
436: }
437: });
438:
439: f.updateUI();
440: } catch (Exception e) {
441: f.add(
442: new JLabel("ERROR: "
443: + e.getMessage()),
444: BorderLayout.CENTER);
445: e.printStackTrace();
446: } finally {
447: quickID1.clear();
448: status.setText(" " + entries.size()
449: + " entries");
450: entries.clear();
451: upd.setEnabled(true);
452: cbTime.setEnabled(true);
453: resolveIPs.setEnabled(true);
454: }
455: }
456: };
457: th.start();
458: }
459: };
460:
461: cbTime.addItemListener(new ItemListener() {
462:
463: public final void itemStateChanged(final ItemEvent e) {
464:
465: switch (cbTime.getSelectedIndex()) {
466: case 0:
467: keepOnlyLastXXMinutes(sm, 5);
468: break;
469: case 1:
470: keepOnlyLastXXMinutes(sm, 60);
471: break;
472: }
473: updater.actionPerformed(null);
474: }
475: });
476:
477: upd.addActionListener(updater);
478: groupSames.addActionListener(updater);
479:
480: // initial call
481: updater.actionPerformed(null);
482:
483: return f;
484: }
485:
486: private void keepOnlyLastXXMinutes(final STableMod sm, final int m) {
487: sm.keepOnlyLastXXMinutes(m);
488: }
489:
490: private void resolveNamesTr(final STableMod sm) {
491: Thread tr = new Thread() {
492: public void run() {
493: sm.resolveIPs();
494: }
495: };
496: tr.start();
497:
498: }
499:
500: /*
501:
502: public Entry getWithID1(final List<Entry> entries, String id1)
503: {
504: for(final Entry ei : entries)
505: {
506: if(ei.uniqueID1().equals(id1)) return ei;
507: }
508: return null;
509: }*/
510:
511: class STableMod extends FineGrainTableModel {
512: final private List<Entry> entries = new ArrayList<Entry>();
513:
514: public final Object getValueAt(final int row, final int col) {
515:
516: Entry ei = null;
517: synchronized (entries) {
518: if (row < 0 || row > entries.size())
519: return "bad row " + row;
520: ei = entries.get(row);
521: }
522:
523: if (col == 0)
524: return df.format(ei.time);
525: if (col == 1)
526: return ei.type;
527: if (col == 2)
528: return ei.count;
529: if (col == 3)
530: return ei.size;
531: if (col == 4)
532: return ei.ip1 + " : " + ei.port1;
533: if (col == 5)
534: return ei.ip2 + " : " + ei.port2;
535: if (col == 6)
536: return ei.prot;
537: if (col == 7)
538: return ei.rest;
539:
540: return "?";
541: }
542:
543: public void setEntries(final List<Entry> ne) {
544: this .fireTableModelWillChange();
545: synchronized (entries) {
546: entries.clear();
547: entries.addAll(ne);
548: }
549: this .fireTableDataChanged();
550: this .fireTableModelHasChanged();
551: }
552:
553: public void keepOnlyLastXXMinutes(int m) {
554: this .fireTableModelWillChange();
555: int rem = 0;
556: synchronized (entries) {
557: for (int i = entries.size() - 1; i >= 0; i--) {
558: if (System.currentTimeMillis()
559: - entries.get(i).time > m * 1000 * 60) {
560: entries.remove(i);
561: rem++;
562: }
563: }
564: }
565: System.out.println("removed " + rem + " entries");
566: this .fireTableDataChanged();
567: this .fireTableModelHasChanged();
568:
569: }
570:
571: /** Retrieved in parallel. Must be called in a userthread. may take minutes !
572: * 90 % have response within millisecs !
573: * 10% remains unresolved after several seconds timeout !!
574: */
575: public void resolveIPs() {
576: final ExecutorService executorService = Executors
577: .newFixedThreadPool(64);
578: System.out.println("Resolving " + entries.size() + " ips");
579:
580: List<Entry> allEntriesCopy = new ArrayList<Entry>(entries);
581: for (final Entry ei : allEntriesCopy) {
582: // SOME VERY SLOW => must be threaded...
583: executorService.submit(new Runnable() {
584: public void run() {
585: //
586: if (ei.ip1 != null && ei.ip1.length() > 0) {
587: if (Character.isDigit(ei.ip1.charAt(0))) {
588: ei.ip1 = getHostNameForIP(ei.ip1);
589: //System.out.println(""+ei.ip1);
590: }
591: }
592:
593: if (ei.ip2 != null && ei.ip2.length() > 0) {
594: if (Character.isDigit(ei.ip2.charAt(0))) {
595: ei.ip2 = getHostNameForIP(ei.ip2);
596: //System.out.println(""+ei.ip2);
597: }
598: }
599: }
600: });
601: }
602:
603: System.out.println("Waiting for IP resolve completion");
604: try {
605: executorService.awaitTermination(10, TimeUnit.MINUTES);
606: } catch (Exception e) {
607: e.printStackTrace();
608: }
609: System.out.println("Done.");
610: }
611:
612: public final int getRowCount() {
613: synchronized (entries) {
614: return entries.size();
615: }
616:
617: }
618:
619: public final int getColumnCount() {
620: return 8;
621: }
622:
623: final private String[] COLUMN_NAMES = new String[] { "time",
624: "type", "calls", "bytes", "src", "dest", "prot",
625: "last entry details" };
626:
627: int[] COLUMN_PREFERED_SIZES = new int[] { 15, 8, 5, 8, 20, 20,
628: 5, 25 };
629:
630: @Override
631: public int getPreferredColumnWidth(int column) {
632: if (column >= 0 && column < COLUMN_PREFERED_SIZES.length)
633: return COLUMN_PREFERED_SIZES[column];
634: return -1;
635: }
636:
637: @Override
638: public String getColumnName(int col) {
639: return COLUMN_NAMES[col];
640: }
641:
642: }
643:
644: /* "c:/windows/pfirewall.log" File format:
645:
646: #Version: 1.5
647: #Software: Microsoft Windows Firewall
648: #Time Format: Local
649: #Fields: date time action protocol src-ip dst-ip src-port dst-port size tcpflags tcpsyn tcpack tcpwin icmptype icmpcode info path
650:
651: */
652: /** = a line in the firewall log. */
653: class Entry {
654: long count = 1;
655: String type, prot, ip1, ip2, port1, port2, rest;
656: long time;
657:
658: long size = 0;
659:
660: public Entry(final String line) throws Exception {
661: SimpleDateFormat df = new SimpleDateFormat(
662: "yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
663: try {
664: time = df.parse(line.substring(0, 19).trim()).getTime();
665: } catch (Exception e) {
666: System.out.println("Can't parse time "
667: + line.substring(0, 19));
668: time = System.currentTimeMillis();
669: }
670:
671: Scanner sc = new Scanner(line.substring(20));
672: type = sc.next(); // DROP | OPEN |
673: prot = sc.next(); // UDP
674:
675: ip1 = sc.next(); // IP (local)
676:
677: if (ip1.equals(localhost)) {
678: ip1 = "localhost";
679: } else {
680: //if(resolveNames) ip1 = getHostNameForIP(ip1);
681: }
682:
683: ip2 = sc.next(); // IP (remote)
684:
685: if (ip2.equals(localhost)) {
686: ip2 = "localhost";
687: } else {
688: //if(resolveNames) ip2 = getHostNameForIP(ip2);
689: }
690:
691: port1 = sc.next(); // Port1 (local port?)
692: port2 = sc.next(); // Port2
693: String ss = sc.next();
694: if (!ss.equals("-")) {
695: size = Long.parseLong(ss);
696: }
697:
698: // NEXT: tcpflags tcpsyn tcpack tcpwin icmptype icmpcode info path
699:
700: rest = sc.nextLine();
701: }
702:
703: public String uniqueID1() {
704: return type + ip2 + ":" + port2 + " (" + prot + ")";
705: }
706: }
707:
708: public static void main(String[] args) throws Exception {
709: System.out.println("" + df.format(new Date()));
710: System.out.println("" + df.parse("2007-12-13 09:18:43"));
711: EventQueue.invokeLater(new Runnable() {
712: public void run() {
713: new NetExplorer(true);
714: }
715: });
716:
717: }
718:
719: }
|