, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ipscan/ext/retroguard/Readme.txt b/ipscan/ext/retroguard/Readme.txt
new file mode 100755
index 00000000..106ec403
--- /dev/null
+++ b/ipscan/ext/retroguard/Readme.txt
@@ -0,0 +1,58 @@
+
+ ** RETROGUARD(tm) BYTECODE OBFUSCATOR **
+
+-- RetroGuard v2.0.5, released 25th August 2005 --
+
+Thank you for downloading this release of the RetroGuard
+bytecode obfuscator. If you have questions about using RetroGuard,
+or wish to report a bug to be fixed for the next release,
+please contact web@retrologic.com
+
+
+This software is distributed under a "dual license" model:
+
+ - GNU GPL for non-commercial projects and for commercial trial integration;
+
+ - a subscription-based commercial license for commercial projects.
+
+In summary, if your project is commercial then RetroGuard is commercial;
+if your project is free software then RetroGuard is free software.
+
+To obtain a license agreement for commercial use of this software,
+please visit http://www.retrologic.com/retroguard-main.html or contact
+Retrologic Systems at web@retrologic.com
+
+
+Included in this release are the following files and directories:
+
+Readme.txt : This file.
+
+GPL.txt : For non-commercial projects, RetroGuard is distributed
+ under the GNU General Public License.
+-OR-
+License.txt : For commercial projects, RetroGuard is distributed under
+ a subscription-based commercial license.
+
+retroguard.jar : A JAR (Java ARchive) file containing the classes that
+ make up the RetroGuard Bytecode Obfuscator.
+
+src-dist.tar.gz: RetroGuard source files (GPL, non-commercial release only).
+
+
+The latest documentation for RetroGuard can be found online at:
+
+ http://www.retrologic.com/retroguard-docs.html
+
+The change history of the software can be found at:
+
+ http://www.retrologic.com/retroguard-changes.html
+
+Answers to common questions about obfuscation and RetroGuard can be found at:
+
+ http://www.retrologic.com/retroguard-faq.html
+
+
+If you have any questions please contact:
+
+ web@retrologic.com
+
diff --git a/ipscan/ext/retroguard/retroguard.conf b/ipscan/ext/retroguard/retroguard.conf
new file mode 100755
index 00000000..e112fd68
--- /dev/null
+++ b/ipscan/ext/retroguard/retroguard.conf
@@ -0,0 +1,13 @@
+.option Application
+.option MapClassString
+
+.method;native ** * and_class
+
+.class net/azib/ipscan/feeders/*Feeder public
+.class net/azib/ipscan/fetchers/*Fetcher public
+.class net/azib/ipscan/exporters/*Exporter public
+.class net/azib/ipscan/**/*Exception
+.class net/azib/ipscan/config/GlobalConfig public
+.class net/azib/ipscan/resources/*
+
+#.class org/eclipse/swt/**/OS
diff --git a/ipscan/ext/retroguard/retroguard.jar b/ipscan/ext/retroguard/retroguard.jar
new file mode 100755
index 00000000..98f79f74
Binary files /dev/null and b/ipscan/ext/retroguard/retroguard.jar differ
diff --git a/ipscan/ext/rocksaw/LICENSE b/ipscan/ext/rocksaw/LICENSE
new file mode 100755
index 00000000..d6456956
--- /dev/null
+++ b/ipscan/ext/rocksaw/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/ipscan/ext/rocksaw/NOTICE b/ipscan/ext/rocksaw/NOTICE
new file mode 100755
index 00000000..3ee836b9
--- /dev/null
+++ b/ipscan/ext/rocksaw/NOTICE
@@ -0,0 +1,2 @@
+This product includes software developed by
+Daniel F. Savarese (http://www.savarese.org/).
diff --git a/ipscan/ext/rocksaw/README b/ipscan/ext/rocksaw/README
new file mode 100755
index 00000000..74666927
--- /dev/null
+++ b/ipscan/ext/rocksaw/README
@@ -0,0 +1,161 @@
+$Id: README 6877 2006-04-14 00:56:38Z dfs $
+
+=====
+ABOUT
+=====
+
+RockSaw is a simple API for performing network I/O with raw
+sockets in Java.
+
+
+============
+REQUIREMENTS
+============
+
+The 0.4.5 version of RockSaw has been compiled and tested on Linux,
+Win32 with Cygwin/MinGW/Winsock or Visual C++, and Solaris 8/9/10. It
+should compile on other POSIX systems using the GNU tool chain.
+
+The Ping.java example program requires VServ TCP/IP version 0.7.x
+(http://www.savarese.org/software/vserv-tcpip.html) or later to
+compile because it uses the ICMPEchoPacket class.
+
+librocksaw.so in the binary distribution is pre-compiled for Linux
+i386 and rocksaw.dll is pre-compiled for Win32 using Visual C++.
+
+J2SE 1.3 or greater is required to compile because of the use of
+Runtime.addShutdownHook on Win32. If you have a need to support
+J2SE 1.2, we can find another solution for calling WSACleanup on
+Win32 platforms.
+
+Winsock2 (ws2_32.dll) is required on Win32 platforms.
+
+=========
+COMPILING
+=========
+
+You must have the JDK_HOME environment variable set and pointing to
+the directory where the Java Development Kit is installed. Otherwise,
+the JNI headers will not be found. RockSaw has been tested primarily
+with J2SE 5 JDK 1.5, but it compiles with the J2SE 2 1.3, and 1.4
+SDKs. Only the Ping.java example program requires J2SE 5.
+
+The source code requires Apache Ant (http://ant.apache.org/), GNU
+make, and GCC to compile. On Windows, you must either have Visual C++
+installed or have Cygwin and MinGW installed with support for the
+Cygwin GCC -mno-cygwin option (http://www.cygwin.com/). Autoconf
+support may be added somewhere down the line if warranted.
+
+The command
+
+ ant -projecthelp
+
+will list all build targets in build.xml. There are very few files
+in the source tree:
+
+ src/java Java source code
+ src/jni The C JNI source and Makefile
+
+When you compile the source with
+
+ ant jar
+
+the C source will also be built by execing a call to gmake in src/jni.
+A jar file and shared library will be created and placed in the lib/
+directory. They will be called:
+
+ rocksaw-version.jar
+ librocksaw.so
+
+Currently there is no version number for the shared library. On
+Win32 systems, the shared library will be called:
+
+ rocksaw.dll
+
+The version of Winsock linked to on Windows can be changed with
+the jni.winsock property. By default, it is set to ws2_32,
+which is Winsock2.
+
+J2SE 1.4/1.3/1.2
+------------------
+
+You may have to override the javac.args, javac.source, and
+javac.target properties because the -Xlint:unchecked parameter
+is only valid for J2SE 5. For example, to compile for
+J2SE 1.3:
+
+ ant -Djavac.args="" -Djavac.source=1.3 -Djavac.target=1.3 jar
+
+Win32: CYGWIN
+-------------
+
+When compiling with cygwin, you may need to redefine the jni.make
+property because it is set to "gmake" by default. Cygwin doesn't
+include a gmake executable for GNU Make. It is named only make.
+Therefore, you may have to use the following command line:
+
+ ant -Djni.make=make jar
+
+Alternatively, you can edit the build.properties file.
+
+Win32: Visual C++
+-----------------
+
+To compile using Visual C++, you have to override the default
+compiler command, make command, and makefile properties:
+
+ jni.cc
+ jni.make
+ jni.makefile
+
+You can override these on the command line or in build.properties.
+For example, to compile using Visual C++, you would use the
+following command:
+
+ ant -Djni.cc=cl -Djni.make=nmake -Djni.makefile=Makefile.win32 jar
+
+Make sure your JDK_HOME environment variable is set and that
+you've run either the vcvars.bat or vsvars32.bat command
+(depending on the version of Visual C++ you're using) to set
+your paths for the command line tools.
+
+Ping Example
+------------
+
+The example Ping program can be compiled separately with the
+example.compile target, but requires VServ TCP/IP to compile. The
+classpath.vserv-tcpip property in build.properties must point to the
+VServ TCP/IP jar, which by default is expected to be present in the
+lib/ directory. Then you can compile the program with:
+
+ ant example.compile
+
+
+=========
+LICENSING
+=========
+
+RockSaw is Copyright 2004-2005 by Daniel F. Savarese and licensed
+under the Apache License 2.0 as described in the files:
+
+ LICENSE
+ NOTICE
+
+
+=====
+NOTES
+=====
+
+On most operating systems, you must have root access or administrative
+privileges to use raw sockets.
+
+The API is at a fairly crude stage of development (i.e., the minimum
+required to do the job it needed to do), but is functional. Don't
+hesitate to submit patches that enhance the functionality.
+
+
+=======
+CONTACT
+=======
+
+To contact me see http://www.savarese.org/contact.html
diff --git a/ipscan/ext/rocksaw/lib/librocksaw.so b/ipscan/ext/rocksaw/lib/librocksaw.so
new file mode 100755
index 00000000..e8c0fc04
Binary files /dev/null and b/ipscan/ext/rocksaw/lib/librocksaw.so differ
diff --git a/ipscan/ext/rocksaw/lib/rocksaw.dll b/ipscan/ext/rocksaw/lib/rocksaw.dll
new file mode 100755
index 00000000..d023bf68
Binary files /dev/null and b/ipscan/ext/rocksaw/lib/rocksaw.dll differ
diff --git a/ipscan/ext/rocksaw/lib/tmp.jar b/ipscan/ext/rocksaw/lib/tmp.jar
new file mode 100755
index 00000000..a2187642
Binary files /dev/null and b/ipscan/ext/rocksaw/lib/tmp.jar differ
diff --git a/ipscan/ext/rocksaw/src/java/org/savarese/rocksaw/net/RawSocket.java b/ipscan/ext/rocksaw/src/java/org/savarese/rocksaw/net/RawSocket.java
new file mode 100755
index 00000000..b90d40a1
--- /dev/null
+++ b/ipscan/ext/rocksaw/src/java/org/savarese/rocksaw/net/RawSocket.java
@@ -0,0 +1,615 @@
+/*
+ * $Id: RawSocket.java 5979 2005-11-09 18:11:02Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.savarese.rocksaw.net;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.SocketException;
+
+/**
+ * The RawSocket class provides a strictly utilitarian API for
+ * performing I/O with raw sockets. The API is currently crude, but
+ * functional. It should evolve into something more feature-complete
+ * and flexible.
+ *
+ * We throw java.io.InterruptedIOException when read/write
+ * operations time out because java.net.SocketTimeoutException is
+ * present only in J2SE 1.4 and up. By using InterruptedIOException,
+ * we allow programmers to use the software with J2SE 1.2 and 1.3.
+ *
+ * Socket options should not be set until the socket has been
+ * opened.
+ *
+ * Important! On most operating systems, you must have root
+ * access or administrative privileges to use raw sockets.
+ *
+ * @author Daniel F. Savarese
+ */
+
+public class RawSocket {
+
+ /**
+ * A protocol family constant for {@link #open} indicating IPv4.
+ * This should be moved to another class.
+ */
+ public static final int PF_INET = 2;
+
+ /**
+ * A protocol family constant for {@link #open} indicating IPv6.
+ * This should be moved to another class.
+ */
+ public static final int PF_INET6 = 10 ;
+
+ /**
+ * Initializes any system resources used by the RockSaw library.
+ * Really, all it does is call WSAStartup on Win32. It may be
+ * called multiple times (only the first call has any effect), but
+ * each call must be matched with a corresponding call to
+ * RockSawShutdown().
+ *
+ * @return zero if successful, otherwise some non-zero value.
+ */
+ private native static int __RockSawStartup();
+
+ /**
+ * Deallocates any system resources used by the RockSaw library.
+ * Really, all it does is call WSACleanup on Win32.
+ */
+ private native static void __RockSawShutdown();
+
+ static {
+ System.loadLibrary("rocksaw");
+ if(__RockSawStartup() != 0)
+ throw new UnsatisfiedLinkError(__getErrorMessage());
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() {
+ __RockSawShutdown();
+ }
+ });
+ }
+
+ private static final int __UNDEFINED = -1;
+
+ /**
+ * TimeVal is a convenience class for tracking select timeouts.
+ * Should we implement select outside of this class, we can make
+ * this a public top-level class with proper getters and setters.
+ */
+ private static final class TimeVal {
+ int seconds;
+ int microseconds;
+
+ TimeVal() {
+ seconds = microseconds = 0;
+ }
+
+ void setInMilliseconds(int milliseconds) {
+ seconds = milliseconds / 1000;
+
+ if(seconds > 0)
+ milliseconds-=(seconds*1000);
+
+ microseconds = milliseconds * 1000;
+ }
+
+ int getInMilliseconds() {
+ return (seconds * 1000 + microseconds / 1000);
+ }
+
+ boolean isZero() {
+ return (seconds == 0 && microseconds == 0);
+ }
+ }
+
+ private int __socket;
+ private int __family;
+ private TimeVal __stimeout, __rtimeout;
+ private boolean __useSelectTimeout;
+
+ /**
+ * Creates an uninitialized socket. If the {@code os.name} system
+ * property starts with the string "SunOS",
+ * {@link #setUseSelectTimeout} is set to true (because Solaris does not
+ * support socket send and receive timeouts), otherwise it is false
+ * by default.
+ */
+ public RawSocket() {
+ __socket = __UNDEFINED;
+ __family = __UNDEFINED;
+ __stimeout = new TimeVal();
+ __rtimeout = new TimeVal();
+
+ String os = System.getProperty("os.name");
+
+ if(os != null && os.startsWith("SunOS"))
+ setUseSelectTimeout(true);
+ else
+ setUseSelectTimeout(false);
+ }
+
+
+ /**
+ * Tests if the socket has been opened.
+ *
+ * @return True if the socket is open.
+ */
+ public boolean isOpen() {
+ return (__socket > 0);
+ }
+
+
+ /**
+ * Writes a system error message into a StringBuffer.
+ * The message is appended to the supplied StringBuffer argument.
+ * This is not a thread safe call because it relies on the value
+ * of errno, which may be altered at any time by another thread.
+ *
+ * @param buffer The buffer in which to store the error message.
+ */
+ private native static void __getErrorMessage(StringBuffer buffer);
+
+ private static String __getErrorMessage() {
+ StringBuffer buf = new StringBuffer();
+ __getErrorMessage(buf);
+ return buf.toString();
+ }
+
+ private static void __throwIOException() throws IOException {
+ throw new IOException(__getErrorMessage());
+ }
+
+ private static void __throwSocketException() throws SocketException {
+ throw new SocketException(__getErrorMessage());
+ }
+
+ private static void __throwInterruptedIOException()
+ throws InterruptedIOException
+ {
+ throw new InterruptedIOException(__getErrorMessage());
+ }
+
+ private native static int __socket(int protocolFamily, int protocol);
+
+
+ /**
+ * Returns the protocol number corresponding to the given protocol name.
+ * For example, {@code getProtocolByName("icmp");} should return 1 and
+ * {@code getProtocolByName("udp");} should return 17. The native system
+ * protocol database is used to look up the protocol numbers.
+ *
+ * This method really belongs in another class, probably in
+ * vserv-tcpip, but is currently included here as a convenience. It
+ * may be moved elsewhere in the 1.0 release API.
+ *
+ * @return The protocol number corresponding to the given protocol name.
+ * If the protocol name cannot be found, returns a negative value.
+ */
+ public native static final int getProtocolByName(String name);
+
+
+ /**
+ * Opens a raw socket.
+ *
+ * @param protocolFamily The protocol family of the socket (e.g.,
+ * {@link #PF_INET} or {@link #PF_INET6}).
+ * @param protocol The protocol within the protocol family. {@link
+ * #getProtocolByName} should be used to obtain protocol numbers.
+ * @exception IllegalStateException If the object instance is
+ * already open.
+ * @exception IOException If an error occurs while opening the socket.
+ */
+ public void open(int protocolFamily, int protocol)
+ throws IllegalStateException, IOException
+ {
+ if(isOpen())
+ throw new IllegalStateException();
+ __socket = __socket(protocolFamily, protocol);
+
+ if(__socket < 0) {
+ __socket = __UNDEFINED;
+ __throwIOException();
+ }
+
+ __family = protocolFamily;
+ }
+
+ private native static int __close(int socket);
+
+ /**
+ * Closes the socket.
+ *
+ * @exception IOException If an I/O error occurs.
+ */
+ public void close() throws IOException {
+ int result = __close(__socket);
+ __socket = __UNDEFINED;
+ __family = __UNDEFINED;
+
+ if(result != 0)
+ __throwIOException();
+ }
+
+ /**
+ * @return True if errno equals EAGAIN or EWOULDBLOCK.
+ */
+ private native boolean __isErrorEAGAIN();
+
+ private native static int __setIPHeaderInclude(int socket, boolean on);
+
+ /**
+ * @return A negative value if an error occurs, else zero for false and
+ * a positive value for true.
+ */
+ private native static int __getIPHeaderInclude(int socket);
+
+
+ /**
+ * Sets or unsets the IP_HDRINCL socket option. Setting this option
+ * causes IPv4 packet writes to expect the entire IP packet,
+ * starting from the header. The default behavior is to only expect
+ * the data payload. This option is valid only for IPv4 sockets.
+ *
+ * @param on True if headers should be included, false if not.
+ * @exception SocketException If the option setting could not be altered.
+ */
+ public void setIPHeaderInclude(boolean on) throws SocketException {
+ int result = __setIPHeaderInclude(__socket, on);
+
+ if(result < 0)
+ __throwSocketException();
+ }
+
+
+ /**
+ * Retrieves the current setting of the IP_HDRINCL option.
+ *
+ * @return True if the IP_HDRINCL option is set, false if not.
+ * @exception SocketException If the option value could not be retrieved.
+ */
+ public boolean getIPHeaderInclude() throws SocketException {
+ int result = __getIPHeaderInclude(__socket);
+
+ if(result < 0)
+ __throwSocketException();
+
+ return (result > 0);
+ }
+
+
+ private native static int __setSendBufferSize(int socket, int size);
+
+ /**
+ * Sets the send buffer size (SO_SNDBUF).
+ *
+ * @param size The size of the send buffer.
+ * @exception SocketException If the option value could not be set.
+ */
+ public void setSendBufferSize(int size) throws SocketException {
+ int result = __setSendBufferSize(__socket, size);
+
+ if(result < 0)
+ __throwSocketException();
+ }
+
+
+ private native static int __getSendBufferSize(int __socket);
+
+ /**
+ * Retrieves the send buffer size (SO_SNDBUF).
+ *
+ * @return The size of the send buffer.
+ * @exception SocketException If the option value could not be retrieved.
+ */
+ public int getSendBufferSize() throws SocketException {
+ int result = __getSendBufferSize(__socket);
+
+ if(result < 0)
+ __throwSocketException();
+
+ return result;
+ }
+
+
+ private native static int __setReceiveBufferSize(int socket, int size);
+
+ /**
+ * Sets the receive buffer size (SO_RCVBUF).
+ *
+ * @param size The size of the receive buffer.
+ * @exception SocketException If the option value could not be set.
+ */
+ public void setReceiveBufferSize(int size) throws SocketException {
+ int result = __setReceiveBufferSize(__socket, size);
+
+ if(result < 0)
+ __throwSocketException();
+ }
+
+
+ private native static int __getReceiveBufferSize(int socket);
+
+ /**
+ * Retrieves the receive buffer size (SO_RCVBUF).
+ *
+ * @return The size of the receive buffer.
+ * @exception SocketException If the option value could not be retrieved.
+ */
+ public int getReceiveBufferSize() throws SocketException {
+ int result = __getReceiveBufferSize(__socket);
+
+ if(result < 0)
+ __throwSocketException();
+
+ return result;
+ }
+
+ /**
+ * @return Zero if the socket is ready for I/O, negative value if
+ * timed out or error occurred.
+ */
+ private native static
+ int __select(int socket, boolean read, int seconds, int microseconds);
+
+
+ /**
+ * Sets whether or not socket send/receive timeouts should be
+ * emulated by using the POSIX {@code select} function. Not all
+ * platforms support socket send/receive timeouts and this method
+ * provides a means to reproduce the same effect.
+ *
+ * This method is not guaranteed to be retained in the 1.0 API. We
+ * may find a better way to provide support for read/write timeouts
+ * on all platforms. Technically, it's better to simply use
+ * non-blocking I/O rather than rely on socket timeouts, but we have
+ * yet to add a non-blocking I/O interface.
+ *
+ * @param useSelect true if {@code select} should be used to
+ * implement timeouts, false if not.
+ */
+ public void setUseSelectTimeout(boolean useSelect) {
+ __useSelectTimeout = useSelect;
+ }
+
+
+ /**
+ * Determines whether or not socket send/receive timeouts are
+ * emulated by using the POSIX {@code select} system function.
+ * Not all platforms support socket send/receive timeouts. The
+ * default value is false except for platforms where the {@code
+ * os.name} property starts with the string "SunOS".
+ *
+ * @return True if send/receive timeouts are emulated with select,
+ * false if not.
+ */
+ public boolean getUseSelectTimeout() {
+ return __useSelectTimeout;
+ }
+
+
+ private native static int __setSendTimeout(int socket, int timeout);
+
+ /**
+ * Sets the send timeout (SO_SNDTIMEO). A timeout of zero indicates
+ * an infinite timeout. A negative timeout is undefined.
+ *
+ * @param timeout The send timeout in milliseconds.
+ * @exception SocketException If the option value could not be set.
+ */
+ public void setSendTimeout(int timeout) throws SocketException {
+ __stimeout.setInMilliseconds(timeout);
+
+ if(!getUseSelectTimeout()) {
+ int result =
+ __setSendTimeout(__socket, timeout);
+
+ if(result < 0)
+ __throwSocketException();
+ }
+ }
+
+
+ private native static int __getSendTimeout(int socket);
+
+ /**
+ * Retrieves the send timeout (SO_SNDTIMEO).
+ *
+ * @return The send timeout in milliseconds.
+ * @exception SocketException If the option value could not be set.
+ */
+ public int getSendTimeout() throws SocketException {
+ int result;
+
+ if(getUseSelectTimeout())
+ result = __stimeout.getInMilliseconds();
+ else {
+ result = __getSendTimeout(__socket);
+
+ if(result < 0)
+ __throwSocketException();
+ }
+
+ return result;
+ }
+
+
+ private native static int __setReceiveTimeout(int socket, int timeout);
+
+ /**
+ * Sets the receive timeout (SO_RCVTIMEO). A timeout of zero indicates
+ * an infinite timeout. A negative timeout is undefined.
+ *
+ * @param timeout The receive timeout in milliseconds.
+ * @exception SocketException If the option value could not be set.
+ */
+ public void setReceiveTimeout(int timeout) throws SocketException {
+ __rtimeout.setInMilliseconds(timeout);
+
+ if(!getUseSelectTimeout()) {
+ int result =
+ __setReceiveTimeout(__socket, timeout);
+
+ if(result < 0)
+ __throwSocketException();
+ }
+ }
+
+
+ private native static int __getReceiveTimeout(int socket);
+
+ /**
+ * Retrieves the receive timeout (SO_RCVTIMEO).
+ *
+ * @return The receive timeout in milliseconds.
+ * @exception SocketException If the option value could not be set.
+ */
+ public int getReceiveTimeout() throws SocketException {
+ int result;
+
+ if(getUseSelectTimeout())
+ result = __rtimeout.getInMilliseconds();
+ else {
+ result = __getReceiveTimeout(__socket);
+
+ if(result < 0)
+ __throwSocketException();
+ }
+
+ return result;
+ }
+
+
+ private native static int __recvfrom(int socket, byte[] data, int offset,
+ int length, int family, byte[] address);
+
+ /**
+ * Reads packet data from the socket. IPv4 ({@link #PF_INET})
+ * packets will be delivered in their entirety, including the IP
+ * header. IPv6 ({@link #PF_INET6}) packet data will not include
+ * the IPV6 header.
+ *
+ * @param address The source to read from.
+ * @param data The buffer in which to store the packet data.
+ * @param offset The offset into the buffer where the data should
+ * be stored.
+ * @param length The number of bytes to read.
+ * @exception IllegalArgumentException If the offset or lengths are invalid.
+ * @exception IOException If an I/O error occurs.
+ * @exception InterruptedIOException If the read operation times out.
+ * @return The number of bytes read.
+ */
+ public int read(InetAddress address, byte[] data, int offset,
+ int length)
+ throws IllegalArgumentException, IOException, InterruptedIOException
+ {
+ if(offset < 0 || length < 0 || length > data.length - offset)
+ throw new IllegalArgumentException("Invalid offset or length.");
+
+ int result = 0;
+
+ if(getUseSelectTimeout() && !__rtimeout.isZero())
+ result =
+ __select(__socket, true, __rtimeout.seconds, __rtimeout.microseconds);
+ //System.out.println("s" + System.currentTimeMillis());
+
+ // ??? setsockopt(ssock, IPPROTO_IP, IP_HDRINCL, (char *)&bOpt, sizeof(bOpt));
+
+ if(result == 0)
+ result =
+ __recvfrom(__socket, data, offset, length, __family,
+ address.getAddress());
+ //System.out.println("e" + System.currentTimeMillis());
+
+ if(result < 0) {
+ if(__isErrorEAGAIN())
+ __throwInterruptedIOException();
+ else
+ __throwIOException();
+ }
+
+ return result;
+ }
+
+
+ /** Same as {@code read(address, data, 0, data.length);} */
+ public int read(InetAddress address, byte[] data)
+ throws IOException, InterruptedIOException
+ {
+ return read(address, data, 0, data.length);
+ }
+
+
+ private native static int __sendto(int socket, byte[] data, int offset,
+ int length, int family, byte[] address);
+
+
+
+ /**
+ * Writes packet data to the socket. The data should not include
+ * the IP header. IPv4 ({@link #PF_INET}) sockets may set the
+ * IP_HDRINCL option with {@link #setIPHeaderInclude}, in which case the
+ * packet data should include the IP header.
+ *
+ * @param address The destination to write to.
+ * @param data The buffer from which to copy the packet data.
+ * @param offset The offset into the buffer where the data starts.
+ * @param length The number of bytes to write.
+ * @exception IllegalArgumentException If the offset or lengths are invalid.
+ * @exception IOException If an I/O error occurs.
+ * @exception InterruptedIOException If the write operation times out.
+ * @return The number of bytes written.
+ */
+ public int write(InetAddress address, byte[] data, int offset, int length)
+ throws IllegalArgumentException, IOException, InterruptedIOException
+ {
+ if(offset < 0 || length < 0 || length > data.length - offset)
+ throw new IllegalArgumentException("Invalid offset or length.");
+
+ int result = 0;
+
+ if(getUseSelectTimeout() && !__stimeout.isZero())
+ result =
+ __select(__socket, false, __stimeout.seconds, __stimeout.microseconds);
+
+ if(result == 0)
+ result =
+ __sendto(__socket, data, offset, length, __family,
+ address.getAddress());
+
+ if(result < 0) {
+ if(__isErrorEAGAIN())
+ __throwInterruptedIOException();
+ else
+ __throwIOException();
+ }
+
+ return result;
+ }
+
+
+ /** Same as {@code write(address, data, 0, data.length);} */
+ public int write(InetAddress address, byte[] data)
+ throws IOException, InterruptedIOException
+ {
+ return write(address, data, 0, data.length);
+ }
+
+}
diff --git a/ipscan/ext/rocksaw/src/jni/Makefile b/ipscan/ext/rocksaw/src/jni/Makefile
new file mode 100755
index 00000000..ff14538d
--- /dev/null
+++ b/ipscan/ext/rocksaw/src/jni/Makefile
@@ -0,0 +1,74 @@
+#
+# $Id: Makefile 6875 2006-04-14 00:29:33Z dfs $
+#
+# Copyright 2004-2005 Daniel F. Savarese
+# Contact Information: http://www.savarese.org/contact.html
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.savarese.org/software/ApacheLicense-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+UNAME := $(shell uname)
+CYGWIN := $(findstring CYGWIN,$(UNAME))
+DARWIN := $(findstring Darwin,$(UNAME))
+
+ifeq ($(CYGWIN),CYGWIN)
+ JDK_HOME := $(shell cygpath $(JDK_HOME))
+endif
+
+JAVA_INCDIR = $(JDK_HOME)/include
+JAVA_INCDIR_PLAF = $(dir $(wildcard $(JAVA_INCDIR)/*/jni_md.h))
+
+CC = gcc
+CFLAGS = -ansi -Wall -O2 -pipe
+CPPFLAGS = -I$(JAVA_INCDIR) -I$(JAVA_INCDIR_PLAF)
+WINSOCK = ws2_32
+LDFLAGS =
+
+ifeq ($(CYGWIN),CYGWIN)
+ override CC += -mno-cygwin
+ CPPFLAGS += -D__int64="long long"
+ LIBNAME = rocksaw
+ LIBEXTENSION = dll
+ LDFLAGS += -Wl,--kill-at -l$(WINSOCK)
+else
+ LIBNAME = librocksaw
+
+ ifeq ($(DARWIN),Darwin)
+ LIBEXTENSION = jnilib
+ LDFLAGS += -dynamiclib -noprebind -single_module
+ else
+ CFLAGS += -fpic
+ LIBEXTENSION = so
+ endif
+endif
+
+SRC := $(shell find . -name "*.c" -print)
+OBJ := $(SRC:%.c=%.o)
+
+CLEAN_EXTENSIONS = o $(LIBEXTENSION)
+
+LIBROCKSAW = $(LIBNAME).$(LIBEXTENSION)
+
+all: $(LIBROCKSAW)
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+$(LIBROCKSAW): $(OBJ)
+ $(CC) -shared -o $@ $^ $(LDFLAGS)
+
+clean:
+ for extension in $(CLEAN_EXTENSIONS); do \
+ find . -name "*.$$extension" | xargs rm -f ; \
+ done
+ find . -name "*~" | xargs rm -f
diff --git a/ipscan/ext/rocksaw/src/jni/Makefile.win32 b/ipscan/ext/rocksaw/src/jni/Makefile.win32
new file mode 100755
index 00000000..d7273453
--- /dev/null
+++ b/ipscan/ext/rocksaw/src/jni/Makefile.win32
@@ -0,0 +1,46 @@
+#
+# $Id: Makefile.win32 5253 2005-05-06 04:24:20Z dfs $
+#
+# Copyright 2004-2005 Daniel F. Savarese
+# Contact Information: http://www.savarese.org/contact.html
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.savarese.org/software/ApacheLicense-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+JAVA_INCDIR = $(JDK_HOME)\include
+JAVA_INCDIR_PLAF = $(JAVA_INCDIR)\win32
+
+CC = cl
+CFLAGS = -TC
+CPPFLAGS = -I$(JAVA_INCDIR) -I$(JAVA_INCDIR_PLAF)
+WINSOCK = ws2_32
+LDFLAGS = $(WINSOCK).lib
+
+SRC = RawSocket.c
+OBJ = $(SRC:.c=.obj)
+
+LIBNAME = rocksaw
+LIBEXTENSION = dll
+LIBROCKSAW = $(LIBNAME).$(LIBEXTENSION)
+CLEAN_EXTENSIONS = *.obj *.$(LIBEXTENSION) *.lib *.exp
+
+all: $(LIBROCKSAW)
+
+.c.obj:
+ $(CC) -nologo $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+$(LIBROCKSAW): $(OBJ)
+ $(CC) -nologo -MD -LD -o $@ $** $(LDFLAGS)
+
+clean:
+ del $(CLEAN_EXTENSIONS)
diff --git a/ipscan/ext/rocksaw/src/jni/RawSocket.c b/ipscan/ext/rocksaw/src/jni/RawSocket.c
new file mode 100755
index 00000000..05a7fa00
--- /dev/null
+++ b/ipscan/ext/rocksaw/src/jni/RawSocket.c
@@ -0,0 +1,516 @@
+/*
+ * $Id: RawSocket.c 5982 2005-11-09 18:20:21Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include
+#include
+
+#if defined(_WIN32)
+
+# include
+# include
+
+# if !defined(close)
+# define close(fd) closesocket(fd)
+# endif
+
+#else
+
+# include
+# include
+# include
+# include
+# include
+
+#endif
+
+#include "RawSocket.h"
+
+/*
+ * Utility functions.
+ */
+
+static int setintsockopt(int socket, int level, int option, int value);
+static int getintsockopt(int socket, int level, int option);
+static int settimeout(int socket, int option, int timeout);
+static int gettimeout(int socket, int option);
+
+static int setintsockopt(int socket, int level, int option, int value) {
+ return setsockopt(socket, level, option, (void*)&value, sizeof(value));
+}
+
+
+static int getintsockopt(int socket, int level, int option) {
+ int value = -1;
+ socklen_t size = sizeof(value);
+ int result = getsockopt(socket, level, option, (void*)&value, &size);
+
+ if(result < 0)
+ return result;
+
+ return value;
+}
+
+
+static int settimeout(int socket, int option, int timeout) {
+#if defined(_WIN32)
+ return setintsockopt(socket, SOL_SOCKET, option, timeout);
+#else
+ int seconds;
+ struct timeval value;
+
+ seconds = timeout / 1000;
+
+ if(seconds > 0)
+ timeout-=(seconds*1000);
+
+ value.tv_sec = seconds;
+ value.tv_usec = timeout * 1000;
+
+ return setsockopt(socket, SOL_SOCKET, option, (void*)&value, sizeof(value));
+#endif
+}
+
+
+static int gettimeout(int socket, int option) {
+ int result;
+ struct timeval value;
+ socklen_t size = sizeof(value);
+
+ result = getsockopt(socket, SOL_SOCKET, option, (void*)&value, &size);
+
+ if(result < 0)
+ return result;
+
+ return (value.tv_sec * 1000 + value.tv_usec / 1000);
+}
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __RockSawInit();
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1RockSawStartup
+(JNIEnv *env, jclass cls)
+{
+#if defined(_WIN32)
+ WORD version = MAKEWORD(2, 0);
+ WSADATA data;
+ return (errno = WSAStartup(version, &data));
+#else
+ return 0;
+#endif
+}
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __RockSawShutdown();
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1RockSawShutdown
+(JNIEnv *env, jclass cls)
+{
+#if defined(_WIN32)
+ WSACleanup();
+#endif
+}
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __getErrorMessage
+ * Signature: (Ljava/lang/StringBuffer;)V
+ */
+JNIEXPORT void JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getErrorMessage
+(JNIEnv *env, jclass cls, jobject buffer)
+{
+ jclass sbc;
+ jstring str;
+ jmethodID mid;
+
+ if(errno) {
+ char *message = NULL;
+
+#if defined(_WIN32)
+ int formatted =
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, errno,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &message, 0, NULL );
+ if(!formatted)
+ message = strerror(errno);
+#else
+ message = strerror(errno);
+#endif
+
+ str = (*env)->NewStringUTF(env, message);
+
+#if defined(_WIN32)
+ if(formatted)
+ LocalFree(message);
+#endif
+
+ sbc = (*env)->GetObjectClass(env, buffer);
+ mid =
+ (*env)->GetMethodID(env, sbc, "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+ (*env)->CallObjectMethod(env, buffer, mid, str);
+ }
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __select
+ * Signature: (IZII)I
+ *
+ * Returns zero if the socket is ready for I/O.
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1select
+(JNIEnv *env, jclass cls,
+ jint socket, jboolean read, jint seconds, jint microseconds)
+{
+ int result;
+ struct timeval timeout;
+ fd_set *rset = NULL, *wset = NULL, errset, fdset;
+
+ FD_ZERO(&fdset);
+ FD_ZERO(&errset);
+ FD_SET(socket, &fdset);
+ FD_SET(socket, &errset);
+
+ timeout.tv_sec = seconds;
+ timeout.tv_usec = microseconds;
+
+ if(read)
+ rset = &fdset;
+ else
+ wset = &fdset;
+
+ result = select(socket + 1, rset, wset, &errset, &timeout);
+
+ if(result >= 0) {
+ if(FD_ISSET(socket, &errset))
+ result = -1;
+ else if(FD_ISSET(socket, &fdset))
+ result = 0;
+ else {
+#if defined(_WIN32)
+ errno = WSAETIMEDOUT;
+#else
+ errno = EAGAIN;
+#endif
+ result = -1;
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __socket
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1socket
+(JNIEnv *env, jclass cls, jint family, jint protocol)
+{
+ return socket(family, SOCK_RAW, protocol);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: getProtocolByName
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket_getProtocolByName
+(JNIEnv *env, jclass cls, jstring name)
+{
+ const char *utf = (*env)->GetStringUTFChars(env, name, NULL);
+ struct protoent *proto = getprotobyname(utf);
+
+ (*env)->ReleaseStringUTFChars(env, name, utf);
+
+ return proto->p_proto;
+}
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __close
+ * Signature: (I)V
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1close
+(JNIEnv *env, jclass cls, jint socket)
+{
+ return close(socket);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __recvfrom
+ * Signature: (I[BIII[B)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1recvfrom
+(JNIEnv *env, jclass cls, jint socket,
+ jbyteArray data, jint offset, jint len, jint family, jbyteArray address)
+{
+ int result;
+ jbyte *buf;
+ struct sockaddr *saddr;
+ struct sockaddr_in sin;
+ socklen_t socklen;
+
+ /* We support only IPv4 for now. There's a better way to handle
+ * this, using getaddrinfo if we only allow receives from a preset
+ * host, obviating the need to check the protocol family and recreate
+ * the required sockaddr.
+ */
+ if(family == PF_INET) {
+ socklen = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = PF_INET;
+ buf = (*env)->GetByteArrayElements(env, address, NULL);
+ memcpy(&sin.sin_addr, buf, sizeof(sin.sin_addr));
+ (*env)->ReleaseByteArrayElements(env, address, buf, JNI_ABORT);
+ saddr = (struct sockaddr *)&sin;
+ } else {
+ errno = EINVAL;
+ return errno;
+ }
+
+ buf = (*env)->GetByteArrayElements(env, data, NULL);
+
+ result = recvfrom(socket, buf+offset, len, 0, saddr, &socklen);
+
+ (*env)->ReleaseByteArrayElements(env, data, buf, 0);
+
+#if defined(_WIN32)
+ if(result < 0)
+ errno = WSAGetLastError();
+#endif
+
+ return result;
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __sendto
+ * Signature: (I[BIII[B)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1sendto
+(JNIEnv *env, jclass cls, jint socket,
+ jbyteArray data, jint offset, jint len, jint family, jbyteArray address)
+{
+ int result;
+ jbyte *buf;
+ struct sockaddr *saddr;
+ struct sockaddr_in sin;
+ socklen_t socklen;
+
+ /* We only support IPv4 for now. There's a better way to handle
+ * this, using getaddrinfo if we only allow receives from a preset
+ * host, obviating the need to check the protocol family and recreate
+ * the required sockaddr.
+ */
+ if(family == PF_INET) {
+ socklen = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = PF_INET;
+ buf = (*env)->GetByteArrayElements(env, address, NULL);
+ memcpy(&sin.sin_addr, buf, sizeof(sin.sin_addr));
+ (*env)->ReleaseByteArrayElements(env, address, buf, JNI_ABORT);
+ saddr = (struct sockaddr *)&sin;
+ } else {
+ errno = EINVAL;
+ return errno;
+ }
+
+ buf = (*env)->GetByteArrayElements(env, data, NULL);
+
+ result = sendto(socket, buf+offset, len, 0, saddr, socklen);
+
+ (*env)->ReleaseByteArrayElements(env, data, buf, JNI_ABORT);
+
+#if defined(_WIN32)
+ if(result < 0)
+ errno = WSAGetLastError();
+#endif
+
+ return result;
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __isErrorEAGAIN
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1isErrorEAGAIN
+(JNIEnv *env, jclass cls)
+{
+#if defined(_WIN32)
+ return (errno == WSAETIMEDOUT);
+#else
+ return (errno == EAGAIN || errno == EWOULDBLOCK);
+#endif
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __setIPHeaderInclude
+ * Signature: (IZ)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setIPHeaderInclude
+(JNIEnv *env, jclass cls, jint socket, jboolean on)
+{
+ return setintsockopt(socket, IPPROTO_IP, IP_HDRINCL, on);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __getIPHeaderInclude
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getIPHeaderInclude
+(JNIEnv *env, jclass cls, jint socket)
+{
+ return getintsockopt(socket, IPPROTO_IP, IP_HDRINCL);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __setSendBufferSize
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setSendBufferSize
+(JNIEnv *env, jclass cls, jint socket, jint size)
+{
+ return setintsockopt(socket, SOL_SOCKET, SO_SNDBUF, size);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __getSendBufferSize
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getSendBufferSize
+(JNIEnv *env, jclass cls, jint socket)
+{
+ return getintsockopt(socket, SOL_SOCKET, SO_SNDBUF);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __setReceiveBufferSize
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setReceiveBufferSize
+(JNIEnv *env, jclass cls, jint socket, jint size)
+{
+ return setintsockopt(socket, SOL_SOCKET, SO_RCVBUF, size);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __getReceiveBufferSize
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getReceiveBufferSize
+(JNIEnv *env, jclass cls, jint socket)
+{
+ return getintsockopt(socket, SOL_SOCKET, SO_RCVBUF);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __setSendTimeout
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setSendTimeout
+(JNIEnv *env, jclass cls, jint socket, jint timeout)
+{
+ return settimeout(socket, SO_SNDTIMEO, timeout);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __getSendTimeout
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getSendTimeout
+(JNIEnv *env, jclass cls, jint socket)
+{
+ return gettimeout(socket, SO_SNDTIMEO);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __setReceiveTimeout
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setReceiveTimeout
+(JNIEnv *env, jclass cls, jint socket, jint timeout)
+{
+ return settimeout(socket, SO_RCVTIMEO, timeout);
+}
+
+
+/*
+ * Class: org_savarese_rocksaw_net_RawSocket
+ * Method: __getReceiveTimeout
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getReceiveTimeout
+(JNIEnv *env, jclass cls, jint socket)
+{
+ return gettimeout(socket, SO_RCVTIMEO);
+}
diff --git a/ipscan/ext/rocksaw/src/jni/RawSocket.h b/ipscan/ext/rocksaw/src/jni/RawSocket.h
new file mode 100755
index 00000000..aab4ec57
--- /dev/null
+++ b/ipscan/ext/rocksaw/src/jni/RawSocket.h
@@ -0,0 +1,112 @@
+/*
+ * $Id: RawSocket.h 5979 2005-11-09 18:11:02Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __ROCKSAW_RAW_SOCKET_H
+#define __ROCKSAW_RAW_SOCKET_H
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT void JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getErrorMessage
+(JNIEnv *, jclass, jobject);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1RockSawStartup
+(JNIEnv *, jclass);
+
+JNIEXPORT void JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1RockSawShutdown
+(JNIEnv *, jclass);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1select
+(JNIEnv *env, jclass cls, jint, jboolean, jint, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1socket
+(JNIEnv *, jclass, jint, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket_getProtocolByName
+(JNIEnv *, jclass, jstring);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1close
+(JNIEnv *, jclass, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1recvfrom
+(JNIEnv *, jclass, jint, jbyteArray, jint, jint, jint, jbyteArray);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1sendto
+(JNIEnv *, jclass, jint, jbyteArray, jint, jint, jint, jbyteArray);
+
+JNIEXPORT jboolean JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1isErrorEAGAIN
+(JNIEnv *, jclass);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setIPHeaderInclude
+(JNIEnv *, jclass, jint, jboolean);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getIPHeaderInclude
+(JNIEnv *env, jclass cls, jint socket);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setSendBufferSize
+(JNIEnv *, jclass, jint, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getSendBufferSize
+(JNIEnv *, jclass, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setReceiveBufferSize
+(JNIEnv *, jclass, jint, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getReceiveBufferSize
+(JNIEnv *, jclass, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setSendTimeout
+(JNIEnv *, jclass, jint, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getSendTimeout
+(JNIEnv *, jclass, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1setReceiveTimeout
+(JNIEnv *, jclass, jint, jint);
+
+JNIEXPORT jint JNICALL
+Java_org_savarese_rocksaw_net_RawSocket__1_1getReceiveTimeout
+(JNIEnv *, jclass, jint);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ipscan/ext/vserv-tcpip/LICENSE b/ipscan/ext/vserv-tcpip/LICENSE
new file mode 100755
index 00000000..d6456956
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/ipscan/ext/vserv-tcpip/NOTICE b/ipscan/ext/vserv-tcpip/NOTICE
new file mode 100755
index 00000000..3ee836b9
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/NOTICE
@@ -0,0 +1,2 @@
+This product includes software developed by
+Daniel F. Savarese (http://www.savarese.org/).
diff --git a/ipscan/ext/vserv-tcpip/README b/ipscan/ext/vserv-tcpip/README
new file mode 100755
index 00000000..20f38ac8
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/README
@@ -0,0 +1,57 @@
+$Id: README 6024 2005-12-10 23:04:56Z dfs $
+
+ABOUT
+-----
+
+Virtual Services TCP/IP, or VServ TCP/IP for short, is a Java library that
+enables you to easily manipulate IP and TCP packets. It is intended for
+use in conjunction with a library which generates packets, such as
+VServ IPQ, as byte arrays. At the moment it supports only IPv4 packet
+manipulation.
+
+The API is still in flux; some methods are missing and some existing
+methods will likely be renamed. If you would like something added,
+please submit a patch in unified diff format.
+
+
+REQUIREMENTS
+------------
+
+VServ TCP/IP requires J2SE 1.4 or greater to compile and run.
+
+
+COMPILING
+---------
+
+The source code requires Apache Ant (http://ant.apache.org/) to compile.
+
+ ant -projecthelp
+
+will list all build targets in build.xml. There are very few files
+in the source tree:
+
+ src/java Java source code
+
+When you compile the source with
+
+ ant jar
+
+the following jar file will be created and placed in the lib/ directory:
+
+ vserv-tcpip-version.jar
+
+
+LICENSING
+---------
+
+VServ TCP/IP is Copyright 2004-2005 by Daniel F. Savarese and licensed
+under the Apache License 2.0 as described in the files:
+
+ LICENSE
+ NOTICE
+
+
+CONTACT
+-------
+
+To contact me see http://www.savarese.org/contact.html
diff --git a/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/ICMPEchoPacket.java b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/ICMPEchoPacket.java
new file mode 100755
index 00000000..343a9755
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/ICMPEchoPacket.java
@@ -0,0 +1,102 @@
+/*
+ * $Id: ICMPEchoPacket.java 5260 2005-05-10 21:01:16Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.savarese.vserv.tcpip;
+
+/**
+ * ICMPEchoPacket extends {@link ICMPPacket} to implement ICMP echo request
+ * and reply packets.
+ *
+ * @author Daniel F. Savarese
+ */
+
+public class ICMPEchoPacket extends ICMPPacket {
+
+ /** Offset into the ICMP packet of the identifier header value. */
+ public static final int OFFSET_IDENTIFIER = 4;
+
+ /** Offset into the ICMP packet of the sequence number header value. */
+ public static final int OFFSET_SEQUENCE = 6;
+
+
+ /**
+ * Creates a new ICMP echo packet of a given size.
+ *
+ * @param size The number of bytes in the packet.
+ */
+ public ICMPEchoPacket(int size) {
+ super(size);
+ }
+
+
+ /**
+ * Creates a new ICMP echo packet that is a copy of a given packet.
+ *
+ * @param packet The packet to replicate.
+ */
+ public ICMPEchoPacket(ICMPEchoPacket packet) {
+ super(packet);
+ }
+
+
+ public int getICMPHeaderByteLength() {
+ return 8;
+ }
+
+
+ /**
+ * Sets the identifier header field.
+ *
+ * @param id The new identifier.
+ */
+ public final void setIdentifier(int id) {
+ _data_[_offset + OFFSET_IDENTIFIER] = (byte)((id >> 8) & 0xff);
+ _data_[_offset + OFFSET_IDENTIFIER + 1] = (byte)(id & 0xff);
+ }
+
+
+ /**
+ * @return The identifier header field.
+ */
+ public final int getIdentifier() {
+ return (((_data_[_offset + OFFSET_IDENTIFIER] & 0xff) << 8) |
+ (_data_[_offset + OFFSET_IDENTIFIER + 1] & 0xff));
+ }
+
+
+ /**
+ * Sets the sequence number.
+ *
+ * @param seq The new sequence number.
+ */
+ public final void setSequenceNumber(int seq) {
+ _data_[_offset + OFFSET_SEQUENCE] = (byte)((seq >> 8) & 0xff);
+ _data_[_offset + OFFSET_SEQUENCE + 1] = (byte)(seq & 0xff);
+ }
+
+
+ /**
+ * @return The sequence number.
+ */
+ public final int getSequenceNumber() {
+ return (((_data_[_offset + OFFSET_SEQUENCE] & 0xff) << 8) |
+ (_data_[_offset + OFFSET_SEQUENCE + 1] & 0xff));
+ }
+
+}
diff --git a/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/ICMPPacket.java b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/ICMPPacket.java
new file mode 100755
index 00000000..affd0162
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/ICMPPacket.java
@@ -0,0 +1,227 @@
+/*
+ * $Id: ICMPPacket.java 5347 2005-05-25 22:45:54Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.savarese.vserv.tcpip;
+
+/**
+ * ICMPPacket extends {@link IPPacket} to handle ICMP packets. The ICMP
+ * packet structure is described in
+ * RFC 792.
+ *
+ * @author Daniel F. Savarese
+ */
+
+public abstract class ICMPPacket extends IPPacket {
+
+ /** Offset into the ICMP packet of the type header value. */
+ public static final int OFFSET_TYPE = 0;
+
+ /** Offset into the ICMP packet of the code header value. */
+ public static final int OFFSET_CODE = 1;
+
+ /** Offset into the ICMP packet of the ICMP checksum. */
+ public static final int OFFSET_ICMP_CHECKSUM = 2;
+
+ /** Offset into the ICMP packet of the identifier header value. */
+ public static final int OFFSET_IDENTIFIER = 4;
+
+ /** Offset into the ICMP packet of the sequence number header value. */
+ public static final int OFFSET_SEQUENCE = 6;
+
+ /** The ICMP type number for an echo request. */
+ public static final int TYPE_ECHO_REQUEST = 8;
+
+ /** The ICMP type number for an echo reply. */
+ public static final int TYPE_ECHO_REPLY = 0;
+
+ /** The ICMP type number for an "host unreachable" message. */
+ public static final int TYPE_HOST_UNREACHABLE = 3;
+
+ /** The byte offset into the IP packet where the ICMP packet begins. */
+ int _offset;
+
+
+ /**
+ * Creates a new ICMP packet of a given size.
+ *
+ * @param size The number of bytes in the packet.
+ */
+ public ICMPPacket(int size) {
+ super(size);
+ _offset = 0;
+ }
+
+
+ /**
+ * Creates a new ICMP packet that is a copy of a given packet.
+ *
+ * @param packet The packet to replicate.
+ */
+ public ICMPPacket(ICMPPacket packet) {
+ super(packet.size());
+ copy(packet);
+ _offset = packet._offset;
+ }
+
+
+ /** @return The number of bytes in the ICMP packet header. */
+ public abstract int getICMPHeaderByteLength();
+
+
+ public void setIPHeaderLength(int length) {
+ super.setIPHeaderLength(length);
+ _offset = getIPHeaderByteLength();
+ }
+
+
+ /**
+ * @return The total number of bytes in the IP and ICMP headers.
+ */
+ public final int getCombinedHeaderByteLength() {
+ return _offset + getICMPHeaderByteLength();
+ }
+
+
+ /**
+ * Sets the length of the ICMP data payload.
+ *
+ * @param length The length of the ICMP data payload in bytes.
+ */
+ public final void setICMPDataByteLength(int length) {
+ if(length < 0)
+ length = 0;
+
+ setIPPacketLength(getCombinedHeaderByteLength() + length);
+ }
+
+
+ /**
+ * @return The number of bytes in the ICMP data payload.
+ */
+ public final int getICMPDataByteLength() {
+ return getIPPacketLength() - getCombinedHeaderByteLength();
+ }
+
+
+ /**
+ * @return The ICMP packet length. This is the size of the IP packet
+ * minus the size of the IP header.
+ */
+ public final int getICMPPacketByteLength() {
+ return getIPPacketLength() - _offset;
+ }
+
+
+ /**
+ * Copies the contents of an ICMPPacket. If the current data array is
+ * of insufficient length to store the contents, a new array is
+ * allocated.
+ *
+ * @param packet The TCPPacket to copy.
+ */
+ public final void copyData(ICMPPacket packet) {
+ if(_data_.length < packet._data_.length) {
+ byte[] data = new byte[packet._data_.length];
+ System.arraycopy(_data_, 0, data, 0, getCombinedHeaderByteLength());
+ _data_ = data;
+ }
+ int length = packet.getICMPDataByteLength();
+ System.arraycopy(packet._data_, packet.getCombinedHeaderByteLength(),
+ _data_, getCombinedHeaderByteLength(), length);
+ setICMPDataByteLength(length);
+ }
+
+
+ public void setData(byte[] data) {
+ super.setData(data);
+ _offset = getIPHeaderByteLength();
+ }
+
+
+ /**
+ * Sets the ICMP type header field.
+ *
+ * @param type The new type.
+ */
+ public final void setType(int type) {
+ _data_[_offset + OFFSET_TYPE] = (byte)(type & 0xff);
+ }
+
+
+ /**
+ * @return The ICMP type header field.
+ */
+ public final int getType() {
+ return (_data_[_offset + OFFSET_TYPE] & 0xff);
+ }
+
+
+ /**
+ * Sets the ICMP code header field.
+ *
+ * @param code The new type.
+ */
+ public final void setCode(int code) {
+ _data_[_offset + OFFSET_CODE] = (byte)(code & 0xff);
+ }
+
+
+ /**
+ * @return The ICMP code header field.
+ */
+ public final int getCode() {
+ return (_data_[_offset + OFFSET_CODE] & 0xff);
+ }
+
+
+ /**
+ * @return The ICMP checksum.
+ */
+ public final int getICMPChecksum() {
+ return (((_data_[_offset + OFFSET_ICMP_CHECKSUM] & 0xff) << 8) |
+ (_data_[_offset + OFFSET_ICMP_CHECKSUM + 1] & 0xff));
+ }
+
+
+ /**
+ * Computes the ICMP checksum, optionally updating the ICMP checksum header.
+ *
+ * @param update Specifies whether or not to update the ICMP checksum
+ * header after computing the checksum. A value of true indicates
+ * the header should be updated, a value of false indicates it
+ * should not be updated.
+ * @return The computed ICMP checksum.
+ */
+ public final int computeICMPChecksum(boolean update) {
+ return _computeChecksum_(_offset, _offset + OFFSET_ICMP_CHECKSUM,
+ getIPPacketLength(), 0, update);
+ }
+
+
+ /**
+ * Same as computeICMPChecksum(true);
+ *
+ * @return The computed ICMP checksum value.
+ */
+ public final int computeICMPChecksum() {
+ return computeICMPChecksum(true);
+ }
+
+}
+
diff --git a/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/IPPacket.java b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/IPPacket.java
new file mode 100755
index 00000000..99d1cc23
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/IPPacket.java
@@ -0,0 +1,527 @@
+/*
+ * $Id: IPPacket.java 6025 2005-12-10 23:21:25Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.savarese.vserv.tcpip;
+
+import java.net.*;
+
+/**
+ * IPPacket wraps the raw bytes comprising an IPv4 packet and exposes
+ * its content via setter and getter methods. After you alter the
+ * header of an IP packet you have to recompute the checksum with
+ * {@link #computeIPChecksum computeIPChecksum()}. The structure of
+ * IP packets is described in
+ * RFC 760.
+ *
+ * @author Daniel F. Savarese
+ */
+
+public class IPPacket {
+
+ /** Offset into byte array of the type of service header value. */
+ public static final int OFFSET_TYPE_OF_SERVICE = 1;
+
+ /** Offset into byte array of total packet length header value. */
+ public static final int OFFSET_TOTAL_LENGTH = 2;
+
+ /** Offset into byte array of the identification header value. */
+ public static final int OFFSET_IDENTIFICATION = 4;
+
+ /** Offset into byte array of the flags header value. */
+ public static final int OFFSET_FLAGS = 6;
+
+ /** Offset into byte array of source address header value. */
+ public static final int OFFSET_SOURCE_ADDRESS = 12;
+
+ /** Number of bytes in source address. */
+ public static final int LENGTH_SOURCE_ADDRESS = 4;
+
+ /** Offset into byte array of destination address header value. */
+ public static final int OFFSET_DESTINATION_ADDRESS = 16;
+
+ /** Number of bytes in destination address. */
+ public static final int LENGTH_DESTINATION_ADDRESS = 4;
+
+ /** Offset into byte array of time to live header value. */
+ public static final int OFFSET_TTL = 8;
+
+ /** Offset into byte array of protocol number header value. */
+ public static final int OFFSET_PROTOCOL = 9;
+
+ /** Offset into byte array of header checksum header value. */
+ public static final int OFFSET_IP_CHECKSUM = 10;
+
+ /** Protocol constant for IPv4. */
+ public static final int PROTOCOL_IP = 0;
+
+ /** Protocol constant for ICMP. */
+ public static final int PROTOCOL_ICMP = 1;
+
+ /** Protocol constant for TCP. */
+ public static final int PROTOCOL_TCP = 6;
+
+ /** Protocol constant for UDP. */
+ public static final int PROTOCOL_UDP = 17;
+
+ /** Raw packet data. */
+ protected byte[] _data_;
+
+
+ /**
+ * Creates a new IPPacket of a given size.
+ *
+ * @param size The number of bytes in the packet.
+ */
+ public IPPacket(int size) {
+ setData(new byte[size]);
+ }
+
+
+ /**
+ * @return The size of the packet.
+ */
+ public int size() {
+ return _data_.length;
+ }
+
+
+ /**
+ * Sets the raw packet byte array. Although this method would
+ * appear to violate object-oriented principles, it is necessary to
+ * implement efficient packet processing. You don't necessarily
+ * want to allocate a new IPPacket and data buffer every time a
+ * packet arrives and you need to be able to wrap packets from
+ * APIs that supply them as byte arrays.
+ *
+ * @param data The raw packet byte array to wrap.
+ */
+ public void setData(byte[] data) {
+ _data_ = data;
+ }
+
+
+ /**
+ * Copies the raw packet data into a byte array. If the array
+ * is too small to hold the data, the data is truncated.
+ *
+ * @param data The raw packet byte array to wrap.
+ */
+ public void getData(byte[] data) {
+ System.arraycopy(_data_, 0, data, 0, data.length);
+ }
+
+
+ /**
+ * Copies the contents of an IPPacket to the calling instance. If
+ * the two packets are of different lengths, a new byte array is
+ * allocated equal to the length of the packet parameter.
+ *
+ * @param packet The packet to copy from.
+ */
+ public final void copy(IPPacket packet) {
+ if(_data_.length != packet.size())
+ setData(new byte[packet.size()]);
+ System.arraycopy(packet._data_, 0, _data_, 0, _data_.length);
+ }
+
+
+ /**
+ * Sets the IP version header value.
+ *
+ * @param version A 4-bit unsigned integer.
+ */
+ public final void setIPVersion(int version) {
+ _data_[0] &= 0x0f;
+ _data_[0] |= ((version << 4) & 0xf0);
+ }
+
+
+ /**
+ * Returns the IP version header value.
+ *
+ * @return The IP version header value.
+ */
+ public final int getIPVersion() {
+ return ((_data_[0] & 0xf0) >> 4);
+ }
+
+
+ /**
+ * Sets the IP header length field. At most, this can be a
+ * four-bit value. The high order bits beyond the fourth bit
+ * will be ignored.
+ *
+ * @param length The length of the IP header in 32-bit words.
+ */
+ public void setIPHeaderLength(int length) {
+ // Clear low order bits and then set
+ _data_[0] &= 0xf0;
+ _data_[0] |= (length & 0x0f);
+ }
+
+
+ /**
+ * @return The length of the IP header in 32-bit words.
+ */
+ public final int getIPHeaderLength() {
+ return (_data_[0] & 0x0f);
+ }
+
+
+ /**
+ * @return The length of the IP header in bytes.
+ */
+ public final int getIPHeaderByteLength() {
+ return getIPHeaderLength() << 2;
+ }
+
+
+ /**
+ * Sets the IP type of service header value. You have to set the individual
+ * service bits yourself. Convenience methods for setting the service
+ * bit fields directly may be added in a future version.
+ *
+ * @param service An 8-bit unsigned integer.
+ */
+ public final void setTypeOfService(int service) {
+ _data_[OFFSET_TYPE_OF_SERVICE] = (byte)(service & 0xff);
+ }
+
+
+ /**
+ * Returns the IP type of service header value.
+ *
+ * @return The IP type of service header value.
+ */
+ public final int getTypeOfService() {
+ return (_data_[OFFSET_TYPE_OF_SERVICE] & 0xff);
+ }
+
+
+ /**
+ * Sets the IP packet total length header value.
+ *
+ * @param length The total IP packet length in bytes.
+ */
+ public final void setIPPacketLength(int length) {
+ _data_[OFFSET_TOTAL_LENGTH] = (byte)((length >> 8) & 0xff);
+ _data_[OFFSET_TOTAL_LENGTH + 1] = (byte)(length & 0xff);
+ }
+
+
+ /**
+ * @return The IP packet total length header value.
+ */
+ public final int getIPPacketLength() {
+ return (((_data_[OFFSET_TOTAL_LENGTH] & 0xff) << 8) |
+ (_data_[OFFSET_TOTAL_LENGTH + 1] & 0xff));
+ }
+
+
+ /**
+ * Sets the IP identification header value.
+ *
+ * @param id A 16-bit unsigned integer.
+ */
+ public void setIdentification(int id) {
+ _data_[OFFSET_IDENTIFICATION] = (byte)((id >> 8) & 0xff);
+ _data_[OFFSET_IDENTIFICATION + 1] = (byte)(id & 0xff);
+ }
+
+
+ /**
+ * Returns the IP identification header value.
+ *
+ * @return The IP identification header value.
+ */
+ public final int getIdentification() {
+ return (((_data_[OFFSET_IDENTIFICATION] & 0xff) << 8) |
+ (_data_[OFFSET_IDENTIFICATION + 1] & 0xff));
+ }
+
+
+ /**
+ * Sets the IP flags header value. You have to set the individual
+ * flag bits yourself. Convenience methods for setting the flag
+ * bit fields directly may be added in a future version.
+ *
+ * @param flags A 3-bit unsigned integer.
+ */
+ public final void setIPFlags(int flags) {
+ _data_[OFFSET_FLAGS] &= 0x1f;
+ _data_[OFFSET_FLAGS] |= ((flags << 5) & 0xe0);
+ }
+
+
+ /**
+ * Returns the IP flags header value.
+ *
+ * @return The IP flags header value.
+ */
+ public final int getIPFlags() {
+ return ((_data_[OFFSET_FLAGS] & 0xe0) >> 5);
+ }
+
+
+ /**
+ * Sets the fragment offset header value. The offset specifies a
+ * number of octets (i.e., bytes).
+ *
+ * @param offset A 13-bit unsigned integer.
+ */
+ public void setFragmentOffset(int offset) {
+ _data_[OFFSET_FLAGS] &= 0xe0;
+ _data_[OFFSET_FLAGS] |= ((offset >> 8) & 0x1f);
+ _data_[OFFSET_FLAGS + 1] = (byte)(offset & 0xff);
+ }
+
+
+ /**
+ * Returns the fragment offset header value.
+ *
+ * @return The fragment offset header value.
+ */
+ public final int getFragmentOffset() {
+ return (((_data_[OFFSET_FLAGS] & 0x1f) << 8) |
+ (_data_[OFFSET_FLAGS + 1] & 0xff));
+ }
+
+
+ /**
+ * Sets the protocol number.
+ *
+ * @param protocol The protocol number.
+ */
+ public final void setProtocol(int protocol) {
+ _data_[OFFSET_PROTOCOL] = (byte)protocol;
+ }
+
+
+ /**
+ * @return The protocol number.
+ */
+ public final int getProtocol() {
+ return _data_[OFFSET_PROTOCOL];
+ }
+
+
+ /**
+ * Sets the time to live value in seconds.
+ *
+ * @param ttl The time to live value in seconds.
+ */
+ public final void setTTL(int ttl) {
+ _data_[OFFSET_TTL] = (byte)ttl;
+ }
+
+
+ /**
+ * @return The time to live value in seconds.
+ */
+ public final int getTTL() {
+ return _data_[OFFSET_TTL];
+ }
+
+
+ /**
+ * Calculates checksums assuming the checksum is a 16-bit header field.
+ * This method is generalized to work for IP, ICMP, UDP, and TCP packets
+ * given the proper parameters.
+ */
+ protected int _computeChecksum_(int startOffset,
+ int checksumOffset,
+ int length,
+ int virtualHeaderTotal,
+ boolean update)
+ {
+ int total = 0;
+ int i = startOffset;
+ int imax = checksumOffset;
+
+ while(i < imax)
+ total+=(((_data_[i++] & 0xff) << 8) | (_data_[i++] & 0xff));
+
+ // Skip existing checksum.
+ i = checksumOffset + 2;
+
+ imax = length - (length % 2);
+
+ while(i < imax)
+ total+=(((_data_[i++] & 0xff) << 8) | (_data_[i++] & 0xff));
+
+ if(i < length)
+ total+=((_data_[i] & 0xff) << 8);
+
+ total+=virtualHeaderTotal;
+
+ // Fold to 16 bits
+ while((total & 0xffff0000) != 0)
+ total = (total & 0xffff) + (total >>> 16);
+
+ total = (~total & 0xffff);
+
+ if(update) {
+ _data_[checksumOffset] = (byte)(total >> 8);
+ _data_[checksumOffset + 1] = (byte)(total & 0xff);
+ }
+
+ return total;
+ }
+
+
+ /**
+ * Computes the IP checksum, optionally updating the IP checksum header.
+ *
+ * @param update Specifies whether or not to update the IP checksum
+ * header after computing the checksum. A value of true indicates
+ * the header should be updated, a value of false indicates it
+ * should not be updated.
+ * @return The computed IP checksum.
+ */
+ public final int computeIPChecksum(boolean update) {
+ return _computeChecksum_(0, OFFSET_IP_CHECKSUM, getIPHeaderByteLength(),
+ 0, update);
+ }
+
+
+ /**
+ * Same as computeIPChecksum(true);
+ *
+ * @return The computed IP checksum value.
+ */
+ public final int computeIPChecksum() {
+ return computeIPChecksum(true);
+ }
+
+
+ /**
+ * @return The IP checksum header value.
+ */
+ public final int getIPChecksum() {
+ return (((_data_[OFFSET_IP_CHECKSUM] & 0xff) << 8) |
+ (_data_[OFFSET_IP_CHECKSUM + 1] & 0xff));
+ }
+
+
+ /**
+ * Retrieves the source IP address into a byte array. The array
+ * should be {@link #LENGTH_SOURCE_ADDRESS} bytes long.
+ *
+ * @param address The array in which to store the address.
+ */
+ public final void getSource(byte[] address) {
+ System.arraycopy(_data_, OFFSET_SOURCE_ADDRESS, address,
+ 0, (address.length < LENGTH_SOURCE_ADDRESS ?
+ address.length : LENGTH_SOURCE_ADDRESS));
+ }
+
+
+ /**
+ * Retrieves the destionation IP address into a byte array. The array
+ * should be {@link #LENGTH_DESTINATION_ADDRESS} bytes long.
+ *
+ * @param address The array in which to store the address.
+ */
+ public final void getDestination(byte[] address) {
+ System.arraycopy(_data_, OFFSET_DESTINATION_ADDRESS, address,
+ 0, (address.length < LENGTH_DESTINATION_ADDRESS ?
+ address.length : LENGTH_DESTINATION_ADDRESS));
+ }
+
+
+ /**
+ * Retrieves the source IP address as a string into a StringBuffer.
+ *
+ * @param buffer The StringBuffer in which to store the address.
+ */
+ public final void getSource(StringBuffer buffer) {
+ OctetConverter.octetsToString(buffer, _data_, OFFSET_SOURCE_ADDRESS);
+ }
+
+
+ /**
+ * Retrieves the destination IP address as a string into a StringBuffer.
+ *
+ * @param buffer The StringBuffer in which to store the address.
+ */
+ public final void getDestination(StringBuffer buffer) {
+ OctetConverter.octetsToString(buffer, _data_, OFFSET_DESTINATION_ADDRESS);
+ }
+
+
+ /**
+ * Sets the source IP address using a word representation.
+ *
+ * @param src The source IP address as a 32-bit word.
+ */
+ public final void setSourceAsWord(int src) {
+ OctetConverter.intToOctets(src, _data_, OFFSET_SOURCE_ADDRESS);
+ }
+
+
+ /**
+ * Sets the destination IP address using a word representation.
+ *
+ * @param dest The source IP address as a 32-bit word.
+ */
+ public final void setDestinationAsWord(int dest) {
+ OctetConverter.intToOctets(dest, _data_, OFFSET_DESTINATION_ADDRESS);
+ }
+
+
+ /**
+ * @return The source IP address as a 32-bit word.
+ */
+ public final int getSourceAsWord() {
+ return OctetConverter.octetsToInt(_data_, OFFSET_SOURCE_ADDRESS);
+ }
+
+
+ /**
+ * @return The destination IP address as a 32-bit word.
+ */
+ public final int getDestinationAsWord() {
+ return OctetConverter.octetsToInt(_data_, OFFSET_DESTINATION_ADDRESS);
+ }
+
+
+ /**
+ * @return The source IP address as a java.net.InetAddress instance.
+ */
+ public final InetAddress getSourceAsInetAddress()
+ throws UnknownHostException
+ {
+ byte[] octets = new byte[4];
+ getSource(octets);
+ return InetAddress.getByAddress(octets);
+ }
+
+
+
+ /**
+ * @return The destination IP address as a java.net.InetAddress instance.
+ */
+ public final InetAddress getDestinationAsInetAddress()
+ throws UnknownHostException
+ {
+ byte[] octets = new byte[4];
+ getDestination(octets);
+ return InetAddress.getByAddress(octets);
+ }
+}
diff --git a/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/OctetConverter.java b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/OctetConverter.java
new file mode 100755
index 00000000..bcfb459d
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/OctetConverter.java
@@ -0,0 +1,183 @@
+/*
+ * $Id: OctetConverter.java 5067 2005-03-24 06:10:10Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.savarese.vserv.tcpip;
+
+/**
+ * OctetConverter is a utility singleton class for converting IP
+ * octets to and from other representations.
+ *
+ * @author Daniel F. Savarese
+ */
+
+public final class OctetConverter {
+
+ private OctetConverter() { }
+
+ /**
+ * Converts a set of IPv4 octets to a 32-bit word.
+ *
+ * @param octets A byte array containing the IPv4 octets.
+ * @param offset The offset into the array where the octets start.
+ * @return The 32-bit word representation of the IPv4 address.
+ */
+ public static final int octetsToInt(byte[] octets, int offset) {
+ return (((octets[offset] & 0xff) << 24) |
+ ((octets[offset + 1] & 0xff) << 16) |
+ ((octets[offset + 2] & 0xff) << 8) |
+ (octets[offset + 3] & 0xff));
+ }
+
+
+ /**
+ * Same as octetsToInt(octets, 0);
+ */
+ public static final int octetsToInt(byte[] octets) {
+ return octetsToInt(octets, 0);
+ }
+
+
+ /**
+ * Converts a set of octets to a 64-bit word.
+ *
+ * @param octets A byte array containing the octets.
+ * @param offset The offset into the array where the octets start.
+ * @return The 64-bit word representation of the octets.
+ */
+ public static final long octetsToLong(byte[] octets, int offset) {
+ return (((octets[offset] & 0xffffL) << 56) |
+ ((octets[offset + 1] & 0xffL) << 48) |
+ ((octets[offset + 2] & 0xffL) << 40) |
+ ((octets[offset + 3] & 0xffL) << 32) |
+ ((octets[offset + 4] & 0xffL) << 24) |
+ ((octets[offset + 5] & 0xffL) << 16) |
+ ((octets[offset + 6] & 0xffL) << 8) |
+ (octets[offset + 7] & 0xffL));
+ }
+
+
+ /**
+ * Same as octetsToLong(octets, 0);
+ */
+ public static final long octetsToLong(byte[] octets) {
+ return octetsToLong(octets, 0);
+ }
+
+
+ /**
+ * Converts a set of IPv4 octets to a string representation.
+ *
+ * @param buffer The StringBuffer to which to append the string.
+ * @param octets A byte array containing the IPv4 octets.
+ * @param offset The offset into the array where the octets start.
+ */
+ public static final void octetsToString(StringBuffer buffer, byte[] octets,
+ int offset)
+ {
+ buffer.append(octets[offset++] & 0xff);
+ buffer.append(".");
+ buffer.append(octets[offset++] & 0xff);
+ buffer.append(".");
+ buffer.append(octets[offset++] & 0xff);
+ buffer.append(".");
+ buffer.append(octets[offset++] & 0xff);
+ }
+
+
+ /**
+ * Same as octetsToString(buffer, octets, 0);
+ */
+ public static final void octetsToString(StringBuffer buffer, byte[] octets) {
+ octetsToString(buffer, octets, 0);
+ }
+
+
+ /**
+ * Converts a 32-bit word representation of an IPv4 address to a
+ * string representation.
+ *
+ * @param buffer The StringBuffer to which to append the string.
+ * @param address The 32-bit word representation of the address.
+ */
+ public static final void intToString(StringBuffer buffer, int address) {
+ buffer.append(0xff & (address >>> 24));
+ buffer.append(".");
+ buffer.append(0xff & (address >>> 16));
+ buffer.append(".");
+ buffer.append(0xff & (address >>> 8));
+ buffer.append(".");
+ buffer.append(0xff & address);
+ }
+
+
+ /**
+ * Converts a 32-bit word representation of an IPv4 address to a
+ * byte array of octets.
+ *
+ * @param address The 32-bit word representation of the IPv4 address.
+ * @param octets The byte array in which to store the IPv4 octets.
+ * @param offset The offset into the array where the octets start.
+ */
+ public static final void intToOctets(int address, byte[] octets,
+ int offset)
+ {
+ octets[offset] = (byte)(0xff & (address >>> 24));
+ octets[offset + 1] = (byte)(0xff & (address >>> 16));
+ octets[offset + 2] = (byte)(0xff & (address >>> 8));
+ octets[offset + 3] = (byte)(0xff & address);
+ }
+
+
+ /**
+ * Same as intToOctets(address, octets, 0);
+ */
+ public static final void intToOctets(int address, byte[] octets) {
+ intToOctets(address, octets, 0);
+ }
+
+
+ /**
+ * Converts a 64-bit word to a byte array of octets.
+ *
+ * @param address The 64-bit word.
+ * @param octets The byte array in which to store octets.
+ * @param offset The offset into the array where the octets start.
+ */
+ public static final void longToOctets(long address, byte[] octets,
+ int offset)
+ {
+ octets[offset] = (byte)(0xffL & (address >>> 56));
+ octets[offset + 1] = (byte)(0xffL & (address >>> 48));
+ octets[offset + 2] = (byte)(0xffL & (address >>> 40));
+ octets[offset + 3] = (byte)(0xffL & (address >>> 32));
+ octets[offset + 4] = (byte)(0xffL & (address >>> 24));
+ octets[offset + 5] = (byte)(0xffL & (address >>> 16));
+ octets[offset + 6] = (byte)(0xffL & (address >>> 8));
+ octets[offset + 7] = (byte)(0xffL & address);
+ }
+
+
+ /**
+ * Same as longToOctets(address, octets, 0);
+ */
+ public static final void longToOctets(long address, byte[] octets) {
+ longToOctets(address, octets, 0);
+ }
+
+}
diff --git a/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/TCPPacket.java b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/TCPPacket.java
new file mode 100755
index 00000000..1a9ee380
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/TCPPacket.java
@@ -0,0 +1,492 @@
+/*
+ * $Id: TCPPacket.java 6023 2005-12-10 20:42:15Z dfs $
+ *
+ * Copyright 2004-2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.savarese.vserv.tcpip;
+
+/**
+ * TCPPacket extends {@link IPPacket} to handle TCP packets. The TCP
+ * packet structure is described in
+ * RFC 761.
+ *
+ * @author Daniel F. Savarese
+ */
+
+public class TCPPacket extends IPPacket {
+
+ /** Offset into the TCP packet of the source port header value. */
+ public static final int OFFSET_SOURCE_PORT = 0;
+
+ /** Offset into the TCP packet of the destination port header value. */
+ public static final int OFFSET_DESTINATION_PORT = 2;
+
+ /** Offset into the TCP packet of the sequence number header value. */
+ public static final int OFFSET_SEQUENCE = 4;
+
+ /** Offset into the TCP packet of the acknowledgement number header value. */
+ public static final int OFFSET_ACK = 8;
+
+ /** Offset into the TCP packet of the TCP header length. */
+ public static final int OFFSET_HEADER_LENGTH = 12;
+
+ /** Offset into the TCP packet of the control header. */
+ public static final int OFFSET_CONTROL = 13;
+
+ /** Offset into the TCP packet of the window size. */
+ public static final int OFFSET_WINDOW_SIZE = 14;
+
+ /** Offset into the TCP packet of the TCP checksum. */
+ public static final int OFFSET_TCP_CHECKSUM = 16;
+
+ /** Offset into the TCP packet of the URG pointer. */
+ public static final int OFFSET_URG_POINTER = 18;
+
+ /** A mask for extracting the FIN bit from the control header. */
+ public static final int MASK_FIN = 0x01;
+
+ /** A mask for extracting the SYN bit from the control header. */
+ public static final int MASK_SYN = 0x02;
+
+ /** A mask for extracting the reset bit from the control header. */
+ public static final int MASK_RST = 0x04;
+
+ /** A mask for extracting the push bit from the control header. */
+ public static final int MASK_PSH = 0x08;
+
+ /** A mask for extracting the ACK bit from the control header. */
+ public static final int MASK_ACK = 0x10;
+
+ /** A mask for extracting the urgent bit from the control header. */
+ public static final int MASK_URG = 0x20;
+
+ /** A byte value for TCP options indicating end of option list. */
+ public static final byte KIND_EOL = 0;
+
+ /** A byte value for TCP options indicating no operation. */
+ public static final byte KIND_NOP = 1;
+
+ /**
+ * A byte value for TCP options identifying a selective
+ * acknowledgement option.
+ */
+ public static final byte KIND_SACK = 4;
+
+ /** The byte offset into the IP packet where the TCP packet begins. */
+ private int __offset;
+
+ /**
+ * Creates a new TCP packet of a given size.
+ *
+ * @param size The number of bytes in the packet.
+ */
+ public TCPPacket(int size) {
+ super(size);
+ __offset = 0;
+ }
+
+
+ /**
+ * Creates a new TCP packet that is a copy of a given packet.
+ *
+ * @param packet The packet to replicate.
+ */
+ public TCPPacket(TCPPacket packet) {
+ super(packet.size());
+ copy(packet);
+ __offset = packet.__offset;
+ }
+
+
+ /**
+ * Clears all selective acknowledgement options. This is a
+ * temporary kluge and will be removed from the final API. Do not
+ * use it. The final API will have proper methods for adjusting
+ * selective acknowledgement options.
+ */
+ public void clearSACK() {
+ int headerLength = getTCPHeaderByteLength();
+ int offset = OFFSET_URG_POINTER + 2;
+
+ if(headerLength > offset) {
+ offset+=__offset;
+ headerLength+=__offset;
+
+ loop:
+ do {
+ byte kind = _data_[offset];
+
+ switch(kind) {
+ case KIND_NOP:
+ ++offset;
+ break;
+ case KIND_EOL:
+ break loop;
+ case KIND_SACK:
+ _data_[offset] = KIND_NOP;
+ _data_[offset + 1] = KIND_NOP;
+ break loop;
+ //break;
+ default:
+ offset+=_data_[offset + 1];
+ /*
+ int length = _data_[offset + 1];
+ while(length-- > 0)
+ _data_[offset++] = KIND_NOP;
+ */
+ break;
+ }
+
+ } while(offset < headerLength);
+
+ }
+ }
+
+
+ /**
+ * Copies the contents of a TCPPacket. If the current data array is
+ * of insufficient length to store the contents, a new array is
+ * allocated.
+ *
+ * @param packet The TCPPacket to copy.
+ */
+ public final void copyData(TCPPacket packet) {
+ if(_data_.length < packet._data_.length) {
+ byte[] data = new byte[packet._data_.length];
+ System.arraycopy(_data_, 0, data, 0, getCombinedHeaderByteLength());
+ _data_ = data;
+ }
+ int length = packet.getTCPDataByteLength();
+ System.arraycopy(packet._data_, packet.getCombinedHeaderByteLength(),
+ _data_, getCombinedHeaderByteLength(), length);
+ setTCPDataByteLength(length);
+ }
+
+ /**
+ * @param mask The bit mask to check.
+ * @return True only if all of the bits in the mask are set.
+ */
+ public boolean isSet(int mask) {
+ return ((_data_[__offset + OFFSET_CONTROL] & mask) == mask);
+ }
+
+
+ /**
+ * @param mask The bit mask to check.
+ * @return True if any of the bits in the mask are set.
+ */
+ public boolean isSetAny(int mask) {
+ return ((_data_[__offset + OFFSET_CONTROL] & mask) != 0);
+ }
+
+
+ /**
+ * @param mask The bit mask to check.
+ * @return True only if all of the bits in the mask are set
+ * and ONLY the bits in the mask are set.
+ */
+ public boolean isSetOnly(int mask) {
+ int flags = _data_[__offset + OFFSET_CONTROL] & 0xff;
+ return ((flags & mask) == flags);
+ }
+
+
+ /**
+ * Sets the specified control bits without altering any other bits
+ * in the control header.
+ *
+ * @param mask The bits to set.
+ */
+ public void addControlFlags(int mask) {
+ int flags = _data_[__offset + OFFSET_CONTROL] & 0xff;
+ flags |= mask;
+ _data_[__offset + OFFSET_CONTROL] = (byte)(flags & 0xff);
+ }
+
+
+ /**
+ * Unsets the specified control bits.
+ *
+ * @param mask The bits to unset.
+ */
+ public void removeControlFlags(int mask) {
+ int flags = _data_[__offset + OFFSET_CONTROL] & 0xff;
+ flags |= mask;
+ flags ^= mask;
+ _data_[__offset + OFFSET_CONTROL] = (byte)(flags & 0xff);
+ }
+
+
+ /**
+ * Sets the control header to the sepecified value.
+ *
+ * @param mask The new control header bit mask.
+ */
+ public void setControlFlags(int mask) {
+ _data_[__offset + OFFSET_CONTROL] = (byte)(mask & 0xff);
+ }
+
+
+ public void setData(byte[] data) {
+ super.setData(data);
+ __offset = getIPHeaderByteLength();
+ }
+
+
+ /**
+ * Sets the source port.
+ *
+ * @param port The new source port.
+ */
+ public final void setSourcePort(int port) {
+ _data_[__offset + OFFSET_SOURCE_PORT] = (byte)((port >> 8) & 0xff);
+ _data_[__offset + OFFSET_SOURCE_PORT + 1] = (byte)(port & 0xff);
+ }
+
+
+ /**
+ * Sets the destination port.
+ *
+ * @param port The new destination port.
+ */
+ public final void setDestinationPort(int port) {
+ _data_[__offset + OFFSET_DESTINATION_PORT] = (byte)((port >> 8) & 0xff);
+ _data_[__offset + OFFSET_DESTINATION_PORT + 1] = (byte)(port & 0xff);
+ }
+
+
+ /**
+ * @return The source port.
+ */
+ public final int getSourcePort() {
+ return (((_data_[__offset + OFFSET_SOURCE_PORT] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_SOURCE_PORT + 1] & 0xff));
+ }
+
+
+ /**
+ * @return The destination port.
+ */
+ public final int getDestinationPort() {
+ return (((_data_[__offset + OFFSET_DESTINATION_PORT] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_DESTINATION_PORT + 1] & 0xff));
+ }
+
+
+ /**
+ * Sets the sequence number.
+ *
+ * @param seq The new sequence number.
+ */
+ public final void setSequenceNumber(long seq) {
+ OctetConverter.intToOctets((int)(seq & 0xffffffff), _data_,
+ __offset + OFFSET_SEQUENCE);
+ }
+
+
+ /**
+ * @return The sequence number.
+ */
+ public final long getSequenceNumber() {
+ return (((_data_[__offset + OFFSET_SEQUENCE] & 0xffL) << 24) |
+ ((_data_[__offset + OFFSET_SEQUENCE + 1] & 0xffL) << 16) |
+ ((_data_[__offset + OFFSET_SEQUENCE + 2] & 0xffL) << 8) |
+ (_data_[__offset + OFFSET_SEQUENCE + 3] & 0xffL));
+ }
+
+
+ /**
+ * Sets the acknowledgement number.
+ *
+ * @param seq The new acknowledgement number.
+ */
+ public final void setAckNumber(long seq) {
+ OctetConverter.intToOctets((int)(seq & 0xffffffff), _data_,
+ __offset + OFFSET_ACK);
+ }
+
+
+ /**
+ * @return The acknowledgement number.
+ */
+ public final long getAckNumber() {
+ return (((_data_[__offset + OFFSET_ACK] & 0xffL) << 24) |
+ ((_data_[__offset + OFFSET_ACK + 1] & 0xffL) << 16) |
+ ((_data_[__offset + OFFSET_ACK + 2] & 0xffL) << 8) |
+ (_data_[__offset + OFFSET_ACK + 3] & 0xffL));
+ }
+
+
+ public void setIPHeaderLength(int length) {
+ super.setIPHeaderLength(length);
+ __offset = getIPHeaderByteLength();
+ }
+
+
+ /**
+ * Sets te TCP header length (i.e., the data offset field) in 32-bit words.
+ *
+ * @param length The TCP header length in 32-bit words.
+ */
+ public final void setTCPHeaderLength(int length) {
+ _data_[__offset + OFFSET_HEADER_LENGTH] &= 0x0f;
+ _data_[__offset + OFFSET_HEADER_LENGTH] |= ((length << 4) & 0xf0);
+ }
+
+
+ /**
+ * @return The TCP header length in 32-bit words.
+ */
+ public final int getTCPHeaderLength() {
+ return (_data_[__offset + OFFSET_HEADER_LENGTH] & 0xf0) >> 4;
+ }
+
+
+ /**
+ * @return The TCP header length in bytes.
+ */
+ public final int getTCPHeaderByteLength() {
+ return getTCPHeaderLength() << 2;
+ }
+
+
+ /**
+ * Sets the TCP window size.
+ *
+ * @param window The TCP window size.
+ */
+ public final void setWindowSize(int window) {
+ _data_[__offset + OFFSET_WINDOW_SIZE] = (byte)((window >> 8) & 0xff);
+ _data_[__offset + OFFSET_WINDOW_SIZE + 1] = (byte)(window & 0xff);
+ }
+
+
+ /**
+ * @return The TCP window size.
+ */
+ public final int getWindowSize() {
+ return (((_data_[__offset + OFFSET_WINDOW_SIZE] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_WINDOW_SIZE + 1] & 0xff));
+ }
+
+
+ /**
+ * Sets the urgent pointer.
+ *
+ * @param pointer The urgent pointer value.
+ */
+ public final void setUrgentPointer(int pointer) {
+ _data_[__offset + OFFSET_URG_POINTER] = (byte)((pointer >> 8) & 0xff);
+ _data_[__offset + OFFSET_URG_POINTER + 1] = (byte)(pointer & 0xff);
+ }
+
+
+ /**
+ * @return The urgent pointer value.
+ */
+ public final int getUrgentPointer() {
+ return (((_data_[__offset + OFFSET_URG_POINTER] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_URG_POINTER + 1] & 0xff));
+ }
+
+
+ /**
+ * @return The TCP checksum.
+ */
+ public final int getTCPChecksum() {
+ return (((_data_[__offset + OFFSET_TCP_CHECKSUM] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_TCP_CHECKSUM + 1] & 0xff));
+ }
+
+
+ /**
+ * @return The TCP packet length in bytes. This is the size of the
+ * IP packet minus the size of the IP header.
+ */
+ public final int getTCPPacketByteLength() {
+ return getIPPacketLength() - __offset;
+ }
+
+
+ /**
+ * @return The IP header length plus the TCP header length in bytes.
+ */
+ public final int getCombinedHeaderByteLength() {
+ return __offset + getTCPHeaderByteLength();
+ }
+
+
+ /**
+ * Sets the length of the TCP data payload.
+ *
+ * @param length The length of the TCP data payload in bytes.
+ */
+ public final void setTCPDataByteLength(int length) {
+ if(length < 0)
+ length = 0;
+
+ setIPPacketLength(getCombinedHeaderByteLength() + length);
+ }
+
+
+ public final int getTCPDataByteLength() {
+ return getIPPacketLength() - getCombinedHeaderByteLength();
+ }
+
+
+ private final int __getVirtualHeaderTotal() {
+ int s1 =
+ ((_data_[OFFSET_SOURCE_ADDRESS] & 0xff) << 8) |
+ (_data_[OFFSET_SOURCE_ADDRESS + 1] & 0xff);
+ int s2 =
+ ((_data_[OFFSET_SOURCE_ADDRESS + 2] & 0xff) << 8) |
+ (_data_[OFFSET_SOURCE_ADDRESS + 3] & 0xff);
+ int d1 =
+ ((_data_[OFFSET_DESTINATION_ADDRESS] & 0xff) << 8) |
+ (_data_[OFFSET_DESTINATION_ADDRESS + 1] & 0xff);
+ int d2 =
+ ((_data_[OFFSET_DESTINATION_ADDRESS + 2] & 0xff) << 8) |
+ (_data_[OFFSET_DESTINATION_ADDRESS + 3] & 0xff);
+ return s1 + s2 + d1 + d2 + getProtocol() + getTCPPacketByteLength();
+ }
+
+
+ /**
+ * Computes the TCP checksum, optionally updating the TCP checksum header.
+ *
+ * @param update Specifies whether or not to update the TCP checksum
+ * header after computing the checksum. A value of true indicates
+ * the header should be updated, a value of false indicates it
+ * should not be updated.
+ * @return The computed TCP checksum.
+ */
+ public final int computeTCPChecksum(boolean update) {
+ return _computeChecksum_(__offset, __offset + OFFSET_TCP_CHECKSUM,
+ getIPPacketLength(), __getVirtualHeaderTotal(),
+ update);
+ }
+
+
+ /**
+ * Same as computeTCPChecksum(true);
+ *
+ * @return The computed TCP checksum value.
+ */
+ public final int computeTCPChecksum() {
+ return computeTCPChecksum(true);
+ }
+}
diff --git a/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/UDPPacket.java b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/UDPPacket.java
new file mode 100755
index 00000000..360e5f3c
--- /dev/null
+++ b/ipscan/ext/vserv-tcpip/src/java/org/savarese/vserv/tcpip/UDPPacket.java
@@ -0,0 +1,252 @@
+/*
+ * $Id: UDPPacket.java 5347 2005-05-25 22:45:54Z dfs $
+ *
+ * Copyright 2005 Daniel F. Savarese
+ * Contact Information: http://www.savarese.org/contact.html
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.savarese.org/software/ApacheLicense-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.savarese.vserv.tcpip;
+
+/**
+ * UDPPacket extends {@link IPPacket} to handle UDP packets. The UDP
+ * packet structure is described in
+ * RFC 768.
+ *
+ * @author Daniel F. Savarese
+ */
+
+public class UDPPacket extends IPPacket {
+
+ /** Offset into the UDP packet of the source port header value. */
+ public static final int OFFSET_SOURCE_PORT = 0;
+
+ /** Offset into the UDP packet of the destination port header value. */
+ public static final int OFFSET_DESTINATION_PORT = 2;
+
+ /** Offset into the UDP packet of UDP total packet length header value. */
+ public static final int OFFSET_UDP_TOTAL_LENGTH = 4;
+
+ /** Offset into the UDP packet of the UDP checksum. */
+ public static final int OFFSET_UDP_CHECKSUM = 6;
+
+ /** Length of the UDP packet header in bytes. */
+ public static final int LENGTH_UDP_HEADER = 8;
+
+ /** The byte offset into the IP packet where the UDP packet begins. */
+ private int __offset;
+
+ /**
+ * Creates a new UDP packet of a given size.
+ *
+ * @param size The number of bytes in the packet.
+ */
+ public UDPPacket(int size) {
+ super(size);
+ __offset = 0;
+ }
+
+
+ /**
+ * Creates a new UDP packet that is a copy of a given packet.
+ *
+ * @param packet The packet to replicate.
+ */
+ public UDPPacket(UDPPacket packet) {
+ super(packet.size());
+ copy(packet);
+ __offset = packet.__offset;
+ }
+
+
+ /**
+ * Copies the contents of a UDPPacket. If the current data array is
+ * of insufficient length to store the contents, a new array is
+ * allocated.
+ *
+ * @param packet The UDPPacket to copy.
+ */
+ public final void copyData(UDPPacket packet) {
+ if(_data_.length < packet._data_.length) {
+ byte[] data = new byte[packet._data_.length];
+ System.arraycopy(_data_, 0, data, 0, getCombinedHeaderByteLength());
+ _data_ = data;
+ }
+ int length = packet.getUDPDataByteLength();
+ System.arraycopy(packet._data_, packet.getCombinedHeaderByteLength(),
+ _data_, getCombinedHeaderByteLength(), length);
+ setUDPDataByteLength(length);
+ }
+
+
+ public void setData(byte[] data) {
+ super.setData(data);
+ __offset = getIPHeaderByteLength();
+ }
+
+
+ /**
+ * Sets the source port.
+ *
+ * @param port The new source port.
+ */
+ public final void setSourcePort(int port) {
+ _data_[__offset + OFFSET_SOURCE_PORT] = (byte)((port >> 8) & 0xff);
+ _data_[__offset + OFFSET_SOURCE_PORT + 1] = (byte)(port & 0xff);
+ }
+
+
+ /**
+ * Sets the destination port.
+ *
+ * @param port The new destination port.
+ */
+ public final void setDestinationPort(int port) {
+ _data_[__offset + OFFSET_DESTINATION_PORT] = (byte)((port >> 8) & 0xff);
+ _data_[__offset + OFFSET_DESTINATION_PORT + 1] = (byte)(port & 0xff);
+ }
+
+
+ /**
+ * @return The source port.
+ */
+ public final int getSourcePort() {
+ return (((_data_[__offset + OFFSET_SOURCE_PORT] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_SOURCE_PORT + 1] & 0xff));
+ }
+
+
+ /**
+ * @return The destination port.
+ */
+ public final int getDestinationPort() {
+ return (((_data_[__offset + OFFSET_DESTINATION_PORT] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_DESTINATION_PORT + 1] & 0xff));
+ }
+
+
+ public void setIPHeaderLength(int length) {
+ super.setIPHeaderLength(length);
+ __offset = getIPHeaderByteLength();
+ }
+
+
+ /**
+ * Sets the UDP total length header field.
+ *
+ * @param length The length of the UDP packet in bytes.
+ */
+ public void setUDPPacketLength(int length) {
+ _data_[__offset + OFFSET_UDP_TOTAL_LENGTH] = (byte)((length >> 8) & 0xff);
+ _data_[__offset + OFFSET_UDP_TOTAL_LENGTH + 1] = (byte)(length & 0xff);
+ }
+
+
+ /**
+ * @return The value of the UDP total length header field.
+ */
+ public final int getUDPPacketLength() {
+ return (((_data_[__offset + OFFSET_UDP_TOTAL_LENGTH] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_UDP_TOTAL_LENGTH + 1] & 0xff));
+ }
+
+
+ /**
+ * @return The UDP checksum.
+ */
+ public final int getUDPChecksum() {
+ return (((_data_[__offset + OFFSET_UDP_CHECKSUM] & 0xff) << 8) |
+ (_data_[__offset + OFFSET_UDP_CHECKSUM + 1] & 0xff));
+ }
+
+
+ /**
+ * @return The UDP packet length in bytes. This is the size of the
+ * IP packet minus the size of the IP header. Normally, you want
+ * this to equal the length stored in the UDP header
+ * (see {@link #getUDPPacketLength}).
+ */
+ public final int getUDPPacketByteLength() {
+ return getIPPacketLength() - __offset;
+ }
+
+
+ /**
+ * @return The IP header length plus the UDP header length in bytes.
+ */
+ public final int getCombinedHeaderByteLength() {
+ return __offset + LENGTH_UDP_HEADER;
+ }
+
+
+ /**
+ * Sets the length of the UDP data payload.
+ *
+ * @param length The length of the UDP data payload in bytes.
+ */
+ public final void setUDPDataByteLength(int length) {
+ if(length < 0)
+ length = 0;
+
+ setIPPacketLength(getCombinedHeaderByteLength() + length);
+ }
+
+
+ public final int getUDPDataByteLength() {
+ return getIPPacketLength() - getCombinedHeaderByteLength();
+ }
+
+
+ private final int __getVirtualHeaderTotal() {
+ int s1 =
+ ((_data_[OFFSET_SOURCE_ADDRESS] & 0xff) << 8) |
+ (_data_[OFFSET_SOURCE_ADDRESS + 1] & 0xff);
+ int s2 =
+ ((_data_[OFFSET_SOURCE_ADDRESS + 2] & 0xff) << 8) |
+ (_data_[OFFSET_SOURCE_ADDRESS + 3] & 0xff);
+ int d1 =
+ ((_data_[OFFSET_DESTINATION_ADDRESS] & 0xff) << 8) |
+ (_data_[OFFSET_DESTINATION_ADDRESS + 1] & 0xff);
+ int d2 =
+ ((_data_[OFFSET_DESTINATION_ADDRESS + 2] & 0xff) << 8) |
+ (_data_[OFFSET_DESTINATION_ADDRESS + 3] & 0xff);
+ return s1 + s2 + d1 + d2 + getProtocol() + getUDPPacketByteLength();
+ }
+
+
+ /**
+ * Computes the UDP checksum, optionally updating the UDP checksum header.
+ *
+ * @param update Specifies whether or not to update the UDP checksum
+ * header after computing the checksum. A value of true indicates
+ * the header should be updated, a value of false indicates it
+ * should not be updated.
+ * @return The computed UDP checksum.
+ */
+ public final int computeUDPChecksum(boolean update) {
+ return _computeChecksum_(__offset, __offset + OFFSET_UDP_CHECKSUM,
+ getIPPacketLength(), __getVirtualHeaderTotal(),
+ update);
+ }
+
+
+ /**
+ * Same as computeUDPChecksum(true);
+ *
+ * @return The computed UDP checksum value.
+ */
+ public final int computeUDPChecksum() {
+ return computeUDPChecksum(true);
+ }
+}
diff --git a/ipscan/resources/Labels.txt b/ipscan/resources/Labels.txt
new file mode 100755
index 00000000..52cbf4c8
--- /dev/null
+++ b/ipscan/resources/Labels.txt
@@ -0,0 +1,189 @@
+icon=images/icon.gif
+encoding=ISO-8859-1
+menu.file=&File
+menu.file.saveAll=Export &results... Ctrl+S
+menu.file.saveSelection=Export &selected results...
+menu.file.exportOptions=Export &options...
+menu.file.importOptions=&Import options...
+menu.file.exit=E&xit Alt+F4
+menu.goto=&Go to
+menu.goto.aliveHost=Next alive &host Ctrl+Shift+H
+menu.goto.deadHost=Next d&ead host Ctrl+Shift+D
+menu.goto.openPort=Next open &port Ctrl+Shift+P
+menu.goto.find=Find... Ctrl+F
+menu.commands=&Commands
+menu.commands.details=&Show details Dbl-Clk
+menu.commands.rescan=&Rescan IP(s) Ctrl+R
+menu.commands.delete=&Delete IP(s) Del
+menu.commands.copy=&Copy IP Ctrl+C
+menu.commands.copyDetails=Co&py details
+menu.commands.show=Show
+menu.commands.open=Open
+menu.commands.open.edit=Edit openers...
+menu.favorites=Fa&vorites
+menu.favorites.add=Add current... Ctrl+D
+menu.favorites.edit=Manage favorites...
+menu.tools=&Tools
+menu.tools.options=&Options... Ctrl+O
+menu.tools.fetchers=Select &fetchers...
+menu.tools.delete=Delete from list
+menu.tools.lastInfo=Show last scan &info Ctrl+I
+menu.help=&Help
+menu.help.gettingStarted=Getting &Started F1
+menu.help.website=Official Website
+menu.help.forum=Forum
+menu.help.plugins=Download plugins
+menu.help.cmdLine=Command-line usage
+menu.help.checkVersion=Check for newer version...
+menu.help.about=&About... F12
+menu.columns.sortBy=Sort by
+menu.columns.sortDirection=Change sort direction
+menu.columns.info=Fetcher info
+menu.columns.options=Fetcher options
+state.ready=Ready
+state.scanning=Scanning
+state.waitForThreads=Wait for all threads to terminate...
+state.killingThreads=Killing all threads...
+state.saving=Exporting results...
+state.searching=Searching...
+state.opening=Opening
+state.retrievingVersion=Retrieving the latest version...
+title.about=About
+title.options=Options
+title.options.scanning=Scanning
+title.options.display=Display
+title.options.fetchers=Fetchers
+title.options.ports=Ports
+title.details=IP address details
+title.saveAll=Export All Results
+title.saveSelection=Export Selected Results
+title.gettingStarted=Getting Started
+title.favorite.add=Add a favorite
+title.favorite.edit=Edit favorites
+title.openers.edit=Edit Openers
+title.fetchers.select=Select Fetchers
+title.find=Find
+text.ip=IP
+text.threads=Threads:
+text.favorite.add=Enter the name of the new favorite
+text.favorite.edit=Below you can rearrange or delete favorites
+text.find=Enter the text to search for
+text.find.notFound=Nothing was found.
+text.find.restart=Would you like to start from the beginning?
+text.version.latest=You are running the latest version.
+text.version.old=The latest version is %LATEST, but you are running %VERSION.
+text.openers.edit=Below you can edit or add new openers
+text.openers.name=Opener name (menu item):
+text.openers.string=Execution string:
+text.openers.directory=Working directory:
+text.openers.inTerminal=Run the program in terminal
+text.openers.new=New opener
+text.openers.hint=&Substitutions...
+text.openers.hintText=You may use any scanned values returned by fetchers in the execution string.\n\nThe following fetchers are currently available for substitution:\n\n
+text.fetchers.select=Here you can select fetchers for scanning. Fetchers are represented by columns.
+text.fetchers.selectedList=Selected fetchers
+text.fetchers.availableList=Available fetchers
+text.about=%NAME\n\nVersion %VERSION\n%COPYLEFT\n\n%WEBSITE\n%MAILTO\n\nThis is an Open Source Software released under the GPL.
+text.gettingStarted=Dummy
+text.gettingStarted1=Angry IP Scanner is an IP address scanner tool.\n\nIt is used for scanning IP addresses for finding alive hosts, gathering any kind of needed information about each host.
+text.gettingStarted2=Main terminology:\n\nFeeder - generator of IP addresses for scanning. Angry IP Scanner provides various kinds of feeders: IP Range, Random, and IP List File. You can select a feeder using the combo box next to the "Start" button.\n\nFetcher - gathers specific information about a host, e.g. ping time, hostname, open ports. Feeders usually represent columns in the scanning results list.
+button.OK=OK
+button.cancel=Cancel
+button.close=Close
+button.next=Next ->
+button.start.img=images/buttons/start.gif
+button.stop.img=images/buttons/stop.gif
+button.kill.img=images/buttons/kill.gif
+button.ipUp.img=images/buttons/ipup.gif
+button.up=&Up
+button.down=&Down
+button.delete=De&lete
+button.save=&Save
+button.insert=&Insert
+button.add=&Add
+button.left=<
+button.right=>
+combobox.feeder.tooltip=IP Feeder selection. Change this if you need another source for IP addresses to scan
+list.unknown.img=images/list/unknown.gif
+list.dead.img=images/list/dead.gif
+list.alive.img=images/list/alive.gif
+list.addinfo.img=images/list/addinfo.gif
+pinger.icmp=ICMP Echo
+pinger.icmp2=ICMP Echo (Alternative)
+pinger.udp=UDP packet
+pinger.tcp=TCP port probe
+opener.web=Web Browser
+opener.ftp=FTP
+opener.telnet=Telnet
+opener.ssh=SSH
+opener.netbios=Windows Shares
+opener.email=E-mail sample
+feeder.range=IP Range
+feeder.range.startIP=IP Range:
+feeder.range.endIP=to
+feeder.range.netmask=Netmask
+feeder.range.netmask.tooltip=Netmask of the IP range. Use either number of bits (e.g. /24) or the dotted notation (.255. = ..)
+feeder.range.hostname=Hostname:
+feeder.range.hostname.tooltip=Use this field to resolve hostnames to IP addresses
+feeder.file=IP List File
+feeder.file.name=Filename
+feeder.file.browse=Browse...
+feeder.random=Random
+feeder.random.prototype=Base IP:
+feeder.random.mask=IP Mask:
+feeder.random.hostname=Hostname:
+feeder.random.count=Count:
+fetcher.ip=IP
+fetcher.ping=Ping
+fetcher.ping.ttl=TTL
+fetcher.hostname=Hostname
+fetcher.ports=Ports
+fetcher.ports.filtered=Filtered Ports
+fetcher.value.ms= ms
+fetcher.value.notAvailable=[n/a]
+fetcher.value.notScanned=[n/s]
+options.threads=Threads
+options.threads.delay=Delay between starting threads (in ms):
+options.threads.maxThreads=Maximum number of threads:
+options.pinging.deadHosts=Scan dead hosts, which don't reply to pings
+options.pinging=Pinging
+options.pinging.type=Pinging method:
+options.pinging.count=Number of ping probes (packets to send):
+options.pinging.timeout=Ping timeout (in ms):
+options.broadcast=Broadcast
+options.broadcast.skip=Skip likely broadcast IP addresses
+options.fetchers.info=Here you can change options, specific to fetchers
+options.ports.timing=Timing
+options.ports.timing.timeout=Default port connect timeout (in ms):
+options.ports.timing.adaptTimeout=Adapt timeout to ping roundtrip time (if available)
+options.ports.ports=Port selection
+options.ports.portsDescription=Specify ports to scan here. Ranges are supported.\nExample: 1-3,5,7,10-15,6000-6100\nIf many ports are specified, scanning can take a lot of time.
+exporter.txt=Text file (txt)
+exporter.txt.generated=Generated by
+exporter.txt.scanned=Scanned %INFO
+exporter.csv=Comma-separated file (csv)
+exporter.xml=XML file
+exporter.ipList=IP:List file (lst)
+exception.FeederException.invalidNetmask=Invalid netmask specified. Must be in the A.B.C.D format
+exception.FeederException.invalidHostname=Invalid or inexistent hostname specified
+exception.FeederException.malformedIP=Malformed IP address specified, it should look like A.B.C.D
+exception.FeederException.range.greaterThan=The starting IP should be lower than the ending IP
+exception.FeederException.random.invalidCount=Random address count must be greater than 0
+exception.FeederException.file.notExists=Specified file doesn't exist or you don't have permissions to read it
+exception.FeederException.file.errorWhileReading=Error while reading the file
+exception.FeederException.file.nothingFound=No IP addresses found in the file
+exception.ExporterException.failed=Exporting failed
+exception.ExporterException.exporter.unknown=Unknown file type, please specify correct extension in the file name.
+exception.ExporterException.xml.noAppend=Appending to XML files is not supported.
+exception.ExporterException.fetcher.notFound=Not enough data in the scanning results to export to this file type.
+exception.UserErrorException.openURL.failed=Unable to launch your default browser, sorry.\nURL:
+exception.UserErrorException.openTerminal.failed=Unable to launch the terminal, sorry\n
+exception.UserErrorException.opener.failed=Unable to launch an external process, sorry.\nCommand-line:
+exception.UserErrorException.opener.unknownFetcher=The referenced fetcher cannot be resolved in the current scanning result. Cannot execute the opener with parameter:
+exception.UserErrorException.opener.nullFetcherValue=The replacement value of the fetcher is empty in the scanning results. Cannot execute the opener with parameter:
+exception.UserErrorException.opener.edit.noSelection=Please select the position where do you want to save your opener and or the Insert button to add a new one.
+exception.UserErrorException.commands.noSelection=No IP address selected
+exception.UserErrorException.commands.noResults=No scanning results available yet, please perform a scan first
+exception.UserErrorException.favorite.alreadyExists=A favorite with the same name already exists, please try a different one
+exception.UserErrorException.version.latestFailed=Failed to retrieve the latest version. Please visit the website manually.
+exception.OutOfMemoryError=Out Of Memory. The amount of available to the program heap memory has been exceeded.\nPlease increase the maximum heap size for this program.
diff --git a/ipscan/resources/images/buttons/ipup.gif b/ipscan/resources/images/buttons/ipup.gif
new file mode 100755
index 00000000..f50fe3c9
Binary files /dev/null and b/ipscan/resources/images/buttons/ipup.gif differ
diff --git a/ipscan/resources/images/buttons/kill.gif b/ipscan/resources/images/buttons/kill.gif
new file mode 100755
index 00000000..f015e163
Binary files /dev/null and b/ipscan/resources/images/buttons/kill.gif differ
diff --git a/ipscan/resources/images/buttons/start.gif b/ipscan/resources/images/buttons/start.gif
new file mode 100755
index 00000000..4c542428
Binary files /dev/null and b/ipscan/resources/images/buttons/start.gif differ
diff --git a/ipscan/resources/images/buttons/stop.gif b/ipscan/resources/images/buttons/stop.gif
new file mode 100755
index 00000000..0dd6a1f2
Binary files /dev/null and b/ipscan/resources/images/buttons/stop.gif differ
diff --git a/ipscan/resources/images/icon.gif b/ipscan/resources/images/icon.gif
new file mode 100755
index 00000000..a2f0ab65
Binary files /dev/null and b/ipscan/resources/images/icon.gif differ
diff --git a/ipscan/resources/images/list/addinfo.gif b/ipscan/resources/images/list/addinfo.gif
new file mode 100755
index 00000000..c4e77cc9
Binary files /dev/null and b/ipscan/resources/images/list/addinfo.gif differ
diff --git a/ipscan/resources/images/list/alive.gif b/ipscan/resources/images/list/alive.gif
new file mode 100755
index 00000000..aa82c7c7
Binary files /dev/null and b/ipscan/resources/images/list/alive.gif differ
diff --git a/ipscan/resources/images/list/dead.gif b/ipscan/resources/images/list/dead.gif
new file mode 100755
index 00000000..10c774b3
Binary files /dev/null and b/ipscan/resources/images/list/dead.gif differ
diff --git a/ipscan/resources/images/list/unknown.gif b/ipscan/resources/images/list/unknown.gif
new file mode 100755
index 00000000..e09f1c5f
Binary files /dev/null and b/ipscan/resources/images/list/unknown.gif differ
diff --git a/ipscan/src/net/azib/ipscan/Main.java b/ipscan/src/net/azib/ipscan/Main.java
new file mode 100755
index 00000000..55605020
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/Main.java
@@ -0,0 +1,120 @@
+/**
+ *
+ */
+package net.azib.ipscan;
+
+import java.security.Security;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.GUIComponentContainer;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.gui.MainWindow;
+import net.azib.ipscan.gui.UserErrorException;
+
+/**
+ * The main executable class.
+ * It initializes all the needed stuff and launches the user interface.
+ *
+ * All Exceptions, which are thrown out of the program, are catched and logged
+ * using the java.util.logging facilities.
+ *
+ * @author anton
+ */
+public class Main {
+
+ public static void main(String[] args) {
+
+ Logger.global.setLevel(Level.CONFIG);
+
+ initProperties();
+
+ Display display = Display.getDefault();
+
+ // initalize Labels instance
+ Labels.initialize(new Locale("en")); // TODO: retrieve locale normally
+
+ // initialize Config instance
+ Config.initialize();
+
+ // create the main window using dependency injection
+ MainWindow mainWindow = new GUIComponentContainer().createMainWindow();
+
+ while (!mainWindow.isDisposed()) {
+ try {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ catch (Throwable e) {
+ // display a nice error message
+ String localizedMessage = getLocalizedMessage(e);
+ Shell parent = display.getActiveShell();
+ MessageBox messageBox = new MessageBox(parent != null ? parent : mainWindow.getShell(), SWT.OK | SWT.ICON_ERROR);
+ messageBox.setText("Error");
+ messageBox.setMessage(localizedMessage);
+ messageBox.open();
+ }
+ }
+
+ // save config on exit
+ Config.store();
+
+ // dispose the native objects
+ display.dispose();
+ }
+
+ private static void initProperties() {
+ // currently we support IPv4 only
+ System.setProperty("java.net.preferIPv4Stack", "true");
+ // disable DNS caches
+ Security.setProperty("networkaddress.cache.ttl", "0");
+ Security.setProperty("networkaddress.cache.negative.ttl", "0");
+ }
+
+ /**
+ * Returns a nice localized message for the passed exception
+ * in case it is possible, or toString() otherwise.
+ */
+ static String getLocalizedMessage(Throwable e) {
+ String localizedMessage;
+ try {
+ // try to load localized message
+ if (e instanceof UserErrorException) {
+ localizedMessage = e.getMessage();
+ }
+ else {
+ String exceptionClassName = getClassShortName(e.getClass());
+ String originalMessage = e.getMessage();
+ localizedMessage = Labels.getLabel("exception." + exceptionClassName + (originalMessage != null ? "." + originalMessage : ""));
+ }
+ // add cause summary, if it exists
+ if (e.getCause() != null) {
+ localizedMessage += "\n\n" + e.getCause().toString();
+ }
+ Logger.global.log(Level.FINE, "error", e);
+ }
+ catch (Exception e2) {
+ // fallback to default text
+ localizedMessage = e.toString();
+ // output stack trace to the console
+ Logger.global.log(Level.SEVERE, "unexpected error", e);
+ }
+ return localizedMessage;
+ }
+
+ /**
+ * @return the short name of a class (without package name)
+ */
+ static String getClassShortName(Class clazz) {
+ String className = clazz.getName();
+ return className.substring(className.lastIndexOf('.') + 1);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/Config.java b/ipscan/src/net/azib/ipscan/config/Config.java
new file mode 100755
index 00000000..f88732af
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/Config.java
@@ -0,0 +1,80 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+import java.util.prefs.Preferences;
+
+/**
+ * This class encapsulates configuration options of the program.
+ * It is a singleton class.
+ *
+ * @author anton
+ */
+public final class Config {
+
+ private static Preferences preferences;
+
+ /** easily accessible global configuration */
+ private static GlobalConfig globalConfig;
+ /** favorites are stored here */
+ private static NamedListConfig favoritesConfig;
+ /** openers are stored here */
+ private static OpenersConfig openersConfig;
+ /** various dimensions are stored here */
+ private static DimensionsConfig dimensionsConfig;
+
+ private Config() {
+ }
+
+ /**
+ * Initializes the singleton instance
+ */
+ public static void initialize() {
+ preferences = Preferences.userRoot().node("ipscan");
+ globalConfig = new GlobalConfig();
+ favoritesConfig = new FavoritesConfig();
+ openersConfig = new OpenersConfig();
+ dimensionsConfig = new DimensionsConfig();
+ }
+
+ public static void store() {
+ globalConfig.store();
+ favoritesConfig.store();
+ openersConfig.store();
+ dimensionsConfig.store();
+ }
+
+ public static Preferences getPreferences() {
+ return preferences;
+ }
+
+ /**
+ * @return GlobalConfig instance (quick access)
+ */
+ public static GlobalConfig getGlobal() {
+ return globalConfig;
+ }
+
+ /**
+ * @return Favorites config (quick access);
+ */
+ public static NamedListConfig getFavoritesConfig() {
+ return favoritesConfig;
+ }
+
+ /**
+ * @return Openers config (quick access);
+ */
+ public static OpenersConfig getOpenersConfig() {
+ return openersConfig;
+ }
+
+ /**
+ * @return Dimensions config (quick access);
+ */
+ public static DimensionsConfig getDimensionsConfig() {
+ return dimensionsConfig;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/DimensionsConfig.java b/ipscan/src/net/azib/ipscan/config/DimensionsConfig.java
new file mode 100755
index 00000000..49e52056
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/DimensionsConfig.java
@@ -0,0 +1,96 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+import java.util.prefs.Preferences;
+
+import org.eclipse.swt.graphics.Rectangle;
+
+/**
+ * DimensionsConfig
+ *
+ * @author anton
+ */
+public class DimensionsConfig {
+
+ private Preferences preferences;
+
+ public int windowHeight;
+ public int windowWidth;
+ public int windowTop;
+ public int windowLeft;
+ public boolean isWindowMaximized;
+
+ // package local constructor
+ DimensionsConfig() {
+ preferences = Config.getPreferences();
+ load();
+ }
+
+ /**
+ * This constructor is for tests
+ * @param preferences
+ */
+ DimensionsConfig(Preferences preferences) {
+ this.preferences = preferences;
+ load();
+ }
+
+ private void load() {
+ windowHeight = preferences.getInt("windowHeight", 350);
+ windowWidth = preferences.getInt("windowWidth", 560);
+ windowTop = preferences.getInt("windowTop", 100);
+ windowLeft = preferences.getInt("windowLeft", 100);
+ isWindowMaximized = preferences.getBoolean("windowMaximized", false);
+ }
+
+ public void store() {
+ if (!isWindowMaximized) {
+ preferences.putInt("windowHeight", windowHeight);
+ preferences.putInt("windowWidth", windowWidth);
+ preferences.putInt("windowTop", windowTop);
+ preferences.putInt("windowLeft", windowLeft);
+ }
+ preferences.putBoolean("windowMaximized", isWindowMaximized);
+ }
+
+ /**
+ * @return
+ */
+ public Rectangle getWindowBounds() {
+ return new Rectangle(windowLeft, windowTop, windowWidth, windowHeight);
+ }
+
+ /**
+ * @param bounds
+ * @param isMaximized
+ */
+ public void setWindowBounds(Rectangle bounds, boolean isMaximized) {
+ if (!isMaximized) {
+ windowTop = bounds.y;
+ windowLeft = bounds.x;
+ windowHeight = bounds.height;
+ windowWidth = bounds.width;
+ }
+ isWindowMaximized = isMaximized;
+ }
+
+ /**
+ * @param fetcherName
+ * @return column width corresponding to a fetcher
+ */
+ public int getColumnWidth(String fetcherName) {
+ return preferences.getInt("columnWidth." + fetcherName, 90);
+ }
+
+ /**
+ * Persist the width of a column corresponding to a fetcher
+ * @param fetcherName
+ * @param width
+ */
+ public void setColumnWidth(String fetcherName, int width) {
+ preferences.putInt("columnWidth." + fetcherName, width);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/FavoritesConfig.java b/ipscan/src/net/azib/ipscan/config/FavoritesConfig.java
new file mode 100755
index 00000000..90080a90
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/FavoritesConfig.java
@@ -0,0 +1,17 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+/**
+ * FavoritesConfig
+ *
+ * @author anton
+ */
+public class FavoritesConfig extends NamedListConfig {
+
+ public FavoritesConfig() {
+ super("favorites");
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/GUIComponentContainer.java b/ipscan/src/net/azib/ipscan/config/GUIComponentContainer.java
new file mode 100755
index 00000000..f9849f69
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/GUIComponentContainer.java
@@ -0,0 +1,161 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+import net.azib.ipscan.core.Scanner;
+import net.azib.ipscan.core.ScannerThreadFactory;
+import net.azib.ipscan.core.ScanningResultList;
+import net.azib.ipscan.core.net.PingerRegistry;
+import net.azib.ipscan.core.net.PingerRegistryImpl;
+import net.azib.ipscan.exporters.CSVExporter;
+import net.azib.ipscan.exporters.ExporterRegistry;
+import net.azib.ipscan.exporters.IPListExporter;
+import net.azib.ipscan.exporters.TXTExporter;
+import net.azib.ipscan.exporters.XMLExporter;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+import net.azib.ipscan.fetchers.FetcherRegistryImpl;
+import net.azib.ipscan.fetchers.FilteredPortsFetcher;
+import net.azib.ipscan.fetchers.HostnameFetcher;
+import net.azib.ipscan.fetchers.IPFetcher;
+import net.azib.ipscan.fetchers.PingFetcher;
+import net.azib.ipscan.fetchers.PingTTLFetcher;
+import net.azib.ipscan.fetchers.PortsFetcher;
+import net.azib.ipscan.gui.MainMenu;
+import net.azib.ipscan.gui.MainWindow;
+import net.azib.ipscan.gui.OptionsWindow;
+import net.azib.ipscan.gui.ResultTable;
+import net.azib.ipscan.gui.SelectFetchersDialog;
+import net.azib.ipscan.gui.StatusBar;
+import net.azib.ipscan.gui.MainMenu.CommandsMenu;
+import net.azib.ipscan.gui.actions.ColumnsActions;
+import net.azib.ipscan.gui.actions.OpenerLauncher;
+import net.azib.ipscan.gui.actions.StartStopScanningAction;
+import net.azib.ipscan.gui.feeders.FeederGUIRegistry;
+import net.azib.ipscan.gui.feeders.FileFeederGUI;
+import net.azib.ipscan.gui.feeders.RandomFeederGUI;
+import net.azib.ipscan.gui.feeders.RangeFeederGUI;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.picocontainer.MutablePicoContainer;
+import org.picocontainer.Parameter;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.defaults.ComponentParameter;
+import org.picocontainer.defaults.ConstantParameter;
+import org.picocontainer.defaults.DefaultPicoContainer;
+
+/**
+ * This class is a dependency injection configuration
+ * using the Pico Container.
+ *
+ * @author anton
+ */
+public class GUIComponentContainer {
+
+ private PicoContainer container;
+
+ public GUIComponentContainer() {
+ MutablePicoContainer container = new DefaultPicoContainer();
+ this.container = container;
+
+ ComponentParameter anyComponentParameter = new ComponentParameter();
+
+ // non-GUI TODO: move to a separate container
+ container.registerComponentImplementation(ExporterRegistry.class);
+ container.registerComponentImplementation(TXTExporter.class);
+ container.registerComponentImplementation(CSVExporter.class);
+ container.registerComponentImplementation(XMLExporter.class);
+ container.registerComponentImplementation(IPListExporter.class);
+
+ container.registerComponentImplementation(FetcherRegistry.class, FetcherRegistryImpl.class);
+ container.registerComponentImplementation(IPFetcher.class);
+ container.registerComponentImplementation(PingFetcher.class);
+ container.registerComponentImplementation(PingTTLFetcher.class);
+ container.registerComponentImplementation(HostnameFetcher.class);
+ container.registerComponentImplementation(PortsFetcher.class);
+ container.registerComponentImplementation(FilteredPortsFetcher.class);
+
+ container.registerComponentImplementation(PingerRegistry.class, PingerRegistryImpl.class);
+ container.registerComponentImplementation(ScanningResultList.class);
+ container.registerComponentImplementation(Scanner.class);
+ container.registerComponentImplementation(ScannerThreadFactory.class);
+
+ // GUI follows
+
+ // Some "shared" GUI components
+ container.registerComponentImplementation("mainShell", Shell.class);
+ container.registerComponentImplementation("mainMenu", Menu.class, new Parameter[] {
+ new ComponentParameter("mainShell"),
+ new ConstantParameter(new Integer(SWT.BAR))});
+ container.registerComponentImplementation("commandsMenu", CommandsMenu.class);
+
+ container.registerComponentImplementation("feederArea", Composite.class, new Parameter[] {
+ new ComponentParameter("mainShell"),
+ new ConstantParameter(new Integer(SWT.NONE))});
+ container.registerComponentImplementation("controlsArea", Composite.class, new Parameter[] {
+ new ComponentParameter("mainShell"),
+ new ConstantParameter(new Integer(SWT.NONE))});
+ container.registerComponentImplementation("startStopButton", Button.class, new Parameter[] {
+ new ComponentParameter("controlsArea"),
+ new ConstantParameter(new Integer(SWT.NONE))});
+ container.registerComponentImplementation("feederSelectionCombo", Combo.class, new Parameter[] {
+ new ComponentParameter("controlsArea"),
+ new ConstantParameter(new Integer(SWT.READ_ONLY))});
+
+ // GUI Feeders
+ container.registerComponentImplementation(FeederGUIRegistry.class);
+ Parameter[] feederGUIParameters = new Parameter[] {new ComponentParameter("feederArea")};
+ container.registerComponentImplementation(RangeFeederGUI.class, RangeFeederGUI.class, feederGUIParameters);
+ container.registerComponentImplementation(RandomFeederGUI.class, RandomFeederGUI.class, feederGUIParameters);
+ container.registerComponentImplementation(FileFeederGUI.class, FileFeederGUI.class, feederGUIParameters);
+
+ container.registerComponentImplementation(OpenerLauncher.class);
+ container.registerComponentImplementation(MainWindow.class, MainWindow.class, new Parameter[] {
+ new ComponentParameter("mainShell"),
+ new ComponentParameter("feederArea"),
+ new ComponentParameter("controlsArea"),
+ new ComponentParameter("feederSelectionCombo"),
+ new ComponentParameter("startStopButton"),
+ anyComponentParameter,
+ anyComponentParameter,
+ anyComponentParameter,
+ anyComponentParameter,
+ anyComponentParameter});
+ container.registerComponentImplementation(ResultTable.class, ResultTable.class, new Parameter[] {
+ new ComponentParameter("mainShell"),
+ anyComponentParameter,
+ anyComponentParameter,
+ anyComponentParameter});
+ container.registerComponentImplementation(StatusBar.class, StatusBar.class, new Parameter[] {
+ new ComponentParameter("mainShell")});
+
+ container.registerComponentImplementation(MainMenu.class, MainMenu.class, new Parameter[] {
+ new ComponentParameter("mainShell"),
+ new ComponentParameter("mainMenu"),
+ new ComponentParameter("commandsMenu"),
+ new ConstantParameter(container)});
+ container.registerComponentImplementation(MainMenu.ColumnsMenu.class, MainMenu.ColumnsMenu.class, new Parameter[] {
+ new ComponentParameter("mainShell"),
+ anyComponentParameter});
+
+ container.registerComponentImplementation(OptionsWindow.class);
+ container.registerComponentImplementation(SelectFetchersDialog.class);
+
+ // various actions / listener
+ container.registerComponentImplementation(StartStopScanningAction.class);
+ container.registerComponentImplementation(ColumnsActions.SortBy.class);
+ }
+
+ public MainWindow createMainWindow() {
+ // initialize the main menu
+ container.getComponentInstance(MainMenu.class);
+ // initialize and return the main window
+ return (MainWindow) container.getComponentInstance(MainWindow.class);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/GlobalConfig.java b/ipscan/src/net/azib/ipscan/config/GlobalConfig.java
new file mode 100755
index 00000000..28606d33
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/GlobalConfig.java
@@ -0,0 +1,61 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+import java.util.prefs.Preferences;
+
+/**
+ * Global configuration holder.
+ *
+ * @author anton
+ */
+public final class GlobalConfig {
+
+ private static final String MAX_THREADS = "maxThreads";
+ private static final String THREAD_DELAY = "threadDelay";
+ private static final String ACTIVE_FEEDER = "activeFeeder";
+ private static final String SCAN_DEAD_HOSTS = "scanDeadHosts";
+ private static final String SELECTED_PINGER = "selectedPinger";
+ private static final String PING_TIMEOUT = "pingTimeout";
+ private static final String PING_COUNT = "pingCount";
+ private static final String SKIP_BROADCAST_ADDRESSES = "skipBroadcastAddresses";
+ private static final String PORT_TIMEOUT = "portTimeout";
+ private static final String ADAPT_PORT_TIMEOUT = "adaptPortTimeout";
+ private static final String PORT_STRING = "portString";
+
+ private Preferences preferences = Config.getPreferences();
+
+ public int maxThreads = preferences.getInt(MAX_THREADS, 100);
+ public int threadDelay = preferences.getInt(THREAD_DELAY, 20);
+ public int activeFeeder = preferences.getInt(ACTIVE_FEEDER, 0);
+ public boolean scanDeadHosts = preferences.getBoolean(SCAN_DEAD_HOSTS, false);
+ public String selectedPinger = preferences.get(SELECTED_PINGER, "pinger.icmp");
+ public int pingTimeout = preferences.getInt(PING_TIMEOUT, 3000);
+ public int pingCount = preferences.getInt(PING_COUNT, 3);
+ public boolean skipBroadcastAddresses = preferences.getBoolean(SKIP_BROADCAST_ADDRESSES, true);
+ public int portTimeout = preferences.getInt(PORT_TIMEOUT, 3000);;
+ public boolean adaptPortTimeout = preferences.getBoolean(ADAPT_PORT_TIMEOUT, true);
+ public String portString = preferences.get(PORT_STRING, "");
+
+ /**
+ * Stores all the internal properties to the storage media
+ */
+ public void store() {
+ preferences.putInt(MAX_THREADS, maxThreads);
+ preferences.putInt(THREAD_DELAY, threadDelay);
+ preferences.putInt(ACTIVE_FEEDER, activeFeeder);
+ preferences.putBoolean(SCAN_DEAD_HOSTS, scanDeadHosts);
+ preferences.put(SELECTED_PINGER, selectedPinger);
+ preferences.putInt(PING_TIMEOUT, pingTimeout);
+ preferences.putInt(PING_COUNT, pingCount);
+ preferences.putBoolean(SKIP_BROADCAST_ADDRESSES, skipBroadcastAddresses);
+ preferences.putInt(PORT_TIMEOUT, portTimeout);
+ preferences.putBoolean(ADAPT_PORT_TIMEOUT, adaptPortTimeout);
+ preferences.put(PORT_STRING, portString);
+ }
+
+ // package local constructor
+ GlobalConfig() {
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/config/Labels.java b/ipscan/src/net/azib/ipscan/config/Labels.java
new file mode 100755
index 00000000..84f98440
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/Labels.java
@@ -0,0 +1,98 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.PropertyResourceBundle;
+
+/**
+ * Labels class for localization, based on PropertyResourceBundle.
+ * It adds some special methods for loading of images by IDs.
+ *
+ * It is a singleton, so use getInstance() in order to use this class.
+ *
+ * Use initialize() to create an instance of this class.
+ *
+ * @author anton
+ */
+public final class Labels {
+
+ private static Labels instance;
+
+ PropertyResourceBundle labels;
+ Locale locale;
+
+ static {
+ // this is needed for Visual Editor to display
+ // labels at design time
+ initialize(new Locale("en"));
+ }
+
+ private Labels() {
+ // private constructor
+ }
+
+ public static final Labels getInstance() {
+ return instance;
+ }
+
+ /**
+ * Initialized the internal locale-specific data.
+ * The files Labels_LANG.txt and Labels.txt are searched for
+ * in the same package as this class.
+ * This method must be called prior to using this class.
+ */
+ public static void initialize(Locale locale) {
+ if (instance != null && locale.equals(instance.locale)) {
+ // do not reload locale, because it was already initialized in the static block
+ return;
+ }
+ // create a new instance
+ instance = new Labels();
+
+ instance.locale = locale;
+ InputStream labelsStream = null;
+ labelsStream = Labels.class.getClassLoader().getResourceAsStream("Labels_" + locale.getLanguage().toUpperCase() + ".txt");
+ if (labelsStream == null) {
+ labelsStream = Labels.class.getClassLoader().getResourceAsStream("Labels.txt");
+ }
+ if (labelsStream == null) {
+ throw new MissingResourceException("Labels not found!", Labels.class.getName(), "Labels");
+ }
+ try {
+ instance.labels = new PropertyResourceBundle(labelsStream);
+ }
+ catch (IOException e) {
+ throw new MissingResourceException(e.toString(), Labels.class.getName(), "Labels");
+ }
+ }
+
+ /**
+ * Retrieves an InputStream to load the image, specified by a key in resource file.
+ * @param key
+ */
+ public InputStream getImageAsStream(String key) {
+ String imagePath = labels.getString(key);
+ return getClass().getClassLoader().getResourceAsStream(imagePath);
+ }
+
+ /**
+ * Retrieves a String specified by the label key
+ * @param key
+ */
+ public String get(String key) {
+ return labels.getString(key);
+ }
+
+ /**
+ * A shortened form of Labels.getLabel()
+ */
+ public static String getLabel(String key) {
+ return getInstance().get(key);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/NamedListConfig.java b/ipscan/src/net/azib/ipscan/config/NamedListConfig.java
new file mode 100755
index 00000000..8c967c26
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/NamedListConfig.java
@@ -0,0 +1,117 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.prefs.Preferences;
+
+/**
+ * This is a generic named list config.
+ * Can be used for storing favorites, openers, and other
+ * user-defined configurations.
+ *
+ * @author anton
+ */
+public class NamedListConfig {
+
+ protected String preferenceName;
+ protected Preferences preferences = Config.getPreferences();
+ protected Map namedList = new LinkedHashMap();
+
+ // package local constructor
+ NamedListConfig(String preferenceName) {
+ this.preferenceName = preferenceName;
+ load();
+ }
+
+ /**
+ * This constructor is for tests
+ * @param preferences
+ */
+ NamedListConfig(Preferences preferences, String preferenceName) {
+ this.preferenceName = preferenceName;
+ this.preferences = preferences;
+ load();
+ }
+
+ /**
+ * Loads preferences
+ */
+ public void load() {
+ if (preferences == null) {
+ return;
+ }
+
+ String[] namedListPrefs = preferences.get(preferenceName, "").split("###");
+ for (int i = 0; i < namedListPrefs.length; i += 2) {
+ if (namedListPrefs[i].length() > 0) {
+ namedList.put(namedListPrefs[i], serializeValue(namedListPrefs[i+1]));
+ }
+ }
+ }
+
+ Object serializeValue(String value) {
+ return value;
+ }
+
+ /**
+ * Stores the currently available named list
+ */
+ public void store() {
+ StringBuffer sb = new StringBuffer(32);
+ for (Iterator i = namedList.entrySet().iterator(); i.hasNext();) {
+ Map.Entry e = (Map.Entry) i.next();
+ sb.append(e.getKey()).append("###").append(e.getValue()).append("###");
+ }
+ if (sb.length() > 3) {
+ sb.delete(sb.length() - 3, sb.length());
+ }
+ preferences.put(preferenceName, sb.toString());
+ }
+
+ /**
+ * @param name displayed to the user
+ * @param value to store according to the name
+ */
+ public void add(String name, Object value) {
+ namedList.put(name, value);
+ }
+
+ /**
+ * @param name name
+ * @return stored value
+ */
+ public String get(String name) {
+ return (String) namedList.get(name);
+ }
+
+ /**
+ * @return an Iterator for iterating names of available items
+ */
+ public Iterator iterateNames() {
+ return namedList.keySet().iterator();
+ }
+
+ public int size() {
+ return namedList.size();
+ }
+
+ /**
+ * Updates the list, retaining only items that are passed in the array.
+ * The order of elements will be the same as in the array.
+ *
+ * @param names
+ */
+ public void update(String[] names) {
+ // rebuild the map (to recreate the new order of elements)
+ Map newList = new LinkedHashMap();
+ for (int i = 0; i < names.length; i++) {
+ newList.put(names[i], namedList.get(names[i]));
+ }
+ namedList = newList;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/OpenersConfig.java b/ipscan/src/net/azib/ipscan/config/OpenersConfig.java
new file mode 100755
index 00000000..d74c8bc5
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/OpenersConfig.java
@@ -0,0 +1,87 @@
+/**
+ *
+ */
+package net.azib.ipscan.config;
+
+import java.io.File;
+import java.util.logging.Logger;
+import java.util.prefs.Preferences;
+
+/**
+ * OpenersConfig
+ *
+ * @author anton
+ */
+public class OpenersConfig extends NamedListConfig {
+
+ public OpenersConfig() {
+ super("openers");
+
+ if (size() == 0) {
+ boolean isWindows = System.getProperty("os.name").startsWith("Windows");
+
+ Labels labels = Labels.getInstance();
+ // add default openers
+ if (isWindows) add(labels.get("opener.netbios"), new Opener("\\\\${fetcher.ip}", false, null));
+ add(labels.get("opener.web"), new Opener("http://${fetcher.ip}/", false, null));
+ add(labels.get("opener.ftp"), new Opener("ftp://${fetcher.ip}/", false, null));
+ add(labels.get("opener.telnet"), new Opener("telnet ${fetcher.ip}", true, null));
+ if (!isWindows) add(labels.get("opener.ssh"), new Opener("ssh ${fetcher.ip}", true, null));
+ add(labels.get("opener.email"), new Opener("mailto:somebody@example.com?subject=IP: ${fetcher.ip}", true, null));
+ }
+ }
+
+ /**
+ * This constructor is for tests
+ * @param preferences
+ */
+ OpenersConfig(Preferences preferences) {
+ super(preferences, "openers");
+ }
+
+ Object serializeValue(String value) {
+ return new Opener(value);
+ }
+
+ public void add(String name, Object value) {
+ if (value instanceof Opener)
+ super.add(name, value);
+ else
+ // ensure only Openers are allowed here
+ throw new IllegalArgumentException();
+ }
+
+ public Opener getOpener(String name) {
+ return (Opener)namedList.get(name);
+ }
+
+ public static class Opener {
+ public String execString;
+ public boolean inTerminal;
+ public File workingDir;
+
+ Opener(String serialized) {
+ try {
+ String[] parts = serialized.split("@@@");
+ execString = parts[0];
+ inTerminal = parts[1].charAt(0) == '1';
+ workingDir = parts.length >= 3 && parts[2].length() > 0 ? new File(parts[2]) : null;
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ // this happens when broken settings have been loaded
+ Logger.global.fine("Broken opener config read: " + serialized);
+ }
+ }
+
+ public Opener(String execString, boolean inTerminal, File workingDir) {
+ this.execString = execString;
+ this.inTerminal = inTerminal;
+ this.workingDir = workingDir;
+ }
+
+ public String toString() {
+ return execString + "@@@" + (inTerminal ? '1' : '0') + "@@@" + (workingDir != null ? workingDir.toString() : "");
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/config/Version.java b/ipscan/src/net/azib/ipscan/config/Version.java
new file mode 100755
index 00000000..bab7ba72
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/config/Version.java
@@ -0,0 +1,30 @@
+/**
+ * This is a part of Angry IP Scanner source.
+ */
+package net.azib.ipscan.config;
+
+/**
+ * Class with accessors to version information of the program.
+ *
+ * @author anton
+ * @version $Id: Version.java,v 1.2 2005/11/20 14:08:28 angryziber Exp $
+ */
+public class Version {
+ public static final String NAME = "Angry IP Scanner";
+
+ public static final String VERSION = "2.9-alpha";
+
+ public static final String FULL_NAME = NAME + " " + VERSION;
+
+ public static final String COPYLEFT = "(C) 1998-2006 Anton Keks";
+
+ public static final String WEBSITE = "http://www.azib.net/ipscan/";
+
+ public static final String MAILTO = "support@azib.net";
+
+ public static final String FORUM_URL = "http://www.azib.net/ipscan/forum/";
+
+ public static final String PLUGINS_URL = "http://www.azib.net/ipscan/plugins/";
+
+ public static final String LATEST_VERSION_URL = "http://www.azib.net/ipscan/IPSCAN.VERSION";
+}
diff --git a/ipscan/src/net/azib/ipscan/core/InetAddressUtils.java b/ipscan/src/net/azib/ipscan/core/InetAddressUtils.java
new file mode 100755
index 00000000..96412f3a
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/InetAddressUtils.java
@@ -0,0 +1,160 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+/**
+ * This class provides various utility static methods,
+ * useful for transforming InetAddress objects.
+ *
+ * @author anton
+ */
+public class InetAddressUtils {
+
+ // Warning! IPv4 specific code
+ public static final Pattern IP_ADDRESS_REGEX = Pattern.compile("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b");
+
+ public static InetAddress startRangeByNetmask(InetAddress address, InetAddress netmask) {
+ byte[] netmaskBytes = netmask.getAddress();
+ byte[] addressBytes = address.getAddress();
+ for (int i = 0; i < addressBytes.length; i++) {
+ addressBytes[i] = (byte) (addressBytes[i] & netmaskBytes[i]);
+ }
+ try {
+ return InetAddress.getByAddress(addressBytes);
+ }
+ catch (UnknownHostException e) {
+ // this should never happen as we are modifying the same bytes
+ // received from the InetAddress
+ return null;
+ }
+ }
+
+ public static InetAddress endRangeByNetmask(InetAddress address, InetAddress netmask) {
+ byte[] netmaskBytes = netmask.getAddress();
+ byte[] addressBytes = address.getAddress();
+ for (int i = 0; i < addressBytes.length; i++) {
+ addressBytes[i] = (byte) (addressBytes[i] | ~(netmaskBytes[i]));
+ }
+ try {
+ return InetAddress.getByAddress(addressBytes);
+ } catch (UnknownHostException e) {
+ // this should never happen as we are modifying the same bytes
+ // received from the InetAddress
+ return null;
+ }
+ }
+
+ /**
+ * Compares two IP addresses.
+ * @return true in case inetAddress1 is greater than inetAddress2
+ */
+ public static boolean greaterThan(InetAddress inetAddress1, InetAddress inetAddress2) {
+ byte[] address1 = inetAddress1.getAddress();
+ byte[] address2 = inetAddress2.getAddress();
+ for (int i = 0; i < address1.length; i++) {
+ if ((address1[i] & 0xFF) > (address2[i] & 0xFF))
+ return true;
+ else
+ if ((address1[i] & 0xFF) < (address2[i] & 0xFF))
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Increments an IP address by 1.
+ */
+ public static InetAddress increment(InetAddress address) {
+ try {
+ byte[] newAddress = address.getAddress();
+ for (int i = newAddress.length-1; i >= 0; i--) {
+ if (++newAddress[i] != 0x00)
+ break;
+ }
+ return InetAddress.getByAddress(newAddress);
+ }
+ catch (UnknownHostException e) {
+ // this exception is unexpected here
+ assert false : e;
+ return null;
+ }
+ }
+
+ /**
+ * Parses the netmask string provided in special text format:
+ * A.B.C.D, where each term is 0-255 or empty. If any term is empty, it is the same as 255.
+ * @param netmaskString
+ * @throws UnknownHostException
+ */
+ public static InetAddress parseNetmask(String netmaskString) throws UnknownHostException {
+ netmaskString = netmaskString.replaceAll("\\.\\.", ".255.");
+ netmaskString = netmaskString.replaceAll("\\.\\.", ".255.");
+ InetAddress netmask = InetAddress.getByName(netmaskString);
+ return netmask;
+ }
+
+ /**
+ * Where mask bits are set, we use prototype bits.
+ * Where mask bits are cleared, we leave bits as is.
+ * @param addressBytes this array is modified according to the maskBytes and prototypeBytes
+ * @param maskBytes
+ * @param prototypeBytes
+ */
+ public static void maskPrototypeAddressBytes(byte[] addressBytes, byte[] maskBytes, byte[] prototypeBytes) {
+ for (int i = 0; i < addressBytes.length; i++) {
+ addressBytes[i] = (byte) ((addressBytes[i] & ~maskBytes[i]) | (prototypeBytes[i] & maskBytes[i]));
+ }
+ }
+
+ /**
+ * Checks whether the passed address is likely either a broadcast or network address
+ * @param address
+ */
+ public static boolean isLikelyBroadcast(InetAddress address) {
+ byte[] bytes = address.getAddress();
+ return bytes[bytes.length-1] == 0 || bytes[bytes.length-1] == (byte)0xFF;
+ }
+
+ /**
+ * Returns an IP address by hostname.
+ * This method correctly resolves the local IP address on Linux.
+ * @param hostname
+ * @return IP address as String
+ * @throws UnknownHostException
+ */
+ public static String getAddressByName(String hostname) throws UnknownHostException {
+ InetAddress address = InetAddress.getByName(hostname);
+ if (address.isLoopbackAddress()) {
+ // loopback address (127.0.0.1) was returned, try to find the local address
+ // by enumeration of network interfaces
+ try {
+ outer:
+ for (Enumeration i = NetworkInterface.getNetworkInterfaces(); i.hasMoreElements(); ) {
+ NetworkInterface networkInterface = (NetworkInterface) i.nextElement();
+ for (Enumeration i2 = networkInterface.getInetAddresses(); i2.hasMoreElements();) {
+ InetAddress currentAddress = (InetAddress) i2.nextElement();
+ if (!currentAddress.isLoopbackAddress()) {
+ address = currentAddress;
+ break outer;
+ }
+ }
+ }
+ }
+ catch (SocketException e) {
+ Logger.global.log(Level.FINE, "Cannot enumerate network interfaces", e);
+ }
+ }
+ return address.getHostAddress();
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/IntegerWithUnit.java b/ipscan/src/net/azib/ipscan/core/IntegerWithUnit.java
new file mode 100755
index 00000000..444048f1
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/IntegerWithUnit.java
@@ -0,0 +1,54 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import net.azib.ipscan.config.Labels;
+
+/**
+ * IntegerWithUnit - an Integer value together with a unit, e.g. "10 ms"
+ *
+ * @author anton
+ */
+public class IntegerWithUnit implements Comparable {
+
+ private int value;
+ private String unitLabel;
+
+ public IntegerWithUnit(int value, String unitLabel) {
+ this.value = value;
+ this.unitLabel = unitLabel;
+ }
+
+ public int intValue() {
+ return value;
+ }
+
+ public String toString() {
+ return value + Labels.getLabel(unitLabel);
+ }
+
+ public int hashCode() {
+ return value;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (obj instanceof IntegerWithUnit)
+ return value == ((IntegerWithUnit) obj).value;
+ return false;
+ }
+
+ public int compareTo(Object obj) {
+ if (this == obj)
+ return 0;
+ if (obj == null)
+ return 1;
+ int other = ((IntegerWithUnit) obj).value;
+ return value == other ? 0 : value > other ? 1 : -1;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/NotAvailableValue.java b/ipscan/src/net/azib/ipscan/core/NotAvailableValue.java
new file mode 100755
index 00000000..5a733e8a
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/NotAvailableValue.java
@@ -0,0 +1,37 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import net.azib.ipscan.config.Labels;
+
+/**
+ * The value for displaying in the result list, meaning that the actual value is unknown,
+ * because it wasn't resolved successfully.
+ *
+ * @author anton
+ */
+public class NotAvailableValue implements Comparable {
+
+ public static final NotAvailableValue INSTANCE = new NotAvailableValue();
+
+ private NotAvailableValue() {}
+
+ /**
+ * Displays a user-friendly text string :-)
+ */
+ public String toString() {
+ // TODO: make this configurable
+ return Labels.getLabel("fetcher.value.notAvailable");
+ }
+
+ public int compareTo(Object obj) {
+ if (this == obj)
+ return 0;
+ if (obj == null)
+ return 1;
+ // this value is smaller than any other object (except null)
+ return -1;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/NotScannedValue.java b/ipscan/src/net/azib/ipscan/core/NotScannedValue.java
new file mode 100755
index 00000000..c9b14003
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/NotScannedValue.java
@@ -0,0 +1,37 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import net.azib.ipscan.config.Labels;
+
+/**
+ * The value for displaying in the result list, meaning that the actual value is unknown,
+ * because it was not scanned.
+ *
+ * @author anton
+ */
+public class NotScannedValue implements Comparable {
+
+ public static final NotScannedValue INSTANCE = new NotScannedValue();
+
+ private NotScannedValue() {}
+
+ /**
+ * Displays a user-friendly text string :-)
+ */
+ public String toString() {
+ // TODO: make this configurable
+ return Labels.getLabel("fetcher.value.notScanned");
+ }
+
+ public int compareTo(Object obj) {
+ if (this == obj)
+ return 0;
+ if (obj == null)
+ return 1;
+ // this value is smaller than any other object (except null)
+ return -1;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/PortIterator.java b/ipscan/src/net/azib/ipscan/core/PortIterator.java
new file mode 100755
index 00000000..71ca1f14
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/PortIterator.java
@@ -0,0 +1,87 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+/**
+ * A class for iteration of ports, specified in special format, like:
+ * 1,5-7,35-40
+ *
+ * @author anton
+ */
+public final class PortIterator implements Cloneable {
+
+ private int[] portRangeStart;
+ private int[] portRangeEnd;
+
+ private int rangeCountMinus1;
+ private int rangeIndex;
+ private int currentPort;
+
+ private boolean hasNext;
+
+ /**
+ * Constructs the PortIterator instance
+ * @param portString the port string to parse
+ */
+ public PortIterator(String portString) {
+
+ if (portString != null && (portString = portString.trim()).length() > 0) {
+ String[] portRanges = portString.split("[ \t\n\r,;]+");
+
+ // initialize storage
+ portRangeStart = new int[portRanges.length+1]; // +1 for optimiation of 'next' method, prevents ArrayIndexOutOfBoundsException
+ portRangeEnd = new int[portRanges.length];
+
+ // parse ints
+ for (int i = 0; i < portRanges.length; i++) {
+ String range = portRanges[i];
+ int dashPos = range.indexOf('-') + 1;
+ int endPort = Integer.parseInt(range.substring(dashPos));
+ portRangeEnd[i] = endPort;
+ portRangeStart[i] = dashPos == 0 ? endPort : Integer.parseInt(range.substring(0, dashPos-1));
+ }
+
+ currentPort = portRangeStart[0];
+ rangeCountMinus1 = portRanges.length - 1;
+ hasNext = rangeCountMinus1 >= 0;
+ }
+ }
+
+ /**
+ * @return true if there are more ports left
+ */
+ public boolean hasNext() {
+ return hasNext;
+ }
+
+ /**
+ * @return next port number
+ */
+ public int next() {
+ int returnPort = currentPort++;
+
+ if (currentPort > portRangeEnd[rangeIndex]) {
+ hasNext = rangeIndex < rangeCountMinus1;
+ rangeIndex++;
+ currentPort = portRangeStart[rangeIndex];
+ }
+
+ return returnPort;
+ }
+
+ /**
+ * Clones the PortIterator instance.
+ * @return a shallow copy
+ */
+ public PortIterator copy() {
+ try {
+ return (PortIterator) super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ assert false : "this should never happen";
+ return null;
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/Scanner.java b/ipscan/src/net/azib/ipscan/core/Scanner.java
new file mode 100755
index 00000000..808078c7
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/Scanner.java
@@ -0,0 +1,72 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+import java.util.Iterator;
+
+import net.azib.ipscan.fetchers.Fetcher;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+
+/**
+ * Scanner functionality is encapsulated in this class.
+ * It uses a list of fetchers to perform the actual scanning.
+ *
+ * @author anton
+ */
+public class Scanner {
+
+ private FetcherRegistry fetcherRegistry;
+
+ public Scanner(FetcherRegistry fetcherRegistry) {
+ this.fetcherRegistry = fetcherRegistry;
+ }
+
+ /**
+ * Executes all registered fetchers for the current IP address.
+ * @param address the IP address to scan
+ * @param result where the results are injected
+ */
+ public void scan(InetAddress address, ScanningResult result) {
+
+ // create a scanning subject object, which will be used by fetchers
+ // to cache common information
+ ScanningSubject scanningSubject = new ScanningSubject(address);
+
+ // populate results
+ int fetcherIndex = 0;
+ for (Iterator i = fetcherRegistry.getSelectedFetchers().iterator(); i.hasNext();) {
+ Fetcher fetcher = (Fetcher) i.next();
+ if (!scanningSubject.isScanningAborted()) {
+ Object value = fetcher.scan(scanningSubject);
+ result.setValue(fetcherIndex, value != null ? value : NotAvailableValue.INSTANCE);
+ }
+ else {
+ result.setValue(fetcherIndex, NotScannedValue.INSTANCE);
+ }
+ fetcherIndex++;
+ }
+
+ result.setType(scanningSubject.getResultType());
+ }
+
+ /**
+ * Init everything needed for scanning, including Fetchers
+ */
+ public void init() {
+ for (Iterator i = fetcherRegistry.getSelectedFetchers().iterator(); i.hasNext();) {
+ ((Fetcher)i.next()).init();
+ }
+ }
+
+ /**
+ * Cleanup after a scan
+ */
+ public void cleanup() {
+ for (Iterator i = fetcherRegistry.getSelectedFetchers().iterator(); i.hasNext();) {
+ ((Fetcher)i.next()).cleanup();
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/ScannerThread.java b/ipscan/src/net/azib/ipscan/core/ScannerThread.java
new file mode 100755
index 00000000..ad46fae2
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/ScannerThread.java
@@ -0,0 +1,148 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.feeders.Feeder;
+
+/**
+ * Scanning thread.
+ *
+ * @author anton
+ */
+public class ScannerThread extends Thread {
+
+ private Scanner scanner;
+ private ScanningResultList scanningResultList;
+ private Feeder feeder;
+ private ScanningStateCallback statusCallback;
+ private ScanningResultsCallback resultsCallback;
+ private int state;
+ private int runningThreads;
+ private int maxThreads = Config.getGlobal().maxThreads;
+ private int threadDelay = Config.getGlobal().threadDelay;
+
+ public ScannerThread(Feeder feeder, Scanner scanner, ScanningResultList scanningResults) {
+ super("Scanner Thread");
+ // this thread is daemon because we want JVM to terminate it
+ // automatically if user closes the program (Main thread, that is)
+ setDaemon(true);
+ this.feeder = feeder;
+ this.scanner = scanner;
+ this.scanningResultList = scanningResults;
+ }
+
+ public void run() {
+ changeStatus(ScanningStateCallback.STATE_SCANNING);
+
+ scanner.init();
+
+ while(feeder.hasNext() && state == ScanningStateCallback.STATE_SCANNING) {
+ try {
+
+ // make a small delay between thread creation
+ Thread.sleep(threadDelay);
+
+ if (runningThreads >= maxThreads) {
+ // skip this iteration until more threads can be created
+ continue;
+ }
+
+ // rerieve the next IP address to scan
+ final InetAddress address = feeder.next();
+
+ // check if this is a likely broadcast address and needs to be skipped
+ if (Config.getGlobal().skipBroadcastAddresses && InetAddressUtils.isLikelyBroadcast(address)) {
+ continue;
+ }
+
+ // now increment the number of active threads, because we are going
+ // to start a new one below
+ runningThreads++;
+
+ // prepare results receiver for upcoming results
+ int preparationNumber = resultsCallback.prepareForResults(address);
+
+ // notify listeners of the progress we are doing
+ statusCallback.updateProgress(address, runningThreads, feeder.getPercentageComplete());
+
+ // scan each IP in parallel, in a separate thread
+ new IPThread(address, preparationNumber).start();
+ }
+ catch (InterruptedException e) {
+ return;
+ }
+ }
+
+ // inform that no more addresses left
+ changeStatus(ScanningStateCallback.STATE_STOPPING);
+
+ // now wait for all threads, which are still running
+ try {
+ // TODO: make a better and safer implementation
+ while (runningThreads > 0) {
+ Thread.sleep(200);
+ statusCallback.updateProgress(null, runningThreads, 100);
+ }
+ }
+ catch (InterruptedException e) {
+ // nothing special to do here
+ }
+
+ scanner.cleanup();
+
+ // finally, the scanning is complete
+ changeStatus(ScanningStateCallback.STATE_IDLE);
+ }
+
+ private void changeStatus(int status) {
+ this.state = status;
+ statusCallback.scannerStateChanged(status);
+ }
+
+ public void forceStop() {
+ changeStatus(ScanningStateCallback.STATE_STOPPING);
+ }
+
+ public void abort() {
+ changeStatus(ScanningStateCallback.STATE_KILLING);
+ }
+
+ public void setResultsCallback(ScanningResultsCallback resultsCallback) {
+ this.resultsCallback = resultsCallback;
+ }
+
+ public void setStatusCallback(ScanningStateCallback statusCallback) {
+ this.statusCallback = statusCallback;
+ }
+
+ /**
+ * This thread gets executed for each scanned IP address to do the actual
+ * scanning.
+ */
+ private class IPThread extends Thread {
+ private InetAddress address;
+ private int preparationIndex;
+
+ IPThread(InetAddress address, int preparationIndex) {
+ super("IP Thread: " + address.getHostAddress());
+ setDaemon(true);
+ this.address = address;
+ this.preparationIndex = preparationIndex;
+ }
+
+ public void run() {
+ try {
+ ScanningResult result = scanningResultList.getResult(preparationIndex);
+ scanner.scan(address, result);
+ resultsCallback.consumeResults(preparationIndex, result);
+ }
+ finally {
+ runningThreads--;
+ }
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/core/ScannerThreadFactory.java b/ipscan/src/net/azib/ipscan/core/ScannerThreadFactory.java
new file mode 100755
index 00000000..f153fd4b
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/ScannerThreadFactory.java
@@ -0,0 +1,27 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import net.azib.ipscan.feeders.Feeder;
+
+/**
+ * ScannerThreadFactory
+ *
+ * @author anton
+ */
+public class ScannerThreadFactory {
+
+ private ScanningResultList scanningResults;
+ private Scanner scanner;
+
+ public ScannerThreadFactory(ScanningResultList scanningResults, Scanner scanner) {
+ this.scanningResults = scanningResults;
+ this.scanner = scanner;
+ }
+
+ public ScannerThread createScannerThread(Feeder feeder) {
+ return new ScannerThread(feeder, scanner, scanningResults);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/ScanningResult.java b/ipscan/src/net/azib/ipscan/core/ScanningResult.java
new file mode 100755
index 00000000..0d606868
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/ScanningResult.java
@@ -0,0 +1,71 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The holder of scanning result for a single IP address.
+ *
+ * @author anton
+ */
+public class ScanningResult {
+
+ /** The scanned IP address */
+ private InetAddress address;
+ /** Scanning results, result of each Fetcher is an element */
+ private Object[] values;
+ /** Scanning result type, see constants in {@link ScanningSubject} */
+ private int type;
+
+ /**
+ * Creates a new instance, initializing the first value to the
+ * provided address
+ * @param address
+ * @param numberOfFetchers the number of currently available fetchers
+ */
+ ScanningResult(InetAddress address, int numberOfFetchers) {
+ this.address = address;
+ values = new Object[numberOfFetchers];
+ values[0] = address.getHostAddress();
+ type = ScanningSubject.RESULT_TYPE_UNKNOWN;
+ }
+
+ public InetAddress getAddress() {
+ return address;
+ }
+
+ /**
+ * @return the scanning results as an unmodifiable List, result of each Fetcher is an element
+ */
+ public List getValues() {
+ return Arrays.asList(values);
+ }
+
+ /**
+ * Sets scanning result type, see constants in {@link ScanningSubject}
+ */
+ void setType(int type) {
+ this.type = type;
+ }
+
+ /**
+ * @return the scanning result type, see constants in {@link ScanningSubject}
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Sets the value returned by the specified fetcher
+ * @param fetcherIndex
+ * @param value
+ */
+ public void setValue(int fetcherIndex, Object value) {
+ values[fetcherIndex] = value;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/ScanningResultList.java b/ipscan/src/net/azib/ipscan/core/ScanningResultList.java
new file mode 100755
index 00000000..25156239
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/ScanningResultList.java
@@ -0,0 +1,153 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.fetchers.Fetcher;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+
+/**
+ * The holder of scanning results.
+ *
+ * @author anton
+ */
+public class ScanningResultList {
+
+ private static final int RESULT_LIST_INITIAL_SIZE = 1024;
+
+ private FetcherRegistry fetcherRegistry;
+ // selected fetchers are cached here, because the may be changed in the registry already
+ private List selectedFetchers;
+
+ private List resultList = new ArrayList(1024);
+ private ResultsComparator resultsComparator = new ResultsComparator();
+
+ public ScanningResultList(FetcherRegistry fetcherRegistry) {
+ this.fetcherRegistry = fetcherRegistry;
+ clear();
+ }
+
+ /**
+ * @return selected fetchers that were used for the last scan
+ * Note: they may be different from {@link FetcherRegistry#getSelectedFetchers()}
+ */
+ public List getFetchers() {
+ return selectedFetchers;
+ }
+
+ /**
+ * Adds the new scanned IP address
+ * @param address
+ * @return the index of the added address, can be used in calls to other methods
+ */
+ public synchronized int add(InetAddress address) {
+ int index = resultList.size();
+ resultList.add(new ScanningResult(address, fetcherRegistry.getSelectedFetchers().size()));
+ return index;
+ }
+
+ /**
+ * Returns all results for a particular IP address as a String.
+ * This is used in showing the IP Details dialog box.
+ * TODO: write tests!
+ *
+ * @param index
+ * @return
+ */
+ public synchronized String getResultsAsString(int index) {
+ // cross-platform newline :-)
+ String newLine = System.getProperty("line.separator");
+
+ ScanningResult scanningResult = (ScanningResult) resultList.get(index);
+ StringBuffer details = new StringBuffer(1024);
+ Iterator iterator = scanningResult.getValues().iterator();
+ for (int i = 0; iterator.hasNext(); i++) {
+ String fetcherName = Labels.getLabel(((Fetcher)selectedFetchers.get(i)).getLabel());
+ details.append(fetcherName).append(":\t");
+ Object value = iterator.next();
+ details.append(value != null ? value : "");
+ details.append(newLine).append("--------------------------------------------------------------------------------------").append(newLine);
+ }
+ return details.toString();
+ }
+
+ /**
+ * Clears previous scanning results, prepares for a new scan.
+ */
+ public synchronized void clear() {
+ // clear the results
+ resultList.clear();
+ // reload currently selected fetchers
+ selectedFetchers = new ArrayList(fetcherRegistry.getSelectedFetchers());
+ }
+
+ /**
+ * @return an Iterator of scanning results
+ *
+ * Note: the returned Iterator is not synchronized
+ */
+ public synchronized Iterator iterator() {
+ return resultList.iterator();
+ }
+
+ /**
+ * @param index
+ * @return the results of the IP adress, corresponding to an index
+ */
+ public synchronized ScanningResult getResult(int index) {
+ return (ScanningResult) resultList.get(index);
+ }
+
+ /**
+ * Removes the elements by the provided indices
+ * Note: old indices returned by {@link #add(InetAddress)} are no longer valid
+ * @param indices a sorted list of indices to remove
+ */
+ public synchronized void remove(int[] indices) {
+ // this rebuild is faster then a number of calls to remove()
+ // however, a further speedup can be obtained by using a Set instead of binarySearch()
+ List newList = new ArrayList(RESULT_LIST_INITIAL_SIZE);
+ for (int i = 0; i < resultList.size(); i++) {
+ if (Arrays.binarySearch(indices, i) < 0)
+ newList.add(resultList.get(i));
+ }
+ resultList = newList;
+ }
+
+ /**
+ * Sorts by the specified column index.
+ * Note: old indices returned by {@link #add(InetAddress)} are no longer valid
+ * @param columnIndex
+ */
+ public synchronized void sort(int columnIndex) {
+ resultsComparator.index = columnIndex;
+ Collections.sort(resultList, resultsComparator);
+ }
+
+ private static class ResultsComparator implements Comparator {
+
+ private int index;
+
+ public int compare(Object o1, Object o2) {
+ if (!(o1 instanceof ScanningResult))
+ return -1;
+ if (!(o2 instanceof ScanningResult))
+ return 1;
+
+ Object val1 = ((ScanningResult)o1).getValues().get(index);
+ Object val2 = ((ScanningResult)o2).getValues().get(index);
+
+ return val1.toString().compareTo(val2.toString());
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/ScanningResultsCallback.java b/ipscan/src/net/azib/ipscan/core/ScanningResultsCallback.java
new file mode 100755
index 00000000..fec87e00
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/ScanningResultsCallback.java
@@ -0,0 +1,30 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+
+/**
+ * This callback is called to consume scanning results.
+ *
+ * @author anton
+ */
+public interface ScanningResultsCallback {
+
+ /**
+ * This method is called just before starting to retrieve
+ * scanning results for the specified address.
+ * @param address
+ * @return the method should return an int
+ */
+ public int prepareForResults(InetAddress address);
+
+ /**
+ * This method is called when scanning results are ready.
+ * @param preparationNumber the number, which was previously returned by prepareForResults().
+ * @param results the List of Strings, each String corresponds to a Fetcher
+ */
+ public void consumeResults(int preparationNumber, ScanningResult results);
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/ScanningStateCallback.java b/ipscan/src/net/azib/ipscan/core/ScanningStateCallback.java
new file mode 100755
index 00000000..bb40454f
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/ScanningStateCallback.java
@@ -0,0 +1,40 @@
+/**
+ *
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+
+/**
+ * This callback is called on scanning status updates.
+ *
+ * @author anton
+ */
+public interface ScanningStateCallback {
+
+ public static final int STATE_IDLE = 0;
+ public static final int STATE_SCANNING = 1;
+ public static final int STATE_STOPPING = 2;
+ public static final int STATE_KILLING = 3;
+
+ /**
+ * This method is called on scanner status changes,
+ * eg. when scanning is about to stop.
+ *
+ * @param status integer value of current status, having the
+ * corresponding STATE_XXX constant
+ */
+ public void scannerStateChanged(int status);
+
+ /**
+ * This method is called on scanning progress updates.
+ * There are no guarantees that this method is called on every
+ * scanning iteration.
+ *
+ * @param currentAddress currently scanned IP address, can be null
+ * @param runningThreads number of currently running threads
+ * @param percentageComplete value from 0 to 100, showing how much work
+ * is already done.
+ */
+ public void updateProgress(InetAddress currentAddress, int runningThreads, int percentageComplete);
+}
diff --git a/ipscan/src/net/azib/ipscan/core/ScanningSubject.java b/ipscan/src/net/azib/ipscan/core/ScanningSubject.java
new file mode 100755
index 00000000..a0a76807
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/ScanningSubject.java
@@ -0,0 +1,100 @@
+/**
+ * This file is a part of Angry IP Scanner source code,
+ * see http://www.azib.net/ for more information.
+ */
+package net.azib.ipscan.core;
+
+import java.net.InetAddress;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Scanning subject represents a single scanned
+ * IP address and any additional arbitrary parameters,
+ * which may be used to cache some intermediate data
+ * among different Fetchers.
+ *
+ * @author anton
+ */
+public class ScanningSubject {
+
+ // constants for result type (they can be modified by some Fetchers)
+ public static final int RESULT_TYPE_UNKNOWN = 0;
+ public static final int RESULT_TYPE_DEAD = 1;
+ public static final int RESULT_TYPE_ALIVE = 2;
+ public static final int RESULT_TYPE_ADDITIONAL_INFO = 3;
+
+ /** The address being scanned */
+ private InetAddress address;
+ /** Arbitrary parameters for sharing among different (but related) Fetchers */
+ private Map parameters;
+ /** The result type constant value, can be modified by some Fetchers */
+ private int resultType = RESULT_TYPE_UNKNOWN;
+ /** Whether we need to continue scanning or it can be aborted */
+ private boolean isScanningAborted = false;
+
+ /**
+ * This constructor should only be used by the Scanner class or unit tests.
+ */
+ public ScanningSubject(InetAddress address) {
+ this.address = address;
+ this.parameters = new HashMap();
+ }
+
+ public InetAddress getIPAddress() {
+ return address;
+ }
+
+ /**
+ * Sets a subject specific named parameter.
+ */
+ public void setParameter(String name, Object value) {
+ parameters.put(name, value);
+ }
+
+ /**
+ * Gets a subject specific named parameter,
+ * previosly set by setParameter().
+ */
+ public Object getParameter(String name) {
+ return parameters.get(name);
+ }
+
+ /**
+ * @return true in case parameter with given name was specified.
+ * This method is useful in case parameter value was null.
+ */
+ public boolean hasParameter(String name) {
+ return parameters.containsKey(name);
+ }
+
+ /**
+ * @return the result type constant value, possibly modified by Fetchers
+ */
+ public int getResultType() {
+ return resultType;
+ }
+
+ /**
+ * Provides an ability for Fetchers to determine the result type of scanning this particular address
+ * @param resultType constant value
+ */
+ public void setResultType(int resultType) {
+ this.resultType = resultType;
+ }
+
+ /**
+ * @return true if a fetcher has instructed to abort scanning
+ */
+ public boolean isScanningAborted() {
+ return isScanningAborted;
+ }
+
+ /**
+ * Can be used to inform the scanner to abort scanning
+ */
+ public void abortScanning() {
+ this.isScanningAborted = true;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/ICMPPinger.java b/ipscan/src/net/azib/ipscan/core/net/ICMPPinger.java
new file mode 100644
index 00000000..7561129c
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/ICMPPinger.java
@@ -0,0 +1,139 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.savarese.rocksaw.net.RawSocket;
+import org.savarese.vserv.tcpip.ICMPEchoPacket;
+import org.savarese.vserv.tcpip.ICMPPacket;
+import org.savarese.vserv.tcpip.IPPacket;
+import org.savarese.vserv.tcpip.OctetConverter;
+
+/**
+ * Pinging code is encapsulated here.
+ *
+ * @author anton
+ */
+public class ICMPPinger implements Pinger {
+
+ private static final Logger LOG = Logger.getLogger(ICMPPinger.class.getName());
+
+ private int timeout;
+
+ public ICMPPinger(int timeout) {
+ this.timeout = timeout;
+ }
+
+ private RawSocket createRawSocket() throws IOException {
+ RawSocket socket = new RawSocket();
+ socket.open(RawSocket.PF_INET, IPPacket.PROTOCOL_ICMP);
+
+ try {
+ socket.setSendTimeout(timeout);
+ socket.setReceiveTimeout(timeout);
+ } catch (java.net.SocketException se) {
+ socket.setUseSelectTimeout(true);
+ socket.setSendTimeout(timeout);
+ socket.setReceiveTimeout(timeout);
+ }
+ return socket;
+ }
+
+ private void sendReceiveEchoPacket(RawSocket socket, InetAddress address, int sequence, PingResult result) throws IOException {
+
+ ICMPEchoPacket packet = new ICMPEchoPacket(1);
+ byte[] data = new byte[84];
+ packet.setData(data);
+ packet.setIPHeaderLength(5);
+ packet.setICMPDataByteLength(56);
+ packet.setType(ICMPPacket.TYPE_ECHO_REQUEST);
+ packet.setCode(0);
+ packet.setIdentifier(hashCode() & 0xFFFF); // some identification stuff
+ packet.setSequenceNumber(sequence);
+
+ int offset = packet.getIPHeaderByteLength();
+ int dataOffset = offset + packet.getICMPHeaderByteLength();
+ int length = packet.getICMPPacketByteLength();
+
+ OctetConverter.longToOctets(System.currentTimeMillis(), data, dataOffset);
+ packet.computeICMPChecksum();
+
+ socket.write(address, data, offset, length);
+
+ try {
+ int skippedCount = 0;
+ do {
+ socket.read(address, data);
+ skippedCount++;
+ //if (packet.getType() == ICMPPacket.TYPE_ECHO_REPLY)
+ // System.err.println(Thread.currentThread() + " " + packet.getSourceAsInetAddress().getHostAddress() + ": " + skippedCount);
+ }
+ while (packet.getType() != ICMPPacket.TYPE_ECHO_REPLY ||
+ packet.getIdentifier() != (hashCode() & 0xFFFF) ||
+ packet.getSequenceNumber() != sequence);
+
+ if (packet.getSourceAsInetAddress().equals(address)) {
+ long end = System.currentTimeMillis();
+ long start = OctetConverter.octetsToLong(data, dataOffset);
+ long time = end - start;
+
+ result.totalTime += time;
+ result.replyCount++;
+ result.ttl = packet.getTTL() & 0xFF;
+ }
+ }
+ catch (InterruptedIOException e) {
+ // socket read timeout
+ LOG.finer("Receive timeout");
+ // TODO: make RawSocket to throw Exceptions without the stack trace (for speed)
+ }
+ catch (UnknownHostException e) {
+ LOG.log(Level.WARNING, "Cannot retrieve the source address of an ICMP packet", e);
+ }
+ catch (IOException e) {
+ LOG.log(Level.WARNING, "Unable to read from the socket", e);
+ }
+
+ }
+
+ /**
+ * Issues the specified number of pings and
+ * waits for replies.
+ *
+ * @param address address to ping
+ * @param count number of pings to perform
+ */
+ public PingResult ping(InetAddress address, int count) throws IOException {
+
+ PingResult result = new PingResult(address);
+ RawSocket socket = createRawSocket();
+
+ try {
+ // send a bunch of packets
+ for (int i = 0; i < count; i++) {
+ try {
+ sendReceiveEchoPacket(socket, address, i, result);
+ }
+ catch (InterruptedIOException e) {
+ // ignore timeouts
+ }
+ }
+ }
+ finally {
+ socket.close();
+ }
+
+ return result;
+ }
+
+ public void close() {
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/ICMPSharedPinger.java b/ipscan/src/net/azib/ipscan/core/net/ICMPSharedPinger.java
new file mode 100755
index 00000000..783b43a2
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/ICMPSharedPinger.java
@@ -0,0 +1,240 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.savarese.rocksaw.net.RawSocket;
+import org.savarese.vserv.tcpip.ICMPEchoPacket;
+import org.savarese.vserv.tcpip.ICMPPacket;
+import org.savarese.vserv.tcpip.IPPacket;
+import org.savarese.vserv.tcpip.OctetConverter;
+
+/**
+ * Shared multithreaded pinger.
+ *
+ * @author anton
+ */
+public class ICMPSharedPinger implements Pinger {
+
+ private static final Logger LOG = Logger.getLogger(ICMPSharedPinger.class.getName());
+
+ /** a single raw socket for sending of all ICMP packets */
+ private RawSocket sendingSocket;
+ /** a single raw socket for receiving of all ICMP packets */
+ private RawSocket receivingSocket;
+ /** the map with PingResults, keys are InetAddress */
+ private Map results = Collections.synchronizedMap(new HashMap());
+
+ private Thread receiverThread;
+
+ private int timeout;
+ private int timeOffsetInPacket;
+
+ public ICMPSharedPinger(int timeout) throws IOException {
+ // we use two shared sockets, because it works more efficiently
+ // OSs tend to copy all received ICMP packets to all open raw sockets,
+ // so it is very bad to have a separate raw socket for each scanning thread
+ sendingSocket = new RawSocket();
+ sendingSocket.open(RawSocket.PF_INET, IPPacket.PROTOCOL_ICMP);
+ receivingSocket = new RawSocket();
+ receivingSocket.open(RawSocket.PF_INET, IPPacket.PROTOCOL_ICMP);
+ this.timeout = timeout;
+
+ try {
+ sendingSocket.setSendTimeout(timeout);
+ receivingSocket.setReceiveTimeout(timeout);
+ //receivingSocket.setReceiveBufferSize()
+ }
+ catch (java.net.SocketException se) {
+ sendingSocket.setUseSelectTimeout(true);
+ receivingSocket.setUseSelectTimeout(true);
+ sendingSocket.setSendTimeout(timeout);
+ receivingSocket.setReceiveTimeout(timeout);
+ }
+
+ receiverThread = new PacketReceiverThread();
+ receiverThread.start();
+ }
+
+ public void close() throws IOException {
+ synchronized (sendingSocket) {
+ sendingSocket.close();
+ }
+ receiverThread.interrupt();
+ }
+
+ public PingResult ping(InetAddress address, int count) throws IOException {
+
+ PingResult result = new PingResult(address);
+ results.put(address, result);
+
+ // TODO: make ICMPEchoPacket accept byte array in the constructor
+ ICMPEchoPacket packet = new ICMPEchoPacket(1);
+ byte[] data = new byte[84];
+ packet.setData(data);
+ packet.setIPHeaderLength(5);
+ packet.setICMPDataByteLength(56);
+ packet.setType(ICMPPacket.TYPE_ECHO_REQUEST);
+ packet.setCode(0);
+ packet.setIdentifier(hashCode() & 0xFFFF); // some identification stuff
+
+ try {
+ // send a bunch of packets
+ // note: we send sequence numbers starting from 1 (this is used by the ReceiverThread)
+ for (int i = 1; i <= count; i++) {
+ packet.setSequenceNumber(i);
+
+ int offset = packet.getIPHeaderByteLength();
+ timeOffsetInPacket = offset + packet.getICMPHeaderByteLength();
+ int length = packet.getICMPPacketByteLength();
+
+ OctetConverter.longToOctets(System.currentTimeMillis(), data, timeOffsetInPacket);
+ packet.computeICMPChecksum();
+
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Pinging " + i + result.address);
+ }
+ synchronized (sendingSocket) {
+ sendingSocket.write(result.address, data, offset, length);
+ }
+
+ try {
+ // a small pause between sending the packets
+ Thread.sleep(15);
+ }
+ catch (InterruptedException e) {}
+ }
+
+ int totalTimeout = timeout * count;
+ while (totalTimeout > 0 && result.replyCount < count) {
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Waiting for response " + address + ": " + totalTimeout);
+ }
+ synchronized (result) {
+ // wait until we have an answer
+ try {
+ result.wait(timeout);
+ }
+ catch (InterruptedException e) {}
+ }
+ totalTimeout -= timeout;
+ }
+
+
+ return result;
+ }
+ finally {
+ // remove garbage
+ results.remove(address);
+ }
+ }
+
+ /**
+ * An internal thread for receiving of packets
+ */
+ private class PacketReceiverThread extends Thread {
+
+ public PacketReceiverThread() {
+ super("Ping packet receiver");
+ setDaemon(true);
+ setPriority(Thread.MAX_PRIORITY);
+ }
+
+ public void run() {
+ ICMPEchoPacket packet = new ICMPEchoPacket(1);
+ byte[] data = new byte[84];
+ packet.setData(data);
+ packet.setIPHeaderLength(5);
+ packet.setICMPDataByteLength(56);
+
+ // we use this address for receiving
+ // due to some reason, raw sockets return packets coming from any addresses anyway
+ InetAddress tmpAddress = null;
+ try {
+ tmpAddress = InetAddress.getLocalHost();
+ }
+ catch (UnknownHostException e) {
+ LOG.log(Level.SEVERE, null, e);
+ }
+
+ try {
+ // Windows OS cannot read from a raw socket before anything has been sent through it
+ receivingSocket.write(tmpAddress, data);
+ }
+ catch (IOException e) {
+ LOG.log(Level.WARNING, "Sending of test packet failed", e);
+ }
+
+ do {
+ try {
+ receivingSocket.read(tmpAddress, data);
+
+ if (packet.getType() == ICMPPacket.TYPE_ECHO_REPLY &&
+ packet.getIdentifier() == (ICMPSharedPinger.this.hashCode() & 0xFFFF) &&
+ packet.getSequenceNumber() > 0) {
+
+ long endTime = System.currentTimeMillis();
+
+ PingResult result = (PingResult) results.get(packet.getSourceAsInetAddress());
+ if (result == null) {
+ LOG.warning("ICMP packet received from an unknown address: " + packet.getSourceAsInetAddress());
+ continue;
+ }
+
+ long startTime = OctetConverter.octetsToLong(data, timeOffsetInPacket);
+ long time = endTime - startTime;
+
+ if (LOG.isLoggable(Level.FINEST)) {
+ LOG.finest("Received " + packet.getSequenceNumber() + packet.getSourceAsInetAddress() + ": " + time);
+ }
+
+ result.totalTime += time;
+ result.replyCount++;
+ // TTL should be the same among all packets
+ result.ttl = packet.getTTL() & 0xFF;
+
+ synchronized (result) {
+ // notify the sender that we have an answer :-)
+ result.notifyAll();
+ }
+ }
+ else
+ if (packet.getType() == ICMPPacket.TYPE_HOST_UNREACHABLE) {
+ // TODO: received non-ECHO_REPLY packets may also be useful, saying "destination is unreachable"
+ // packet body in this case is the sent ICMP_REQUEST packet
+ }
+ }
+ catch (InterruptedIOException e) {
+ // socket read timeout
+ LOG.finer("Receive timeout");
+ // TODO: make RawSocket to throw Exceptions without the stack trace (for speed)
+ }
+ catch (UnknownHostException e) {
+ LOG.log(Level.WARNING, "Cannot retrieve the source address of an ICMP packet", e);
+ }
+ catch (IOException e) {
+ LOG.log(Level.WARNING, "Unable to read from the socket", e);
+ }
+
+ }
+ while(!interrupted());
+
+ try {
+ receivingSocket.close();
+ }
+ catch (IOException e) { }
+ LOG.fine("Terminated");
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/PingResult.java b/ipscan/src/net/azib/ipscan/core/net/PingResult.java
new file mode 100755
index 00000000..753de288
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/PingResult.java
@@ -0,0 +1,41 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+import java.net.InetAddress;
+
+/**
+ * The result of pinging
+ *
+ * @author anton
+ */
+public class PingResult {
+
+ InetAddress address;
+
+ int ttl;
+ long totalTime;
+ int replyCount;
+
+ public PingResult(InetAddress address) {
+ this.address = address;
+
+ }
+
+ public int getTTL() {
+ return ttl;
+ }
+
+ public int getAverageTime() {
+ return (int)(totalTime / replyCount);
+ }
+
+ /**
+ * @return true in case at least one reply was received
+ */
+ public boolean isAlive() {
+ return replyCount > 0;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/Pinger.java b/ipscan/src/net/azib/ipscan/core/net/Pinger.java
new file mode 100755
index 00000000..2907a1b7
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/Pinger.java
@@ -0,0 +1,30 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * Pinger
+ *
+ * @author anton
+ */
+public interface Pinger {
+
+ /**
+ * Closes the raw socket opened by the constructor. After calling this
+ * method, the object cannot be used.
+ */
+ public void close() throws IOException;
+
+ /**
+ * Issues the specified number of pings and
+ * waits for replies.
+ *
+ * @param count number of pings to perform
+ */
+ public PingResult ping(InetAddress address, int count) throws IOException;
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/PingerRegistry.java b/ipscan/src/net/azib/ipscan/core/net/PingerRegistry.java
new file mode 100755
index 00000000..f013bb3c
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/PingerRegistry.java
@@ -0,0 +1,25 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+/**
+ * PingerRegistry
+ *
+ * @author Anton Keks
+ */
+public interface PingerRegistry {
+
+ /**
+ * @return a String array of pinger names (labels)
+ */
+ public String[] getRegisteredNames();
+
+ /**
+ * Creates a new instance of currently selected Pinger
+ * @param timeout
+ * @return the instance
+ */
+ public Pinger createPinger(String pingerName, int timeout);
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/PingerRegistryImpl.java b/ipscan/src/net/azib/ipscan/core/net/PingerRegistryImpl.java
new file mode 100755
index 00000000..8a82c849
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/PingerRegistryImpl.java
@@ -0,0 +1,58 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * PingerRegistryImpl
+ *
+ * @author Anton Keks
+ */
+public class PingerRegistryImpl implements PingerRegistry {
+
+ private static final Logger LOG = Logger.getLogger(PingerRegistryImpl.class.getName());
+
+ /** All available Pinger implementations */
+ private Map pingers;
+
+ public PingerRegistryImpl() {
+ pingers = new LinkedHashMap();
+ pingers.put("pinger.icmp", ICMPSharedPinger.class);
+ pingers.put("pinger.icmp2", ICMPPinger.class);
+ pingers.put("pinger.udp", UDPPinger.class);
+ pingers.put("pinger.tcp", TCPPinger.class);
+
+ // TODO: implement a windows-specific ICMP pinger for XP SP2 and beyond that uses ping.dll
+ // TODO: autodetect working pingers here
+ }
+
+ public String[] getRegisteredNames() {
+ return (String[]) pingers.keySet().toArray(new String[pingers.size()]);
+ }
+
+ public Pinger createPinger(String pingerName, int timeout) {
+ Class pingerClass = (Class) pingers.get(pingerName);
+ Constructor constructor;
+ try {
+ constructor = pingerClass.getConstructor(new Class[] {int.class});
+ return (Pinger) constructor.newInstance(new Object[] {new Integer(timeout)});
+ }
+ catch (Exception e) {
+ Throwable t = e instanceof InvocationTargetException ? e.getCause() : e;
+ String message = "Unable to create pinger: " + pingerName;
+ LOG.log(Level.SEVERE, message, t);
+ if (t instanceof RuntimeException)
+ throw (RuntimeException) t;
+ throw new RuntimeException(message);
+ }
+
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/TCPPinger.java b/ipscan/src/net/azib/ipscan/core/net/TCPPinger.java
new file mode 100755
index 00000000..84ef1c6b
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/TCPPinger.java
@@ -0,0 +1,71 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NoRouteToHostException;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * TCP Pinger. Uses a TCP port to ping, doesn't require root privileges.
+ *
+ * @author anton
+ */
+public class TCPPinger implements Pinger {
+
+ private static final Logger LOG = Logger.getLogger(UDPPinger.class.getName());
+
+ private static final int PROBE_TCP_PORT = 80;
+
+ private int timeout;
+
+ public TCPPinger(int timeout) {
+ // use double timeout, because TCP connect() produces more packets
+ this.timeout = timeout * 2;
+ }
+
+ public PingResult ping(InetAddress address, int count) throws IOException {
+ PingResult result = new PingResult(address);
+
+ Socket socket = new Socket();
+ socket.setSoTimeout(timeout);
+
+ long startTime = System.currentTimeMillis();
+ try {
+ socket.connect(new InetSocketAddress(address, PROBE_TCP_PORT), timeout);
+ result.replyCount++;
+ result.totalTime+=System.currentTimeMillis()-startTime;
+ }
+ catch (ConnectException e) {
+ result.replyCount++;
+ result.totalTime+=System.currentTimeMillis()-startTime;
+ }
+ catch (SocketTimeoutException e) {
+ }
+ catch (NoRouteToHostException e) {
+ // TODO: this means that the host is down
+ }
+ catch (IOException e) {
+ LOG.setLevel(Level.ALL);
+ LOG.log(Level.FINER, null, e);
+ }
+
+ try {
+ socket.close();
+ }
+ catch (Exception e) {}
+
+ return result;
+ }
+
+ public void close() throws IOException {
+ // nothing to do here
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/core/net/UDPPinger.java b/ipscan/src/net/azib/ipscan/core/net/UDPPinger.java
new file mode 100755
index 00000000..5e4be830
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/core/net/UDPPinger.java
@@ -0,0 +1,65 @@
+/**
+ *
+ */
+package net.azib.ipscan.core.net;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.PortUnreachableException;
+import java.net.SocketTimeoutException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * UDP Pinger. Uses an UDP port to ping, doesn't require root privileges.
+ *
+ * @author anton
+ */
+public class UDPPinger implements Pinger {
+
+ private static final Logger LOG = Logger.getLogger(UDPPinger.class.getName());
+
+ private static final int PROBE_UDP_PORT = 33381;
+
+ private int timeout;
+
+ public UDPPinger(int timeout) {
+ this.timeout = timeout;
+ }
+
+ public PingResult ping(InetAddress address, int count) throws IOException {
+ PingResult result = new PingResult(address);
+
+ DatagramSocket socket = new DatagramSocket();
+ socket.setSoTimeout(timeout);
+ socket.connect(address, PROBE_UDP_PORT);
+
+ for (int i = 0; i < count; i++) {
+ DatagramPacket packet = new DatagramPacket(new byte[]{}, 0);
+ long startTime = System.currentTimeMillis();
+ try {
+ socket.send(packet);
+ socket.receive(packet);
+ }
+ catch (PortUnreachableException e) {
+ result.replyCount++;
+ result.totalTime+=System.currentTimeMillis()-startTime;
+ }
+ catch (SocketTimeoutException e) {
+ }
+ catch (IOException e) {
+ LOG.log(Level.FINER, null, e);
+ }
+ }
+
+ socket.disconnect();
+
+ return result;
+ }
+
+ public void close() throws IOException {
+ // nothing to do here
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/CSVExporter.java b/ipscan/src/net/azib/ipscan/exporters/CSVExporter.java
new file mode 100755
index 00000000..3480eba5
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/CSVExporter.java
@@ -0,0 +1,105 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * CSV Exporter
+ *
+ * @author anton
+ */
+public class CSVExporter implements Exporter {
+
+ /* CSV delimeter character */
+ static final char DELIMETER = ',';
+ /* Delimeter escaping character (if data contains DELIMETER) */
+ static final char DELIMETER_ESCAPED = '.';
+ /* Newline character */
+ static final String NEWLINE = System.getProperty("line.separator");
+
+ private Writer output;
+ private boolean isAppend;
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getLabel()
+ */
+ public String getLabel() {
+ return "exporter.csv";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getFilenameExtension()
+ */
+ public String getFilenameExtension() {
+ return "csv";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setAppend(boolean)
+ */
+ public void setAppend(boolean append) {
+ isAppend = append;
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#start(java.io.OutputStream, String)
+ */
+ public void start(OutputStream outputStream, String feederInfo) {
+ output = new OutputStreamWriter(outputStream);
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#end()
+ */
+ public void end() throws IOException {
+ output.flush();
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setFetchers(String[])
+ */
+ public void setFetchers(String[] fetcherNames) throws IOException {
+ if (!isAppend) {
+ output.write(csvSafeString(fetcherNames[0]));
+ for (int i = 1; i < fetcherNames.length; i++) {
+ output.write(DELIMETER);
+ output.write(csvSafeString(fetcherNames[i]));
+ }
+ output.write(NEWLINE);
+ }
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#nextAdressResults(Object[])
+ */
+ public void nextAdressResults(Object[] results) throws IOException {
+ output.write(csvSafeString(results[0]));
+ for (int i = 1; i < results.length; i++) {
+ Object result = results[i];
+ output.write(DELIMETER);
+ output.write(csvSafeString(result));
+ }
+ output.write(NEWLINE);
+ }
+
+ /**
+ * @return a safe string to be outputted in CSV format (it doesn't contain the DELIMETER)
+ */
+ String csvSafeString(Object o) {
+ if (o == null)
+ return "";
+ return o.toString().replace(DELIMETER, DELIMETER_ESCAPED);
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#clone()
+ */
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/ExportProcessor.java b/ipscan/src/net/azib/ipscan/exporters/ExportProcessor.java
new file mode 100755
index 00000000..dd1cc3d7
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/ExportProcessor.java
@@ -0,0 +1,79 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+import java.io.FileOutputStream;
+import java.util.Iterator;
+import java.util.List;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.ScanningResult;
+import net.azib.ipscan.core.ScanningResultList;
+import net.azib.ipscan.fetchers.Fetcher;
+
+/**
+ * Export Processor controls the actual exporting using the provided Exporter.
+ *
+ * @author anton
+ */
+public class ExportProcessor {
+
+ private Exporter exporter;
+ private String fileName;
+
+ public ExportProcessor(Exporter exporter, String fileName) {
+ this.exporter = exporter;
+ this.fileName = fileName;
+ }
+
+ /**
+ * Called to execute the actual scanning process.
+ * @param scanningResults the scanning results, which are available
+ * @param feederInfo info about the Feeder configuration as String
+ * @param resultSelector optional (can be null) - determines results for saving or skipping
+ */
+ public void process(ScanningResultList scanningResults, String feederInfo, ScanningResultSelector resultSelector) {
+ FileOutputStream outputStream = null;
+ try {
+ outputStream = new FileOutputStream(fileName);
+
+ exporter.start(outputStream, feederInfo);
+
+ // set fetchers
+ List fetchers = scanningResults.getFetchers();
+ String[] fetcherNames = new String[fetchers.size()];
+ int i = 0;
+ for (Iterator j = fetchers.iterator(); j.hasNext(); i++) {
+ fetcherNames[i] = Labels.getLabel(((Fetcher)j.next()).getLabel());
+ }
+ exporter.setFetchers(fetcherNames);
+
+ int index = 0;
+ for (Iterator j = scanningResults.iterator(); j.hasNext(); index++) {
+ ScanningResult scanningResult = (ScanningResult) j.next();
+ if (resultSelector == null || resultSelector.isResultSelected(index, scanningResult)) {
+ exporter.nextAdressResults(scanningResult.getValues().toArray());
+ }
+ }
+
+ exporter.end();
+ }
+ catch (Exception e) {
+ throw new ExporterException("exporting failed", e);
+ }
+ finally {
+ try {
+ outputStream.close();
+ }
+ catch (Exception e) {}
+ }
+ }
+
+ /**
+ * ScanningResultSelector can be implemented and passed to {@link ExportProcessor#process(ScanningResultList, String)}
+ */
+ public static interface ScanningResultSelector {
+ boolean isResultSelected(int index, ScanningResult result);
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/Exporter.java b/ipscan/src/net/azib/ipscan/exporters/Exporter.java
new file mode 100755
index 00000000..3025e3ad
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/Exporter.java
@@ -0,0 +1,78 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An Exporter is a class, which is able to export scanning results into a
+ * specific output format.
+ *
+ * This interface is callback-like. Each method of it is called when more
+ * data is available for writing.
+ *
+ * The sequence of calling:
+ * start, setFetchers, nextAddressResult, ..., end
+ *
+ * Exporters are created by cloning (prototype pattern).
+ *
+ * @author anton
+ */
+public interface Exporter extends Cloneable {
+
+ /**
+ * @return label ID, representing the name of this exporter
+ */
+ public String getLabel();
+
+ /**
+ * @return the filename extension of the file type this Exporter produces (like txt, html, etc)
+ */
+ public String getFilenameExtension();
+
+ /**
+ * @param append determines whether to append to the existing file or create a new one.
+ * false by default.
+ */
+ public void setAppend(boolean append);
+
+ /**
+ * Called on start of the exporting.
+ * @param outputStream this OutputStream should be used to output exported data.
+ * @param feederInfo summary of feeder options, which were used for this scan
+ * @throws IOException
+ */
+ public void start(OutputStream outputStream, String feederInfo) throws IOException;
+
+ /**
+ * Called when no more data is available for exporting. This is the last
+ * method, which is called on any exporter.
+ * @throws IOException
+ */
+ public void end() throws IOException;
+
+ /**
+ * Called after the start to provide the whole list of fetchers
+ * @param fetcherNames
+ * @throws IOException
+ */
+ public void setFetchers(String[] fetcherNames) throws IOException;
+
+ /**
+ * Called to provide the actual scanning results for the IP address.
+ * @param results the results, returned by the Fetcher. This is an array of String
+ * most of the time or objects, which provide toString() methods.
+ * The IP address itself is the first element in the provided array.
+ * Any element of results can be null.
+ * @throws IOException
+ */
+ public void nextAdressResults(Object[] results) throws IOException;
+
+ /**
+ * Clones the Exporter instance
+ */
+ public Object clone() throws CloneNotSupportedException;
+
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/ExporterException.java b/ipscan/src/net/azib/ipscan/exporters/ExporterException.java
new file mode 100755
index 00000000..d6af65b9
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/ExporterException.java
@@ -0,0 +1,23 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+/**
+ * Exception for throwing in case of problems in Exporters.
+ *
+ * @author anton
+ */
+public class ExporterException extends IllegalArgumentException {
+
+ static final long serialVersionUID = 746237846273847L;
+
+ public ExporterException(String message) {
+ super(message);
+ }
+
+ public ExporterException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/ExporterRegistry.java b/ipscan/src/net/azib/ipscan/exporters/ExporterRegistry.java
new file mode 100755
index 00000000..e32b30a5
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/ExporterRegistry.java
@@ -0,0 +1,58 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * The registry of all Exporters.
+ * It registers both plugins and builtins.
+ *
+ * @author anton
+ */
+public class ExporterRegistry {
+
+ /** All available Exporter implementations, Map of Exporter instances (prototypes) */
+ private Map exporters;
+
+ public ExporterRegistry(Exporter[] registeredExporters) {
+ exporters = new LinkedHashMap();
+
+ for (int i = 0; i < registeredExporters.length; i++) {
+ exporters.put(registeredExporters[i].getFilenameExtension(), registeredExporters[i]);
+ }
+ }
+
+ /**
+ * Iterates Exporter instances within this Registry
+ */
+ public Iterator iterator() {
+ return exporters.values().iterator();
+ }
+
+ /**
+ * Creates a new exporter instance examining the extension of the provided file name
+ * @param fileName the file name (with extension)
+ * @throws ExporterException in case such exporter is not registered
+ */
+ public Exporter createExporter(String fileName) throws ExporterException {
+
+ int extensionPos = fileName.lastIndexOf('.') + 1;
+ String extension = fileName.substring(extensionPos);
+
+ Exporter prototype = (Exporter) exporters.get(extension);
+ if (prototype == null) {
+ throw new ExporterException("exporter.unknown");
+ }
+ try {
+ return (Exporter) prototype.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ // this is not possible
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/IPListExporter.java b/ipscan/src/net/azib/ipscan/exporters/IPListExporter.java
new file mode 100755
index 00000000..840702e3
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/IPListExporter.java
@@ -0,0 +1,120 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.PortIterator;
+
+/**
+ * IP List Exporter
+ *
+ * Exports only IP:port info, outputting each distinct IP:port pair on separate line.
+ *
+ * @author anton
+ */
+public class IPListExporter implements Exporter {
+
+ /* CSV delimeter character */
+ static final char DELIMETER = ':';
+
+ private int ipFetcherIndex;
+ private int portsFetcherIndex;
+ private PrintWriter output;
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getLabel()
+ */
+ public String getLabel() {
+ return "exporter.ipList";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getFilenameExtension()
+ */
+ public String getFilenameExtension() {
+ return "lst";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setAppend(boolean)
+ */
+ public void setAppend(boolean append) {
+ // no difference in this fetcher
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#start(java.io.OutputStream, String)
+ */
+ public void start(OutputStream outputStream, String feederInfo) {
+ output = new PrintWriter(new OutputStreamWriter(outputStream));
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#end()
+ */
+ public void end() throws IOException {
+ if (output.checkError()) {
+ throw new IOException();
+ }
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setFetchers(String[])
+ */
+ public void setFetchers(String[] fetcherNames) throws IOException {
+ ipFetcherIndex = findFetcherByLabel("fetcher.ip", fetcherNames);
+ portsFetcherIndex = findFetcherByLabel("fetcher.ports", fetcherNames);
+ }
+
+ /**
+ * Searches for the needed fetcher by name.
+ *
+ * @param label
+ * @param fetcherNames
+ * @return fetcher's index
+ * @throws ExporterException in case fetcher is not found
+ */
+ static int findFetcherByLabel(String label, String[] fetcherNames) {
+ String fetcherName = Labels.getLabel(label);
+ for (int i = 0; i < fetcherNames.length; i++) {
+ if (fetcherName.equals(fetcherNames[i])) {
+ return i;
+ }
+ }
+ throw new ExporterException("fetcher.notFound");
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#nextAdressResults(InetAddress, Object[])
+ */
+ public void nextAdressResults(Object[] results) throws IOException {
+ String address = results[ipFetcherIndex].toString();
+ String portList;
+ try {
+ portList = results[portsFetcherIndex].toString();
+ }
+ catch (Exception e) {
+ // ignore empty results
+ return;
+ }
+
+ if (portList != null) {
+ for (PortIterator i = new PortIterator(portList); i.hasNext(); ) {
+ output.println(address + DELIMETER + i.next());
+ }
+ }
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#clone()
+ */
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/TXTExporter.java b/ipscan/src/net/azib/ipscan/exporters/TXTExporter.java
new file mode 100755
index 00000000..393404c9
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/TXTExporter.java
@@ -0,0 +1,138 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.util.Date;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.Version;
+
+/**
+ * TXT Exporter
+ *
+ * @author anton
+ */
+public class TXTExporter implements Exporter {
+
+ /** Newline character */
+ static final String NEWLINE = System.getProperty("line.separator");
+
+ private Writer output;
+ private boolean isAppend;
+ int[] padLengths;
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getLabel()
+ */
+ public String getLabel() {
+ return "exporter.txt";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getFilenameExtension()
+ */
+ public String getFilenameExtension() {
+ return "txt";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setAppend(boolean)
+ */
+ public void setAppend(boolean append) {
+ isAppend = append;
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#start(java.io.OutputStream, String)
+ */
+ public void start(OutputStream outputStream, String feederInfo) throws IOException {
+ output = new OutputStreamWriter(outputStream, Labels.getLabel("encoding"));
+ if (!isAppend) {
+ output.write(Labels.getLabel("exporter.txt.generated"));
+ println(Version.FULL_NAME);
+ println(Version.WEBSITE);
+ output.write(NEWLINE);
+
+ String scanned = Labels.getLabel("exporter.txt.scanned");
+ scanned = scanned.replaceFirst("%INFO", feederInfo);
+ println(scanned);
+ println(DateFormat.getDateTimeInstance().format(new Date()));
+ output.write(NEWLINE);
+ }
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#end()
+ */
+ public void end() throws IOException {
+ output.flush();
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setFetchers(String[])
+ */
+ public void setFetchers(String[] fetcherNames) throws IOException {
+ padLengths = new int[fetcherNames.length];
+ for (int i = 0; i < fetcherNames.length; i++) {
+ padLengths[i] = fetcherNames[i].length() * 3;
+ if (!isAppend) {
+ output.write(pad(fetcherNames[i], padLengths[i]));
+ }
+ }
+ if (!isAppend) {
+ output.write(NEWLINE);
+ }
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#nextAdressResults(InetAddress, Object[])
+ */
+ public void nextAdressResults(Object[] results) throws IOException {
+ output.write(pad(results[0], padLengths[0]));
+ for (int i = 1; i < results.length; i++) {
+ Object result = results[i];
+ output.write(pad(result, padLengths[i]));
+ }
+ output.write(NEWLINE);
+ }
+
+ /**
+ * Pads the passed string with spaces.
+ * @param s
+ * @param length the total returned length, minimum is 13
+ */
+ String pad(Object o, int length) {
+ if (length < 16)
+ length = 16;
+
+ String s;
+ if (o == null)
+ s = "";
+ else
+ s = o.toString();
+
+ if (s.length() >= length) {
+ return s;
+ }
+ return s + " ".
+ substring(0, length - s.length());
+ }
+
+ void println(String s) throws IOException {
+ output.write(s);
+ output.write(NEWLINE);
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#clone()
+ */
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/exporters/XMLExporter.java b/ipscan/src/net/azib/ipscan/exporters/XMLExporter.java
new file mode 100755
index 00000000..c5bd9d96
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/exporters/XMLExporter.java
@@ -0,0 +1,120 @@
+/**
+ *
+ */
+package net.azib.ipscan.exporters;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import net.azib.ipscan.config.Version;
+
+/**
+ * XMLExporter
+ *
+ * @author anton
+ */
+public class XMLExporter implements Exporter {
+
+ static final String ENCODING = "UTF-8";
+
+ private PrintWriter output;
+ private int ipFetcherIndex;
+ private String[] fetcherNames;
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getLabel()
+ */
+ public String getLabel() {
+ return "exporter.xml";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#getFilenameExtension()
+ */
+ public String getFilenameExtension() {
+ return "xml";
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setAppend(boolean)
+ */
+ public void setAppend(boolean append) {
+ if (append) {
+ throw new ExporterException("xml.noAppend");
+ }
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#start(java.io.OutputStream, java.lang.String)
+ */
+ public void start(OutputStream outputStream, String feederInfo) throws IOException {
+ output = new PrintWriter(new OutputStreamWriter(outputStream, ENCODING));
+ output.println("");
+ output.println("");
+ output.println("");
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
+ output.println("");
+
+ // this is a hack to extract feeder name from the feederInfo
+ // this may not work with some non-standard feeders
+ int colonPos = feederInfo.indexOf(':');
+ String feederName = null;
+ if (colonPos >= 0) {
+ feederName = feederInfo.substring(0, colonPos);
+ feederInfo = feederInfo.substring(colonPos + 1);
+ }
+ output.print("\t");
+ output.print("");
+ output.println("");
+
+ output.println("\t");
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#end()
+ */
+ public void end() throws IOException {
+ output.println("\t");
+ output.println("");
+ if (output.checkError()) {
+ throw new IOException();
+ }
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#setFetchers(java.lang.String[])
+ */
+ public void setFetchers(String[] fetcherNames) throws IOException {
+ ipFetcherIndex = IPListExporter.findFetcherByLabel("fetcher.ip", fetcherNames);
+ this.fetcherNames = fetcherNames;
+ }
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#nextAdressResults(java.lang.Object[])
+ */
+ public void nextAdressResults(Object[] results) throws IOException {
+ output.println("\t\t");
+
+ for (int i = 0; i < results.length; i++) {
+ if (results[i] != null) {
+ output.println("\t\t\t");
+ }
+ }
+
+ output.println("\t\t");
+ }
+
+
+ /*
+ * @see net.azib.ipscan.exporters.Exporter#clone()
+ */
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/feeders/Feeder.java b/ipscan/src/net/azib/ipscan/feeders/Feeder.java
new file mode 100755
index 00000000..e1facd9c
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/feeders/Feeder.java
@@ -0,0 +1,56 @@
+/**
+ *
+ */
+package net.azib.ipscan.feeders;
+
+import java.net.InetAddress;
+
+/**
+ * Interface of a Feeder, which is used to feed scanner with IP addresses.
+ * Basically, classes implementing Feeder must provide an algorithm of
+ * sequentially generating the list of scanned IP addresses.
+ *
+ * Feeders are created with an empty constructor only once in the applications life time.
+ * All subsequent calls to the {@link #initialize(String[])} should reset the state of
+ * the Feeder and begin a new "feeding" process.
+ *
+ * @author anton
+ */
+public interface Feeder {
+
+ /**
+ * @return label ID, representing the name of this Feeder
+ */
+ public String getLabel();
+
+ /**
+ * Initializes the Feeder, passing Strings as initialization parameters.
+ * This method is used for resetting the state of the Feeder (similar to a constructor)
+ * in both GUI and console interfaces.
+ *
+ * @param params the meaning and the number of these Strings depend on the implementation.
+ * @return the number of consumed parameters
+ */
+ public int initialize(String[] params);
+
+ /**
+ * @return true in case there are more IPs left for processing
+ */
+ public boolean hasNext();
+
+ /**
+ * @return the next IP for processing
+ */
+ public InetAddress next();
+
+ /**
+ * @return value from 0 to 100, describing the amount of work already done
+ */
+ public int getPercentageComplete();
+
+ /**
+ * @return information about feeder's current settings.
+ * Used for creation of Favorites, saving to file, etc.
+ */
+ public String getInfo();
+}
diff --git a/ipscan/src/net/azib/ipscan/feeders/FeederException.java b/ipscan/src/net/azib/ipscan/feeders/FeederException.java
new file mode 100755
index 00000000..b69290b8
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/feeders/FeederException.java
@@ -0,0 +1,24 @@
+/**
+ *
+ */
+package net.azib.ipscan.feeders;
+
+/**
+ * Exception for throwing in case of problems with Feeders.
+ *
+ * @author anton
+ */
+public class FeederException extends IllegalArgumentException {
+
+ static final long serialVersionUID = 746237846273847L;
+
+ public FeederException(String message) {
+ super(message);
+ }
+
+ public FeederException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/feeders/FileFeeder.java b/ipscan/src/net/azib/ipscan/feeders/FileFeeder.java
new file mode 100755
index 00000000..ba12f569
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/feeders/FileFeeder.java
@@ -0,0 +1,129 @@
+/**
+ *
+ */
+package net.azib.ipscan.feeders;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+
+import net.azib.ipscan.core.InetAddressUtils;
+
+/**
+ * Feeder, taking IP addresses from text files in any format.
+ * It uses regular expressions for matching of IP addresses.
+ * TODO: tests!!!
+ *
+ * @author anton
+ */
+public class FileFeeder implements Feeder {
+
+ /** Found IP address Strings are put here */
+ private List foundIPAddresses;
+ private Iterator foundIPAddressesIterator;
+
+ /** Total number of found IP addresses. Equivalent to foundIPAddresses.size(),
+ * which is very ineffective in case of a LinkedList.
+ */
+ private int totalAddresses;
+ private int currentIndex;
+
+ /**
+ * @see Feeder#getLabel()
+ */
+ public String getLabel() {
+ return "feeder.file";
+ }
+
+ /**
+ * Initializes the FileFeeder with required parameters
+ * @see Feeder#initialize(String[])
+ * @param params 1 parameter:
+ * params[0] fileName
+ */
+ public int initialize(String[] params) {
+ initialize(params[0]);
+ return 1;
+ }
+
+ public void initialize(String fileName) {
+ try {
+ initialize(new FileReader(fileName));
+ }
+ catch (FileNotFoundException e) {
+ throw new FeederException("file.notExists");
+ }
+ }
+
+ void initialize(Reader reader) {
+ BufferedReader fileReader = new BufferedReader(reader);
+
+ totalAddresses = 0;
+ currentIndex = 0;
+ foundIPAddresses = new LinkedList();
+ try {
+ String fileLine;
+ while ((fileLine = fileReader.readLine()) != null) {
+ Matcher matcher = InetAddressUtils.IP_ADDRESS_REGEX.matcher(fileLine);
+ while (matcher.find()) {
+ foundIPAddresses.add(matcher.group());
+ totalAddresses++;
+ }
+ }
+ if (totalAddresses == 0) {
+ throw new FeederException("file.nothingFound");
+ }
+ }
+ catch (IOException e) {
+ throw new FeederException("file.errorWhileReading");
+ }
+ finally {
+ try {
+ fileReader.close();
+ }
+ catch (IOException e) {
+ // ignore, what else to do?
+ }
+ }
+
+ foundIPAddressesIterator = foundIPAddresses.iterator();
+ }
+
+ public int getPercentageComplete() {
+ return Math.round((float)currentIndex * 100 / totalAddresses);
+ }
+
+ public boolean hasNext() {
+ return foundIPAddressesIterator.hasNext();
+ }
+
+ public InetAddress next() {
+ try {
+ currentIndex++;
+ return InetAddress.getByName((String) foundIPAddressesIterator.next());
+ }
+ catch (UnknownHostException e) {
+ Logger.global.log(Level.WARNING, "malformedIP", e);
+ throw new FeederException("malformedIP");
+ }
+ }
+
+ /**
+ * @see net.azib.ipscan.feeders.Feeder#getInfo()
+ */
+ public String getInfo() {
+ // let's return the number of found addresses
+ return Integer.toString(totalAddresses);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/feeders/RandomFeeder.java b/ipscan/src/net/azib/ipscan/feeders/RandomFeeder.java
new file mode 100755
index 00000000..a08a75ad
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/feeders/RandomFeeder.java
@@ -0,0 +1,111 @@
+/**
+ *
+ */
+package net.azib.ipscan.feeders;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+
+import net.azib.ipscan.core.InetAddressUtils;
+
+/**
+ * A feeder, that generates random IP addresses.
+ *
+ * @author anton
+ */
+public class RandomFeeder implements Feeder {
+
+ SecureRandom random = new SecureRandom();
+ InetAddress currentAddress;
+
+ byte[] prototypeBytes;
+ byte[] maskBytes;
+ byte[] currentBytes;
+
+ int addressCount;
+ int currentNumber;
+
+ /**
+ * @see Feeder#getLabel()
+ */
+ public String getLabel() {
+ return "feeder.random";
+ }
+
+ /**
+ * Initializes the RandomFeeder with required parameters
+ * @see Feeder#initialize(String[])
+ * @param params 3 parameters:
+ * params[0] prototypeIP
+ * params[1] mask
+ * params[2] count
+ */
+ public int initialize(String[] params) {
+ try {
+ initialize(params[0], params[1], Integer.parseInt(params[2]));
+ return 3;
+ }
+ catch (NumberFormatException e) {
+ throw new FeederException("random.invalidCount");
+ }
+ }
+
+ public void initialize(String prototypeIP, String mask, int count) {
+ try {
+ this.prototypeBytes = InetAddress.getByName(prototypeIP).getAddress();
+ }
+ catch (UnknownHostException e) {
+ throw new FeederException("malformedIP");
+ }
+
+ try {
+ this.maskBytes = InetAddressUtils.parseNetmask(mask).getAddress();
+ }
+ catch (UnknownHostException e) {
+ throw new FeederException("invalidNetmask");
+ }
+
+ if (count <= 0) {
+ throw new FeederException("random.invalidCount");
+ }
+
+ this.currentNumber = 0;
+ this.addressCount = count;
+ this.currentBytes = new byte[prototypeBytes.length];
+ }
+
+ public int getPercentageComplete() {
+ return Math.round((float)currentNumber * 100 / addressCount);
+ }
+
+ public boolean hasNext() {
+ return currentNumber < addressCount;
+ }
+
+ public InetAddress next() {
+ currentNumber++;
+ random.nextBytes(currentBytes);
+ try {
+ InetAddressUtils.maskPrototypeAddressBytes(currentBytes, maskBytes, prototypeBytes);
+ return InetAddress.getByAddress(currentBytes);
+ }
+ catch (UnknownHostException e) {
+ // this should never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @see net.azib.ipscan.feeders.Feeder#getInfo()
+ */
+ public String getInfo() {
+ try {
+ return InetAddress.getByAddress(prototypeBytes).getHostAddress() + " / " + InetAddress.getByAddress(maskBytes).getHostAddress() + ": " + addressCount;
+ }
+ catch (UnknownHostException e) {
+ assert false : e;
+ return null;
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/feeders/RangeFeeder.java b/ipscan/src/net/azib/ipscan/feeders/RangeFeeder.java
new file mode 100755
index 00000000..e81d8c44
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/feeders/RangeFeeder.java
@@ -0,0 +1,108 @@
+/**
+ *
+ */
+package net.azib.ipscan.feeders;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import net.azib.ipscan.core.InetAddressUtils;
+
+import org.savarese.vserv.tcpip.OctetConverter;
+
+/**
+ * IP Range Feeder.
+ * It contains the starting and ending values, which
+ * are then iterated sequentially.
+ *
+ * @author anton
+ */
+public class RangeFeeder implements Feeder {
+
+ private InetAddress startIP;
+ private InetAddress endIP;
+ private InetAddress originalEndIP;
+ private InetAddress currentIP;
+
+ double percentageComplete;
+ double percentageIncrement;
+
+ /**
+ * @see Feeder#getLabel()
+ */
+ public String getLabel() {
+ return "feeder.range";
+ }
+
+ /**
+ * Initializes the RangeFeeder with required parameters
+ * @see Feeder#initialize(String[])
+ * @param params 2 IP addresses:
+ * params[0] - startIP
+ * params[1] - endIP
+ */
+ public int initialize(String[] params) {
+ initialize(params[0], params[1]);
+ return 2;
+ }
+
+ public void initialize(String startIP, String endIP) {
+ try {
+ this.startIP = this.currentIP = InetAddress.getByName(startIP);
+ this.endIP = this.originalEndIP = InetAddress.getByName(endIP);
+ }
+ catch (UnknownHostException e) {
+ throw new FeederException("malformedIP");
+ }
+ if (InetAddressUtils.greaterThan(this.startIP, this.endIP)) {
+ throw new FeederException("range.greaterThan");
+ }
+ initPercentageIncrement();
+ this.endIP = InetAddressUtils.increment(this.endIP);
+ }
+
+ /**
+ * Initalizes fields, used for computation of percentage of completion.
+ */
+ private void initPercentageIncrement() {
+ // Warning: IPv4 specific code!
+ long rawEndIP = OctetConverter.octetsToInt(this.endIP.getAddress());
+ long rawStartIP = OctetConverter.octetsToInt(this.startIP.getAddress());
+ // make 32-bit usigned values
+ rawEndIP = rawEndIP >= 0 ? rawEndIP : rawEndIP + Integer.MAX_VALUE;
+ rawStartIP = rawStartIP >= 0 ? rawStartIP : rawStartIP + Integer.MAX_VALUE;
+ // compute 1% of the whole range
+ percentageIncrement = 100.0/(rawEndIP - rawStartIP + 1);
+ percentageComplete = 0;
+ }
+
+ /**
+ * @see net.azib.ipscan.feeders.Feeder#hasNext()
+ */
+ public boolean hasNext() {
+ // equals() is faster than greaterThan()
+ return !currentIP.equals(endIP);
+ }
+
+ /**
+ * @see net.azib.ipscan.feeders.Feeder#next()
+ */
+ public InetAddress next() {
+ percentageComplete += percentageIncrement;
+ InetAddress prevIP = this.currentIP;
+ this.currentIP = InetAddressUtils.increment(prevIP);
+ return prevIP;
+ }
+
+ public int getPercentageComplete() {
+ return (int)Math.round(percentageComplete);
+ }
+
+ /**
+ * @see net.azib.ipscan.feeders.Feeder#getInfo()
+ */
+ public String getInfo() {
+ // let's return the range
+ return startIP.getHostAddress() + " - " + originalEndIP.getHostAddress();
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/feeders/SmartTextFeeder.java b/ipscan/src/net/azib/ipscan/feeders/SmartTextFeeder.java
new file mode 100755
index 00000000..b8cc6843
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/feeders/SmartTextFeeder.java
@@ -0,0 +1,67 @@
+/**
+ *
+ */
+package net.azib.ipscan.feeders;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import net.azib.ipscan.core.InetAddressUtils;
+
+import org.savarese.vserv.tcpip.OctetConverter;
+
+/**
+ * Smart text feeder for advenced users.
+ *
+ * TODO: implement SmartTextFeeder to accept text, e.g.
+ * 127.0.0.1-255
+ * 127.0-10.13-15.1
+ * 127.0.0.1/24
+ *
+ * Warning: IPv4-specific!
+ *
+ * @author anton
+ */
+public class SmartTextFeeder implements Feeder {
+
+ private String netmask;
+
+ public String getLabel() {
+ return null;
+ }
+
+ public int initialize(String[] params) {
+ return 0;
+ }
+
+ public void initialize(String text) {
+ // remove all whitespace
+ text = text.replaceAll("\\w+", "");
+
+ // extract netmask
+ int slashPos = text.indexOf('/');
+ if (slashPos >= 0) {
+ netmask = text.substring(slashPos+1);
+ text = text.substring(0, slashPos);
+ }
+
+ String[] tokens = text.split("\\.");
+ // TODO: use port list parsing code here
+ }
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public InetAddress next() {
+ return null;
+ }
+
+ public int getPercentageComplete() {
+ return 0;
+ }
+
+ public String getInfo() {
+ return null;
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/CommentFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/CommentFetcher.java
new file mode 100755
index 00000000..27769716
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/CommentFetcher.java
@@ -0,0 +1,39 @@
+/**
+ * This file is a part of Angry IP Scanner source code,
+ * see http://www.azib.net/ for more information.
+ */
+package net.azib.ipscan.fetchers;
+
+import net.azib.ipscan.core.ScanningSubject;
+
+/**
+ * A fetcher for displaying of user-defined comments about every IP address.
+ *
+ * TODO: implement CommentFetcher
+ * TODO: make an editor for comments
+ *
+ * @author anton
+ */
+public class CommentFetcher implements Fetcher {
+
+ /**
+ * @see net.azib.ipscan.fetchers.Fetcher#getLabel()
+ */
+ public String getLabel() {
+ return "fetcher.ip";
+ }
+
+ /**
+ * @see net.azib.ipscan.fetchers.Fetcher#scan(net.azib.ipscan.core.ScanningSubject)
+ */
+ public Object scan(ScanningSubject subject) {
+ return "a dummy comment!!!";
+ }
+
+ public void init() {
+ }
+
+ public void cleanup() {
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/Fetcher.java b/ipscan/src/net/azib/ipscan/fetchers/Fetcher.java
new file mode 100755
index 00000000..919e906e
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/Fetcher.java
@@ -0,0 +1,50 @@
+/**
+ * This file is a part of Angry IP Scanner source code,
+ * see http://www.azib.net/ for more information.
+ */
+package net.azib.ipscan.fetchers;
+
+import net.azib.ipscan.core.NotAvailableValue;
+import net.azib.ipscan.core.NotScannedValue;
+import net.azib.ipscan.core.ScanningSubject;
+
+/**
+ * Interface of all IP Fetchers.
+ *
+ * Fetcher is responsible for gathering a certain type of
+ * information about the provided scanning subject
+ * (in GUI terms, Fetcher is a column in the results list).
+ *
+ * Fetchers do the actual information fetching about each
+ * scanned IP address.
+ *
+ * Instances of this classes are shared among all the threads,
+ * so implementations must be thread safe and stateless.
+ *
+ * @author anton
+ */
+public interface Fetcher extends Cloneable {
+
+ /**
+ * @return label ID, representing the name of this fetcher
+ */
+ public String getLabel();
+
+ /**
+ * Does the actual fetching.
+ * @param subject the scanning subject, containing an IP address
+ * @return the fetched data (a String in most cases), null in case of any error.
+ * Special values may also be returned, such as {@link NotAvailableValue} or {@link NotScannedValue}
+ */
+ public Object scan(ScanningSubject subject);
+
+ /**
+ * Called before scanning has started to do any intialization stuff
+ */
+ public void init();
+
+ /**
+ * Called after the scanning has been completed to do any cleanup needed
+ */
+ public void cleanup();
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/FetcherException.java b/ipscan/src/net/azib/ipscan/fetchers/FetcherException.java
new file mode 100755
index 00000000..6c83fc2e
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/FetcherException.java
@@ -0,0 +1,19 @@
+/**
+ *
+ */
+package net.azib.ipscan.fetchers;
+
+/**
+ * FetcherException
+ *
+ * @author anton
+ */
+public class FetcherException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public FetcherException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistry.java b/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistry.java
new file mode 100755
index 00000000..4560c787
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistry.java
@@ -0,0 +1,46 @@
+/**
+ *
+ */
+package net.azib.ipscan.fetchers;
+
+import java.util.Collection;
+
+/**
+ * FetcherRegistry
+ *
+ * @author anton
+ */
+public interface FetcherRegistry {
+
+ /**
+ * @return a List of all registered Fetchers
+ */
+ public Collection getRegisteredFetchers();
+
+ /**
+ * @return a List of selected Fetchers only
+ */
+ public Collection getSelectedFetchers();
+
+ /**
+ * Searches for selected fetcher with the given label
+ * @param label
+ * @return the index, if found, or -1
+ */
+ public int getSelectedFetcherIndex(String label);
+
+ /**
+ * Updates the list, retaining only items that are passed in the array.
+ * The order of elements will be the same as in the array.
+ *
+ * @param names
+ */
+ public void updateSelectedFetchers(String[] names);
+
+ /**
+ * Adds a listener to observe FetcherRegistry events, like modification of selected fetchers.
+ * @param listener
+ */
+ public void addListener(FetcherRegistryUpdateListener listener);
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistryImpl.java b/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistryImpl.java
new file mode 100755
index 00000000..e1d5bcdd
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistryImpl.java
@@ -0,0 +1,80 @@
+/**
+ *
+ */
+package net.azib.ipscan.fetchers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Fetcher Registry singleton class.
+ * Actually, it registers both plugins and builtins.
+ *
+ * @author anton
+ */
+public class FetcherRegistryImpl implements FetcherRegistry {
+
+ /** All available Fetcher implementations, List of Fetcher instances */
+ private List registeredFetchers;
+ /** Selected for scanning Fetcher implementations, keys are fetcher labels, values are Fetcher instances */
+ private Map selectedFetchers;
+ /** A collection of update listeners - observers of FetcherRegistry */
+ private List updateListeners = new ArrayList();
+
+ public FetcherRegistryImpl(Fetcher[] registeredFetchers) {
+ this.registeredFetchers = Arrays.asList(registeredFetchers);
+ this.registeredFetchers = Collections.unmodifiableList(this.registeredFetchers);
+
+ // TODO: this should be loaded from config as well as reasonable defaults should be made
+ this.selectedFetchers = new LinkedHashMap();
+ for (Iterator i = this.registeredFetchers.iterator(); i.hasNext();) {
+ Fetcher fetcher = (Fetcher) i.next();
+ this.selectedFetchers.put(fetcher.getLabel(), fetcher);
+ }
+ }
+
+ public void addListener(FetcherRegistryUpdateListener listener) {
+ updateListeners.add(listener);
+ }
+
+ public Collection getRegisteredFetchers() {
+ return registeredFetchers;
+ }
+
+ public Collection getSelectedFetchers() {
+ return selectedFetchers.values();
+ }
+
+ public int getSelectedFetcherIndex(String label) {
+ int index = -1;
+ for (Iterator i = selectedFetchers.values().iterator(); i.hasNext();) {
+ if (((Fetcher)i.next()).getLabel().equals(label))
+ break;
+ index++;
+
+ }
+ return index;
+ }
+
+ public void updateSelectedFetchers(String[] labels) {
+ // rebuild the map (to recreate the new order of elements)
+ Map newList = new LinkedHashMap();
+ for (int i = 0; i < labels.length; i++) {
+ newList.put(labels[i], selectedFetchers.get(labels[i]));
+ }
+ selectedFetchers = newList;
+
+ // invorm observers
+ for (Iterator i = updateListeners.iterator(); i.hasNext();) {
+ FetcherRegistryUpdateListener listener = (FetcherRegistryUpdateListener) i.next();
+ listener.handleUpdateOfSelectedFetchers(this);
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistryUpdateListener.java b/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistryUpdateListener.java
new file mode 100755
index 00000000..03e08c7e
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/FetcherRegistryUpdateListener.java
@@ -0,0 +1,20 @@
+/**
+ *
+ */
+package net.azib.ipscan.fetchers;
+
+/**
+ * FetcherRegistryUpdateListener.
+ * Implement this interface if you need to react to FetcherRegistry updates.
+ *
+ * @author Anton Keks
+ */
+public interface FetcherRegistryUpdateListener {
+
+ /**
+ * This method is called when the list of selected Fetchers was changed.
+ * @param fetcherRegistry
+ */
+ void handleUpdateOfSelectedFetchers(FetcherRegistry fetcherRegistry);
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/FilteredPortsFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/FilteredPortsFetcher.java
new file mode 100755
index 00000000..24ec3fdf
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/FilteredPortsFetcher.java
@@ -0,0 +1,27 @@
+/**
+ *
+ */
+package net.azib.ipscan.fetchers;
+
+import java.util.Set;
+
+import net.azib.ipscan.core.ScanningSubject;
+
+/**
+ * FilteredPortsFetcher uses the scanning results of PortsFetcher to display filtered ports.
+ *
+ * @author anton
+ */
+public class FilteredPortsFetcher extends PortsFetcher {
+
+ public String getLabel() {
+ return "fetcher.ports.filtered";
+ }
+
+ public Object scan(ScanningSubject subject) {
+ scanPorts(subject);
+ Set filteredPorts = getFilteredPorts(subject);
+ return filteredPorts.size() > 0 ? portListToRange(filteredPorts, displayAsRanges) : null;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/HostnameFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/HostnameFetcher.java
new file mode 100755
index 00000000..fc3dd307
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/HostnameFetcher.java
@@ -0,0 +1,38 @@
+/**
+ * This file is a part of Angry IP Scanner source code,
+ * see http://www.azib.net/ for more information.
+ */
+package net.azib.ipscan.fetchers;
+
+import net.azib.ipscan.core.ScanningSubject;
+
+/**
+ * HostnameFetcher retrieves hostnames of IP addresses by reverse DNS lookups.
+ *
+ * @author anton
+ */
+public class HostnameFetcher implements Fetcher {
+
+ /**
+ * @see net.azib.ipscan.fetchers.Fetcher#getLabel()
+ */
+ public String getLabel() {
+ return "fetcher.hostname";
+ }
+
+ /**
+ * @see net.azib.ipscan.fetchers.Fetcher#scan(net.azib.ipscan.core.ScanningSubject)
+ */
+ public Object scan(ScanningSubject subject) {
+ String hostname = subject.getIPAddress().getCanonicalHostName();
+ // return the returned hostname only if it is not the same as the IP address (this is how the above method works)
+ return subject.getIPAddress().getHostAddress().equals(hostname) ? null : hostname;
+ }
+
+ public void init() {
+ }
+
+ public void cleanup() {
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/IPFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/IPFetcher.java
new file mode 100755
index 00000000..6dc4a909
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/IPFetcher.java
@@ -0,0 +1,37 @@
+/**
+ * This file is a part of Angry IP Scanner source code,
+ * see http://www.azib.net/ for more information.
+ */
+package net.azib.ipscan.fetchers;
+
+import net.azib.ipscan.core.ScanningSubject;
+
+/**
+ * Dummy fetcher, which is able to return the textual representation
+ * of the passed IP address.
+ *
+ * @author anton
+ */
+public class IPFetcher implements Fetcher {
+
+ /**
+ * @see net.azib.ipscan.fetchers.Fetcher#getLabel()
+ */
+ public String getLabel() {
+ return "fetcher.ip";
+ }
+
+ /**
+ * @see net.azib.ipscan.fetchers.Fetcher#scan(net.azib.ipscan.core.ScanningSubject)
+ */
+ public Object scan(ScanningSubject subject) {
+ return subject.getIPAddress().getHostAddress();
+ }
+
+ public void init() {
+ }
+
+ public void cleanup() {
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/PacketLossFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/PacketLossFetcher.java
new file mode 100755
index 00000000..e17d53fc
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/PacketLossFetcher.java
@@ -0,0 +1,19 @@
+/**
+ *
+ */
+package net.azib.ipscan.fetchers;
+
+import net.azib.ipscan.core.net.PingerRegistry;
+
+/**
+ * TODO PacketLossFetcher
+ *
+ * @author anton
+ */
+public class PacketLossFetcher extends PingFetcher {
+
+ public PacketLossFetcher(PingerRegistry pingerRegistry) {
+ super(pingerRegistry);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/PingFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/PingFetcher.java
new file mode 100755
index 00000000..11416850
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/PingFetcher.java
@@ -0,0 +1,101 @@
+/**
+ * This file is a part of Angry IP Scanner source code,
+ * see http://www.azib.net/ for more information.
+ */
+package net.azib.ipscan.fetchers;
+
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.core.IntegerWithUnit;
+import net.azib.ipscan.core.ScanningSubject;
+import net.azib.ipscan.core.net.PingResult;
+import net.azib.ipscan.core.net.Pinger;
+import net.azib.ipscan.core.net.PingerRegistry;
+
+/**
+ * PingFetcher is able to ping IP addresses.
+ * It returns the average round trip time of all pings sent.
+ *
+ * @author anton
+ */
+public class PingFetcher implements Fetcher {
+
+ public static final String PARAMETER_PINGER = "pinger";
+
+ /** The shared pinger - this one must be static, because PingTTLFetcher will use it as well */
+ private static Pinger pinger;
+
+ /** The registry used for creation of Pinger instances */
+ private PingerRegistry pingerRegistry;
+
+ public PingFetcher(PingerRegistry pingerRegistry) {
+ this.pingerRegistry = pingerRegistry;
+ }
+
+ public String getLabel() {
+ return "fetcher.ping";
+ }
+
+ protected PingResult executePing(ScanningSubject subject) {
+
+ PingResult result = null;
+
+ if (subject.hasParameter(PARAMETER_PINGER)) {
+ result = (PingResult) subject.getParameter(PARAMETER_PINGER);
+ }
+ else {
+ try {
+ result = pinger.ping(subject.getIPAddress(), Config.getGlobal().pingCount);
+ }
+ catch (IOException e) {
+ // if this is not a timeout
+ Logger.global.log(Level.WARNING, "Pinging failed", e);
+ // return an empty ping result
+ result = new PingResult(subject.getIPAddress());
+ }
+ // remember the result for other fetchers to use
+ subject.setParameter(PARAMETER_PINGER, result);
+ }
+ return result;
+ }
+
+ public Object scan(ScanningSubject subject) {
+ PingResult result = executePing(subject);
+ subject.setResultType(result.isAlive() ? ScanningSubject.RESULT_TYPE_ALIVE : ScanningSubject.RESULT_TYPE_DEAD);
+
+ if (!result.isAlive() && !Config.getGlobal().scanDeadHosts) {
+ // the host is dead, we are not going to continue...
+ subject.abortScanning();
+ }
+
+ return result.isAlive() ? new IntegerWithUnit(result.getAverageTime(), "fetcher.value.ms") : null;
+ }
+
+ public void init() {
+ try {
+ if (pinger == null) {
+ pinger = pingerRegistry.createPinger(Config.getGlobal().selectedPinger, Config.getGlobal().pingTimeout);
+ }
+ }
+ catch (Exception e) {
+ throw new FetcherException(e);
+ }
+ }
+
+ public void cleanup() {
+ try {
+ if (pinger != null) {
+ pinger.close();
+ }
+ }
+ catch (IOException e) {
+ throw new FetcherException(e);
+ }
+ pinger = null;
+ }
+
+
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/PingTTLFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/PingTTLFetcher.java
new file mode 100755
index 00000000..41ac5307
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/PingTTLFetcher.java
@@ -0,0 +1,32 @@
+/**
+ * This file is a part of Angry IP Scanner source code,
+ * see http://www.azib.net/ for more information.
+ */
+package net.azib.ipscan.fetchers;
+
+import net.azib.ipscan.core.ScanningSubject;
+import net.azib.ipscan.core.net.PingResult;
+import net.azib.ipscan.core.net.PingerRegistry;
+
+/**
+ * PingTTLFetcher shares pinging results with PingFetcher
+ * and returns the TTL field of the last received packet.
+ *
+ * @author anton
+ */
+public class PingTTLFetcher extends PingFetcher {
+
+ public PingTTLFetcher(PingerRegistry pingerRegistry) {
+ super(pingerRegistry);
+ }
+
+ public String getLabel() {
+ return "fetcher.ping.ttl";
+ }
+
+ public Object scan(ScanningSubject subject) {
+ PingResult result = executePing(subject);
+ subject.setResultType(result.isAlive() ? ScanningSubject.RESULT_TYPE_ALIVE : ScanningSubject.RESULT_TYPE_DEAD);
+ return result.isAlive() && result.getTTL() > 0 ? new Integer(result.getTTL()) : null;
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/fetchers/PortsFetcher.java b/ipscan/src/net/azib/ipscan/fetchers/PortsFetcher.java
new file mode 100755
index 00000000..1e6bbc9b
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/fetchers/PortsFetcher.java
@@ -0,0 +1,178 @@
+/**
+ *
+ */
+package net.azib.ipscan.fetchers;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.core.PortIterator;
+import net.azib.ipscan.core.ScanningSubject;
+import net.azib.ipscan.core.net.PingResult;
+
+/**
+ * PortsFetcher scans TCP ports.
+ * Port list is obtained using the {@link net.azib.ipscan.core.PortIterator}.
+ *
+ * TODO: return a specialized "list" object instead of a String
+ *
+ * @author anton
+ */
+public class PortsFetcher implements Fetcher {
+
+ private static final String PARAMETER_OPEN_PORTS = "openPorts";
+ private static final String PARAMETER_FILTERED_PORTS = "filteredPorts";
+
+ // initialize options for this scan
+ private int timeout = Config.getGlobal().portTimeout;
+ private boolean adaptTimeout = Config.getGlobal().adaptPortTimeout;
+ private PortIterator portIteratorPrototype = new PortIterator(Config.getGlobal().portString);
+ protected boolean displayAsRanges = true; // TODO: make configurable
+
+ /**
+ * @see net.azib.ipscan.fetchers.Fetcher#getLabel()
+ */
+ public String getLabel() {
+ return "fetcher.ports";
+ }
+
+ /**
+ * This method does the actual port scanning.
+ * It then remembers the results for other extending fetchers to use, like FilteredPortsFetcher.
+ * @param subject
+ */
+ protected void scanPorts(ScanningSubject subject) {
+ Set openPorts = getOpenPorts(subject);
+
+ if (openPorts == null) {
+ // no results are available yet, let's proceed with the scanning
+ openPorts = new TreeSet();
+ Set filteredPorts = new TreeSet();
+ subject.setParameter(PARAMETER_OPEN_PORTS, openPorts);
+ subject.setParameter(PARAMETER_FILTERED_PORTS, filteredPorts);
+
+ int adaptedTimeout = timeout;
+
+ // now try to adapt timeout if it is enabled and pinging results are availbale
+ PingResult pingResult = (PingResult) subject.getParameter(PingFetcher.PARAMETER_PINGER);
+ if (adaptTimeout && pingResult.isAlive()) {
+ // TODO: use longest time istead of the average one for adapting
+ adaptedTimeout = Math.min(Math.max(pingResult.getAverageTime() * 4, 30), timeout);
+ }
+
+ Socket socket = null;
+ // clone port iterator for performance instead of creating for every thread
+ for (PortIterator i = portIteratorPrototype.copy(); i.hasNext(); ) {
+ // TODO: UDP ports?
+ // TODO: reuse sockets?
+ socket = new Socket();
+ int port = i.next();
+ try {
+ socket.connect(new InetSocketAddress(subject.getIPAddress(), port), adaptedTimeout);
+ if (socket.isConnected()) {
+ openPorts.add(new Integer(port));
+ }
+ }
+ catch (SocketTimeoutException e) {
+ filteredPorts.add(new Integer(port));
+ }
+ catch (IOException e) {
+ // connection refused
+ assert e instanceof ConnectException : e;
+ }
+ finally {
+ if (socket != null) {
+ try {
+ socket.close();
+ }
+ catch (IOException e) {}
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @param subject
+ * @return
+ */
+ protected Set getFilteredPorts(ScanningSubject subject) {
+ return (Set) subject.getParameter(PARAMETER_FILTERED_PORTS);
+ }
+
+ /**
+ * @param subject
+ * @return
+ */
+ protected Set getOpenPorts(ScanningSubject subject) {
+ return (Set) subject.getParameter(PARAMETER_OPEN_PORTS);
+ }
+
+ /**
+ * Utility method to convert the resulting port List to String.
+ * @param portList source List of Integers
+ * @return a String
+ */
+ protected static String portListToRange(Collection portList, boolean asRanges) {
+ StringBuffer sb = new StringBuffer();
+
+ Iterator i = portList.iterator();
+ Integer prevPort = new Integer(Integer.MAX_VALUE);
+ boolean isRange = false;
+
+ if (i.hasNext()) {
+ prevPort = (Integer) i.next();
+ sb.append(prevPort);
+ }
+
+ while (i.hasNext()) {
+ Integer port = (Integer) i.next();
+
+ if (asRanges && prevPort.intValue() + 1 == port.intValue()) {
+ isRange = true;
+ }
+ else {
+ if (isRange) {
+ sb.append('-').append(prevPort);
+ isRange = false;
+ }
+ sb.append(',').append(port);
+ }
+ prevPort = port;
+ }
+
+ if (isRange) {
+ sb.append('-').append(prevPort);
+ }
+
+ return sb.toString();
+ }
+
+ /*
+ * @see net.azib.ipscan.fetchers.Fetcher#scan(net.azib.ipscan.core.ScanningSubject)
+ */
+ public Object scan(ScanningSubject subject) {
+ scanPorts(subject);
+ Set openPorts = getOpenPorts(subject);
+ boolean portsFound = openPorts.size() > 0;
+ if (portsFound) {
+ subject.setResultType(ScanningSubject.RESULT_TYPE_ADDITIONAL_INFO);
+ }
+ return portsFound ? portListToRange(openPorts, displayAsRanges) : null;
+ }
+
+ public void init() {
+ }
+
+ public void cleanup() {
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/AboutWindow.java b/ipscan/src/net/azib/ipscan/gui/AboutWindow.java
new file mode 100755
index 00000000..01a9c415
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/AboutWindow.java
@@ -0,0 +1,96 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.Version;
+import net.azib.ipscan.gui.actions.HelpActions;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * About Window
+ *
+ * @author anton
+ */
+public class AboutWindow extends AbstractModalDialog {
+
+ public AboutWindow() {
+ createShell();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell() {
+ Display currentDisplay = Display.getCurrent();
+ Shell parent = currentDisplay != null ? currentDisplay.getActiveShell() : null;
+ shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
+
+ shell.setText(Labels.getLabel("title.about"));
+ shell.setSize(new Point(400, 363));
+
+ Label iconLabel = new Label(shell, SWT.ICON);
+ iconLabel.setBounds(10, 10, 0, 0);
+
+ if (parent != null) {
+ iconLabel.setImage(parent.getImage());
+ shell.setImage(parent.getImage());
+ }
+ iconLabel.pack();
+
+ // TODO: make clicking on links work
+ Link textLabel = new Link(shell, SWT.NONE);
+ String text = Labels.getLabel("text.about");
+ text = text.replaceAll("%NAME", Version.NAME);
+ text = text.replaceAll("%VERSION", Version.VERSION);
+ text = text.replaceAll("%COPYLEFT", Version.COPYLEFT);
+ text = text.replaceAll("%WEBSITE", Version.WEBSITE);
+ text = text.replaceAll("%MAILTO", Version.MAILTO);
+ textLabel.setText(text);
+ textLabel.setBounds(60, 10, 0, 0);
+ textLabel.addListener(SWT.Selection, new HelpActions.Website());
+ textLabel.pack();
+
+ Text licenseText = new Text(shell, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.V_SCROLL | SWT.WRAP);
+ licenseText.setBounds(60, 140, 320, 160);
+ licenseText.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ licenseText.setText("Licensed under the GNU General Public License Version 2\n\n" +
+ Version.NAME + " is free software; you can redistribute it and/or " +
+ "modify it under the terms of the GNU General Public License " +
+ "as published by the Free Software Foundation; either version 2 " +
+ "of the License, or (at your option) any later version.\n\n" +
+ Version.NAME + " is distributed in the hope that it will be useful, " +
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of " +
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " +
+ "GNU General Public License for more details.\n\n" +
+ "You should have received a copy of the GNU General Public License " +
+ "along with this program; if not, write to the Free Software " +
+ "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA " +
+ "02110-1301, USA, or visit http://www.fsf.org/");
+
+ Button button = new Button(shell, SWT.NONE);
+ button.setText(Labels.getLabel("button.close"));
+ button.setBounds(170, 305, 80, 25);
+ button.setFocus();
+ button.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ shell.close();
+ shell.dispose();
+ }
+ });
+
+ shell.setDefaultButton(button);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/AbstractModalDialog.java b/ipscan/src/net/azib/ipscan/gui/AbstractModalDialog.java
new file mode 100755
index 00000000..a2992d5f
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/AbstractModalDialog.java
@@ -0,0 +1,93 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * This is the base of a modal dialog window
+ *
+ * @author anton
+ */
+public abstract class AbstractModalDialog {
+
+ protected Shell shell = null;
+
+ public void open() {
+ shell.open();
+ Display display = Display.getCurrent();
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch())
+ display.sleep();
+ }
+ shell.dispose();
+ }
+
+ // common listeners follow
+
+ protected static class UpButtonListener implements Listener {
+
+ private List list;
+
+ public UpButtonListener(List list) {
+ this.list = list;
+ }
+
+ public void handleEvent(Event event) {
+ if (list.isSelected(0)) {
+ // do not move anything if the first item is selected
+ return;
+ }
+
+ int[] selectedItems = list.getSelectionIndices();
+ for (int i = 0; i < selectedItems.length; i++) {
+ // here, index is always > 0
+ int index = selectedItems[i];
+
+ list.deselect(index);
+ String oldItem = list.getItem(index - 1);
+ list.setItem(index - 1, list.getItem(index));
+ list.setItem(index, oldItem);
+ list.select(index - 1);
+ }
+
+ list.setTopIndex(selectedItems[0] - 2);
+ }
+ }
+
+ protected static class DownButtonListener implements Listener {
+
+ private List list;
+
+ public DownButtonListener(List list) {
+ this.list = list;
+ }
+
+ public void handleEvent(Event event) {
+ if (list.isSelected(list.getItemCount() - 1)) {
+ // do not move anything if the last items is selected
+ return;
+ }
+
+ int[] selectedItems = list.getSelectionIndices();
+ for (int i = selectedItems.length - 1; i >= 0; i--) {
+ // here, index is always < getItemCount()
+ int index = selectedItems[i];
+
+ list.deselect(index);
+ String oldItem = list.getItem(index + 1);
+ list.setItem(index + 1, list.getItem(index));
+ list.setItem(index, oldItem);
+ list.select(index + 1);
+ }
+
+ list.setTopIndex(selectedItems[0]);
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/DetailsWindow.java b/ipscan/src/net/azib/ipscan/gui/DetailsWindow.java
new file mode 100755
index 00000000..26ee2003
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/DetailsWindow.java
@@ -0,0 +1,59 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Labels;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * The "Show IP Details" Window
+ *
+ * @author anton
+ */
+public class DetailsWindow extends AbstractModalDialog {
+
+ private ResultTable resultTable;
+
+ public DetailsWindow(ResultTable resultTable) {
+ this.resultTable = resultTable;
+ createShell(resultTable.getShell());
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell(Shell parent) {
+ shell = new Shell(parent, SWT.TOOL | SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE);
+ shell.setText(Labels.getLabel("title.details"));
+ shell.setSize(new Point(300, 300));
+ shell.setImage(parent.getImage());
+ FillLayout fillLayout = new FillLayout();
+ fillLayout.spacing = 3;
+ fillLayout.marginHeight = 3;
+ fillLayout.marginWidth = 3;
+ shell.setLayout(fillLayout);
+
+ Text detailsText = new Text(shell, SWT.BORDER | SWT.READ_ONLY | SWT.MULTI | SWT.V_SCROLL);
+ detailsText.setText(resultTable.getIPDetails());
+ detailsText.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+ detailsText.setTabs(32);
+
+ detailsText.addListener(SWT.Traverse, new Listener() {
+ public void handleEvent(Event e) {
+ if (e.detail == SWT.TRAVERSE_RETURN) {
+ shell.close();
+ shell.dispose();
+ }
+ }
+ });
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/EditFavoritesDialog.java b/ipscan/src/net/azib/ipscan/gui/EditFavoritesDialog.java
new file mode 100755
index 00000000..fa7d8566
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/EditFavoritesDialog.java
@@ -0,0 +1,108 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import java.util.Iterator;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.NamedListConfig;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * EditFavoritesDialog
+ *
+ * @author anton
+ */
+public class EditFavoritesDialog extends AbstractModalDialog {
+
+ private List favoritesList;
+
+ public EditFavoritesDialog() {
+ createShell();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell() {
+ Display currentDisplay = Display.getCurrent();
+ Shell parent = currentDisplay != null ? currentDisplay.getActiveShell() : null;
+ shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
+
+ shell.setText(Labels.getLabel("title.favorite.edit"));
+ shell.setSize(new Point(405, 297));
+ shell.setLayout(null);
+
+ Label messageLabel = new Label(shell, SWT.NONE);
+ messageLabel.setText(Labels.getLabel("text.favorite.edit"));
+ messageLabel.setBounds(new Rectangle(10, 10, 282, 14));
+
+ favoritesList = new List(shell, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
+ favoritesList.setBounds(new Rectangle(10, 30, 330, 200));
+ for (Iterator i = Config.getFavoritesConfig().iterateNames(); i.hasNext();) {
+ String name = (String) i.next();
+ favoritesList.add(name);
+ }
+
+ Button upButton = new Button(shell, SWT.NONE);
+ upButton.setText(Labels.getLabel("button.up"));
+ upButton.setBounds(new Rectangle(350, 30, 40, 25));
+ upButton.addListener(SWT.Selection, new UpButtonListener(favoritesList));
+
+ Button downButton = new Button(shell, SWT.NONE);
+ downButton.setText(Labels.getLabel("button.down"));
+ downButton.setBounds(new Rectangle(350, 60, 40, 25));
+ downButton.addListener(SWT.Selection, new DownButtonListener(favoritesList));
+
+ Button deleteButton = new Button(shell, SWT.NONE);
+ deleteButton.setText(Labels.getLabel("button.delete"));
+ deleteButton.setBounds(new Rectangle(350, 105, 40, 25));
+ deleteButton.addListener(SWT.Selection, new DeleteButtonListener());
+
+ Button okButton = new Button(shell, SWT.NONE);
+ okButton.setText(Labels.getLabel("button.OK"));
+ okButton.setBounds(new Rectangle(180, 238, 75, 25));
+ shell.setDefaultButton(okButton);
+
+ Button cancelButton = new Button(shell, SWT.NONE);
+ cancelButton.setText(Labels.getLabel("button.cancel"));
+ cancelButton.setBounds(new Rectangle(265, 238, 75, 25));
+
+ okButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
+ public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
+ saveFavorites();
+ shell.close();
+ }
+ });
+ cancelButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
+ public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
+ shell.close();
+ }
+ });
+ }
+
+ private void saveFavorites() {
+ NamedListConfig favoritesConfig = Config.getFavoritesConfig();
+ favoritesConfig.update(favoritesList.getItems());
+ favoritesConfig.store();
+ }
+
+ private class DeleteButtonListener implements Listener {
+ public void handleEvent(Event event) {
+ favoritesList.remove(favoritesList.getSelectionIndices());
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/EditOpenersDialog.java b/ipscan/src/net/azib/ipscan/gui/EditOpenersDialog.java
new file mode 100755
index 00000000..dafc6a50
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/EditOpenersDialog.java
@@ -0,0 +1,253 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import java.io.File;
+import java.util.Iterator;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.OpenersConfig;
+import net.azib.ipscan.config.OpenersConfig.Opener;
+import net.azib.ipscan.fetchers.Fetcher;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * EditOpenersDialog
+ *
+ * @author anton
+ */
+public class EditOpenersDialog extends AbstractModalDialog {
+
+ private FetcherRegistry fetcherRegistry;
+ private List openersList;
+ private Group editFieldsGroup;
+ private Text openerNameText;
+ private Text openerStringText;
+ private Text workingDirText;
+ private Button isInTerminalCheckbox;
+ private int currentSelectionIndex;
+
+ public EditOpenersDialog(FetcherRegistry fetcherRegistry) {
+ this.fetcherRegistry = fetcherRegistry;
+ createShell();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell() {
+ Display currentDisplay = Display.getCurrent();
+ Shell parent = currentDisplay != null ? currentDisplay.getActiveShell() : null;
+ shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
+
+ shell.setText(Labels.getLabel("title.openers.edit"));
+ shell.setSize(new Point(405, 307));
+ shell.setLayout(null);
+
+ Label messageLabel = new Label(shell, SWT.NONE);
+ messageLabel.setText(Labels.getLabel("text.openers.edit"));
+ messageLabel.setBounds(new Rectangle(10, 10, 282, 14));
+
+ openersList = new List(shell, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
+ openersList.setBounds(new Rectangle(10, 30, 135, 200));
+ for (Iterator i = Config.getOpenersConfig().iterateNames(); i.hasNext();) {
+ String name = (String) i.next();
+ openersList.add(name);
+ }
+ openersList.addListener(SWT.Selection, new ItemSelectListener());
+
+ Button upButton = new Button(shell, SWT.NONE);
+ upButton.setText(Labels.getLabel("button.up"));
+ upButton.setBounds(new Rectangle(150, 30, 40, 25));
+ upButton.addListener(SWT.Selection, new UpButtonListener(openersList));
+
+ Button downButton = new Button(shell, SWT.NONE);
+ downButton.setText(Labels.getLabel("button.down"));
+ downButton.setBounds(new Rectangle(150, 60, 40, 25));
+ downButton.addListener(SWT.Selection, new DownButtonListener(openersList));
+
+ Button addButton = new Button(shell, SWT.NONE);
+ addButton.setText(Labels.getLabel("button.add"));
+ addButton.setBounds(new Rectangle(150, 105, 40, 25));
+ addButton.addListener(SWT.Selection, new AddButtonListener());
+
+ Button deleteButton = new Button(shell, SWT.NONE);
+ deleteButton.setText(Labels.getLabel("button.delete"));
+ deleteButton.setBounds(new Rectangle(150, 135, 40, 25));
+ deleteButton.addListener(SWT.Selection, new DeleteButtonListener());
+
+ Button closeButton = new Button(shell, SWT.NONE);
+ closeButton.setText(Labels.getLabel("button.close"));
+ closeButton.setBounds(new Rectangle(315, 245, 75, 25));
+
+ editFieldsGroup = new Group(shell, SWT.NONE);
+ editFieldsGroup.setBounds(205, 30, 185, 200);
+ RowLayout rowLayout = new RowLayout(SWT.VERTICAL);
+ rowLayout.fill = true;
+ rowLayout.justify = true;
+ rowLayout.marginTop = 13;
+ editFieldsGroup.setLayout(rowLayout);
+
+ Label openerNameLabel = new Label(editFieldsGroup, SWT.NONE);
+ openerNameLabel.setText(Labels.getLabel("text.openers.name"));
+ openerNameLabel.setSize(SWT.DEFAULT, 18);
+ openerNameText = new Text(editFieldsGroup, SWT.BORDER);
+ openerNameText.setSize(SWT.DEFAULT, 22);
+ openerNameText.addListener(SWT.KeyUp, new OpenerNameChange());
+
+ isInTerminalCheckbox = new Button(editFieldsGroup, SWT.CHECK);
+ isInTerminalCheckbox.setText(Labels.getLabel("text.openers.inTerminal"));
+ isInTerminalCheckbox.setSize(SWT.DEFAULT, 18);
+
+ Label openerStringLabel = new Label(editFieldsGroup, SWT.NONE);
+ openerStringLabel.setText(Labels.getLabel("text.openers.string"));
+ openerStringLabel.setSize(SWT.DEFAULT, 18);
+ openerStringText = new Text(editFieldsGroup, SWT.BORDER);
+ openerStringText.setSize(SWT.DEFAULT, 22);
+
+ Button hintButton = new Button(editFieldsGroup, SWT.NONE);
+ hintButton.setText(Labels.getLabel("text.openers.hint"));
+ hintButton.addListener(SWT.Selection, new HintButtonListener());
+
+ Label openerDirLabel = new Label(editFieldsGroup, SWT.NONE);
+ openerDirLabel.setText(Labels.getLabel("text.openers.directory"));
+ openerDirLabel.setSize(SWT.DEFAULT, 18);
+ workingDirText = new Text(editFieldsGroup, SWT.BORDER);
+ workingDirText.setSize(SWT.DEFAULT, 22);
+
+ editFieldsGroup.layout();
+
+ openersList.select(0);
+ loadFieldsForSelection();
+
+ shell.addListener(SWT.Close, new Listener() {
+ public void handleEvent(Event e) {
+ saveOpeners();
+ }
+ });
+ closeButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ shell.close();
+ }
+ });
+ shell.setDefaultButton(closeButton);
+ }
+
+ private void saveOpeners() {
+ // save any possible changes in text boxes
+ saveCurrentFields();
+
+ // now save everything else (order, etc)
+ OpenersConfig openersConfig = Config.getOpenersConfig();
+ openersConfig.update(openersList.getItems());
+ openersConfig.store();
+ }
+
+ private void saveCurrentFields() {
+ String openerName = openerNameText.getText();
+ if (openerName.length() == 0)
+ return;
+
+ File workingDir = workingDirText.getText().length() > 0 ? new File(workingDirText.getText()) : null;
+ Config.getOpenersConfig().add(openerName, new OpenersConfig.Opener(openerStringText.getText(), isInTerminalCheckbox.getSelection(), workingDir));
+ openersList.setItem(currentSelectionIndex, openerName);
+ }
+
+ private void loadFieldsForSelection() {
+ currentSelectionIndex = openersList.getSelectionIndex();
+ String openerName = openersList.getItem(currentSelectionIndex);
+ editFieldsGroup.setText(openerName);
+ Opener opener = Config.getOpenersConfig().getOpener(openerName);
+ openerNameText.setText(openerName);
+ openerStringText.setText(opener.execString);
+ workingDirText.setText(opener.workingDir != null ? opener.workingDir.toString() : "");
+ isInTerminalCheckbox.setSelection(opener.inTerminal);
+ }
+
+ private class HintButtonListener implements Listener {
+
+ public void handleEvent(Event event) {
+ // compose the message with all available fetchers
+ StringBuffer message = new StringBuffer(Labels.getLabel("text.openers.hintText"));
+ for (Iterator i = fetcherRegistry.getSelectedFetchers().iterator(); i.hasNext(); ) {
+ String fetcherLabel = ((Fetcher)i.next()).getLabel();
+ message.append("${").append(fetcherLabel).append("} - ").append(Labels.getLabel(fetcherLabel)).append('\n');
+ }
+
+ MessageBox mb = new MessageBox(shell, SWT.ICON_INFORMATION | SWT.OK);
+ mb.setText(Labels.getLabel("title.openers.edit"));
+ mb.setMessage(message.toString());
+ mb.open();
+ }
+ }
+
+ private class DeleteButtonListener implements Listener {
+
+ public void handleEvent(Event event) {
+ int firstIndex = openersList.getSelectionIndex();
+ openersList.remove(openersList.getSelectionIndices());
+ openersList.setSelection(firstIndex);
+ loadFieldsForSelection();
+ }
+ }
+
+ private class AddButtonListener implements Listener {
+
+ public void handleEvent(Event event) {
+ saveCurrentFields();
+
+ currentSelectionIndex = openersList.getSelectionIndex();
+ if (currentSelectionIndex < 0) {
+ currentSelectionIndex = openersList.getItemCount();
+ }
+ String newName = Labels.getLabel("text.openers.new");
+ openersList.add(newName, currentSelectionIndex);
+ openersList.setSelection(currentSelectionIndex);
+
+ // reset fields
+ editFieldsGroup.setText(newName);
+ openerNameText.setText(newName);
+ openerStringText.setText("${fetcher.ip}");
+ workingDirText.setText("");
+ isInTerminalCheckbox.setSelection(false);
+
+ openerNameText.forceFocus();
+ openerNameText.setSelection(0, newName.length());
+ }
+ }
+
+ private class ItemSelectListener implements Listener {
+
+ public void handleEvent(Event event) {
+ saveCurrentFields();
+ loadFieldsForSelection();
+ }
+ }
+
+ private class OpenerNameChange implements Listener {
+
+ public void handleEvent(Event event) {
+ String name = openerNameText.getText();
+ editFieldsGroup.setText(name);
+ openersList.setItem(currentSelectionIndex, name);
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/GettingStartedWindow.java b/ipscan/src/net/azib/ipscan/gui/GettingStartedWindow.java
new file mode 100755
index 00000000..77f428b6
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/GettingStartedWindow.java
@@ -0,0 +1,100 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Labels;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * About Window
+ *
+ * @author anton
+ */
+public class GettingStartedWindow extends AbstractModalDialog {
+
+ private int activePage = 1;
+ private Text gettingStartedText;
+ private Button closeButton;
+ private Button nextButton;
+
+ public GettingStartedWindow() {
+ createShell();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell() {
+ Display currentDisplay = Display.getCurrent();
+ Shell parent = currentDisplay != null ? currentDisplay.getActiveShell() : null;
+ shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
+
+ shell.setText(Labels.getLabel("title.gettingStarted"));
+ shell.setSize(new Point(400, 240));
+
+ Label iconLabel = new Label(shell, SWT.ICON);
+ iconLabel.setBounds(10, 10, 0, 0);
+
+ if (parent != null) {
+ iconLabel.setImage(parent.getImage());
+ shell.setImage(parent.getImage());
+ }
+ iconLabel.pack();
+
+ gettingStartedText = new Text(shell, SWT.BORDER | SWT.MULTI | SWT.READ_ONLY | SWT.V_SCROLL | SWT.WRAP);
+ gettingStartedText.setBounds(60, 10, 320, 160);
+ gettingStartedText.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
+
+ closeButton = new Button(shell, SWT.NONE);
+ closeButton.setText(Labels.getLabel("button.close"));
+ closeButton.setBounds(110, 180, 80, 25);
+ closeButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ shell.close();
+ shell.dispose();
+ }
+ });
+
+ nextButton = new Button(shell, SWT.NONE);
+ nextButton.setText(Labels.getLabel("button.next"));
+ nextButton.setBounds(210, 180, 80, 25);
+ nextButton.setFocus();
+ nextButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ activePage++;
+ displayActivePage();
+ }
+ });
+
+ shell.setDefaultButton(nextButton);
+
+ displayActivePage();
+ }
+
+ private void displayActivePage() {
+ String text = Labels.getLabel("text.gettingStarted" + activePage);
+ gettingStartedText.setText(text);
+
+ // check for the next one
+ try {
+ Labels.getLabel("text.gettingStarted" + (activePage+1));
+ }
+ catch (Exception e) {
+ // no label, disable the next button
+ nextButton.setEnabled(false);
+ shell.setDefaultButton(closeButton);
+ closeButton.setFocus();
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/InputDialog.java b/ipscan/src/net/azib/ipscan/gui/InputDialog.java
new file mode 100755
index 00000000..5c36de0a
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/InputDialog.java
@@ -0,0 +1,91 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Labels;
+
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Button;
+
+/**
+ * Customizable InputDialog
+ *
+ * @author anton
+ */
+public class InputDialog extends AbstractModalDialog {
+
+ private Label messageLabel = null;
+ private Text text = null;
+ private Button okButton = null;
+ private Button cancelButton = null;
+
+ private String message;
+
+ public InputDialog(String title, String message) {
+ createShell();
+ shell.setText(title);
+ messageLabel.setText(message);
+ messageLabel.pack();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell() {
+ Display currentDisplay = Display.getCurrent();
+ Shell parent = currentDisplay != null ? currentDisplay.getActiveShell() : null;
+
+ shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
+ shell.setSize(new Point(300, 112));
+ shell.setLayout(null);
+ messageLabel = new Label(shell, SWT.NONE);
+ messageLabel.setBounds(new Rectangle(3, 5, 282, 14));
+ text = new Text(shell, SWT.BORDER);
+ text.setBounds(new Rectangle(5, 24, 281, 24));
+ okButton = new Button(shell, SWT.NONE);
+ okButton.setLocation(new Point(57, 55));
+ okButton.setSize(new Point(70, 25));
+ okButton.setText(Labels.getLabel("button.OK"));
+ okButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
+ public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
+ message = text.getText();
+ shell.dispose();
+ }
+ });
+ shell.setDefaultButton(okButton);
+ cancelButton = new Button(shell, SWT.NONE);
+ cancelButton.setLocation(new Point(155, 55));
+ cancelButton.setSize(new Point(70, 25));
+ cancelButton.setText(Labels.getLabel("button.cancel"));
+ cancelButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
+ public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
+ message = null;
+ shell.dispose();
+ }
+ });
+ }
+
+ private void setText(String text) {
+ this.text.setText(text);
+ this.text.setSelection(0, -1);
+ }
+
+ /**
+ * Opens the dialog and waits for user to input the data.
+ *
+ * @return the entered text or null in case of cancel.
+ */
+ public String open(String text) {
+ setText(text);
+ super.open();
+ return message;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/MainMenu.java b/ipscan/src/net/azib/ipscan/gui/MainMenu.java
new file mode 100755
index 00000000..06097fba
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/MainMenu.java
@@ -0,0 +1,227 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.gui.actions.ColumnsActions;
+import net.azib.ipscan.gui.actions.CommandsActions;
+import net.azib.ipscan.gui.actions.FavoritesActions;
+import net.azib.ipscan.gui.actions.FileActions;
+import net.azib.ipscan.gui.actions.GotoActions;
+import net.azib.ipscan.gui.actions.HelpActions;
+import net.azib.ipscan.gui.actions.ToolsActions;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Decorations;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Shell;
+import org.picocontainer.MutablePicoContainer;
+import org.picocontainer.PicoContainer;
+import org.picocontainer.defaults.ConstructorInjectionComponentAdapter;
+import org.picocontainer.defaults.DefaultPicoContainer;
+
+/**
+ * MainMenu
+ *
+ * @author anton
+ */
+public class MainMenu {
+
+ private MutablePicoContainer container;
+
+ public MainMenu(Shell shell, Menu mainMenu, CommandsMenu resultsContextMenu, PicoContainer parentContainer) {
+
+ // create the menu-specific child container
+ container = new DefaultPicoContainer(parentContainer);
+
+ // register some components not registered in the main menu
+ container.registerComponentImplementation(FavoritesMenu.class);
+ container.registerComponentImplementation(FavoritesActions.ShowMenu.class);
+ container.registerComponentImplementation(FavoritesActions.Select.class);
+ container.registerComponentImplementation(FavoritesActions.Add.class);
+ container.registerComponentImplementation(FavoritesActions.Edit.class);
+
+ container.registerComponentImplementation(CommandsActions.EditOpeners.class);
+ container.registerComponentImplementation(CommandsActions.SelectOpener.class);
+ container.registerComponentImplementation(CommandsActions.ShowOpenersMenu.class);
+ // this one is not cached because we need 2 instances of it - in the Commands menu and in the context menu
+ container.registerComponent(new ConstructorInjectionComponentAdapter(OpenersMenu.class, OpenersMenu.class));
+
+ shell.setMenuBar(mainMenu);
+ createMainMenuItems(mainMenu);
+
+ createCommandsMenuItems(resultsContextMenu);
+ }
+
+ private void createMainMenuItems(Menu menu) {
+
+ Menu subMenu = initMenu(menu, "menu.file");
+ initMenuItem(subMenu, "menu.file.saveAll", new Integer(SWT.CONTROL | 'S'), initListener(FileActions.SaveAll.class));
+ initMenuItem(subMenu, "menu.file.saveSelection", null, initListener(FileActions.SaveSelection.class));
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.file.exportOptions", null, null);
+ initMenuItem(subMenu, "menu.file.importOptions", null, null);
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.file.exit", null, initListener(FileActions.Exit.class));
+
+ subMenu = initMenu(menu, "menu.goto");
+ initMenuItem(subMenu, "menu.goto.aliveHost", new Integer(SWT.CONTROL | SWT.SHIFT | 'H'), initListener(GotoActions.NextAliveHost.class));
+ initMenuItem(subMenu, "menu.goto.deadHost", new Integer(SWT.CONTROL | SWT.SHIFT | 'D'), initListener(GotoActions.NextDeadHost.class));
+ initMenuItem(subMenu, "menu.goto.openPort", new Integer(SWT.CONTROL | SWT.SHIFT | 'P'), initListener(GotoActions.NextHostWithInfo.class));
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.goto.find", new Integer(SWT.CONTROL | 'F'), initListener(GotoActions.Find.class));
+
+ subMenu = initMenu(menu, "menu.commands");
+ createCommandsMenuItems(subMenu);
+
+ createFavoritesMenu(menu);
+
+ subMenu = initMenu(menu, "menu.tools");
+ initMenuItem(subMenu, "menu.tools.options", new Integer(SWT.CONTROL | 'O'), initListener(ToolsActions.Options.class));
+ initMenuItem(subMenu, "menu.tools.fetchers", null, initListener(ToolsActions.SelectFetchers.class));
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.tools.delete", null, null);
+ initMenuItem(subMenu, "menu.tools.lastInfo", new Integer(SWT.CONTROL | 'I'), null);
+
+ subMenu = initMenu(menu, "menu.help");
+ initMenuItem(subMenu, "menu.help.gettingStarted", new Integer(SWT.F1), initListener(HelpActions.GettingStarted.class));
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.help.website", null, initListener(HelpActions.Website.class));
+ initMenuItem(subMenu, "menu.help.forum", null, initListener(HelpActions.Forum.class));
+ initMenuItem(subMenu, "menu.help.plugins", null, initListener(HelpActions.Plugins.class));
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.help.cmdLine", null, null);
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.help.checkVersion", null, initListener(HelpActions.CheckVersion.class));
+ initMenuItem(subMenu, null, null, null);
+ initMenuItem(subMenu, "menu.help.about", new Integer(SWT.F12), initListener(HelpActions.About.class));
+ }
+
+ private void createCommandsMenuItems(Menu menu) {
+ initMenuItem(menu, "menu.commands.details", null, initListener(CommandsActions.Details.class));
+ initMenuItem(menu, null, null, null);
+ initMenuItem(menu, "menu.commands.rescan", new Integer(SWT.CONTROL | 'R'), null);
+ initMenuItem(menu, "menu.commands.delete", new Integer(SWT.DEL), initListener(CommandsActions.Delete.class));
+ initMenuItem(menu, null, null, null);
+ initMenuItem(menu, "menu.commands.copy", new Integer(SWT.CONTROL | 'C'), initListener(CommandsActions.CopyIP.class));
+ initMenuItem(menu, "menu.commands.copyDetails", null, initListener(CommandsActions.CopyIPDetails.class));
+ initMenuItem(menu, null, null, null);
+ createOpenersMenu(menu);
+ // initMenuItem(subMenu, "menu.commands.show", null, initListener());
+ }
+
+ private void createOpenersMenu(Menu subMenu) {
+ OpenersMenu openersMenu = (OpenersMenu) container.getComponentInstance(OpenersMenu.class);
+ MenuItem openersMenuItem = new MenuItem(subMenu, SWT.CASCADE);
+ openersMenuItem.setText(Labels.getLabel("menu.commands.open"));
+ openersMenuItem.setMenu(openersMenu);
+ }
+
+ private void createFavoritesMenu(Menu parentMenu) {
+ MenuItem favoritesMenuItem = new MenuItem(parentMenu, SWT.CASCADE);
+ favoritesMenuItem.setText(Labels.getLabel("menu.favorites"));
+ Menu favoritesMenu = (Menu) container.getComponentInstance(FavoritesMenu.class);
+ favoritesMenuItem.setMenu(favoritesMenu);
+ }
+
+ private static Menu initMenu(Menu menu, String label) {
+ MenuItem menuItem = new MenuItem(menu, SWT.CASCADE);
+ menuItem.setText(Labels.getLabel(label));
+
+ Menu subMenu = new Menu(menu.getShell(), SWT.DROP_DOWN);
+ menuItem.setMenu(subMenu);
+
+ return subMenu;
+ }
+
+ private Listener initListener(Class listenerClass) {
+ // register the component if it is not registered yet
+ if (container.getComponentAdapter(listenerClass) == null)
+ container.registerComponentImplementation(listenerClass);
+ // .. and create the instance, satisfying all the dependencies
+ return (Listener) container.getComponentInstance(listenerClass);
+ }
+
+ private static MenuItem initMenuItem(Menu parent, String label, Integer accelerator, Listener listener) {
+ MenuItem menuItem = new MenuItem(parent, label == null ? SWT.SEPARATOR : SWT.PUSH);
+
+ if (label != null)
+ menuItem.setText(Labels.getLabel(label));
+
+ if (accelerator != null)
+ menuItem.setAccelerator(accelerator.intValue());
+
+ if (listener != null)
+ menuItem.addListener(SWT.Selection, listener);
+ else
+ menuItem.setEnabled(false);
+
+ return menuItem;
+ }
+
+ /**
+ * CommandsMenu wrapper for type-safety
+ */
+ public static class CommandsMenu extends Menu {
+ public CommandsMenu(Decorations parent) {
+ super(parent, SWT.POP_UP);
+ }
+ protected void checkSubclass() { } // allow extending of Menu class
+ }
+
+ /**
+ * OpenersMenu wrapper for type-safety
+ */
+ public static class OpenersMenu extends Menu {
+ public OpenersMenu(Decorations parent, CommandsActions.EditOpeners editOpenersListener, CommandsActions.ShowOpenersMenu showOpenersMenuListener) {
+ super(parent, SWT.DROP_DOWN);
+
+ initMenuItem(this, "menu.commands.open.edit", null, editOpenersListener);
+ initMenuItem(this, null, null, null);
+
+ addListener(SWT.Show, showOpenersMenuListener);
+
+ // run the listener to populate the menu initially and initialize accelerators
+ Event e = new Event();
+ e.widget = this;
+ showOpenersMenuListener.handleEvent(e);
+ }
+ protected void checkSubclass() { } // allow extending of Menu class
+ }
+
+ /**
+ * FavoritesMenu wrapper for type-safety
+ */
+ public static class FavoritesMenu extends Menu {
+ public FavoritesMenu(Decorations parent, FavoritesActions.Add addListener, FavoritesActions.Edit editListener, FavoritesActions.ShowMenu showFavoritesMenuListener) {
+ super(parent, SWT.DROP_DOWN);
+
+ initMenuItem(this, "menu.favorites.add", new Integer(SWT.CONTROL | 'D'), addListener);
+ initMenuItem(this, "menu.favorites.edit", null, editListener);
+ initMenuItem(this, null, null, null);
+
+ addListener(SWT.Show, showFavoritesMenuListener);
+ }
+ protected void checkSubclass() { } // allow extending of Menu class
+ }
+
+ /**
+ * ColumnsMenu wrapper for type-safety.
+ * This is the menu when clicking on a column header.
+ */
+ public static class ColumnsMenu extends Menu {
+ public ColumnsMenu(Decorations parent, ColumnsActions.SortBy sortByListener) {
+ super(parent, SWT.POP_UP);
+
+ initMenuItem(this, "menu.columns.sortBy", null, sortByListener);
+ initMenuItem(this, "menu.columns.info", null, null);
+ initMenuItem(this, "menu.columns.options", null, null);
+ }
+ protected void checkSubclass() { } // allow extending of Menu class
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/MainWindow.java b/ipscan/src/net/azib/ipscan/gui/MainWindow.java
new file mode 100755
index 00000000..b3e3fc81
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/MainWindow.java
@@ -0,0 +1,189 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import java.util.Iterator;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.Version;
+import net.azib.ipscan.gui.MainMenu.CommandsMenu;
+import net.azib.ipscan.gui.actions.StartStopScanningAction;
+import net.azib.ipscan.gui.feeders.AbstractFeederGUI;
+import net.azib.ipscan.gui.feeders.FeederGUIRegistry;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * Main window of Angry IP Scanner.
+ * Contains the menu, IP resultTable, status bar (with progress bar) and
+ * a Composite area, which can be substituted dynamically based on
+ * the selected feeder.
+ *
+ * @author anton
+ */
+public class MainWindow {
+
+ private Shell shell;
+
+ private Composite feederArea;
+
+ private Combo feederSelectionCombo;
+ private FeederGUIRegistry feederRegistry;
+
+ /**
+ * Creates and initializes the main window.
+ */
+ public MainWindow(Shell shell, Composite feederArea, Composite controlsArea, Combo feederSelectionCombo, Button startStopButton, StartStopScanningAction startStopScanningAction, ResultTable resultTable, StatusBar statusBar, CommandsMenu resultsContextMenu, FeederGUIRegistry feederGUIRegistry) {
+
+ initShell(shell);
+
+ initFeederArea(feederArea, feederGUIRegistry);
+
+ initControlsArea(controlsArea, feederSelectionCombo, startStopButton, startStopScanningAction);
+
+ initTableAndStatusBar(resultTable, resultsContextMenu, statusBar);
+
+ // after all controls are initialized, resize and open
+ shell.setBounds(Config.getDimensionsConfig().getWindowBounds());
+ shell.setMaximized(Config.getDimensionsConfig().isWindowMaximized);
+ shell.open();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void initShell(final Shell shell) {
+ this.shell = shell;
+
+ FormLayout formLayout = new FormLayout();
+ shell.setLayout(formLayout);
+ shell.setText(Version.FULL_NAME);
+
+ // load and set icon
+ Image image = new Image(shell.getDisplay(), Labels.getInstance().getImageAsStream("icon"));
+ shell.setImage(image);
+
+ shell.addListener(SWT.Close, new Listener() {
+ public void handleEvent(Event event) {
+ // save dimensions!
+ Config.getDimensionsConfig().setWindowBounds(shell.getBounds(), shell.getMaximized());
+ }
+ });
+ }
+
+ /**
+ * @return the underlying shell, used by the Main class
+ */
+ public Shell getShell() {
+ return shell;
+ }
+
+ /**
+ * @return true if the underlying shell is disposed
+ */
+ public boolean isDisposed() {
+ return shell.isDisposed();
+ }
+
+ /**
+ * This method initializes resultTable
+ */
+ private void initTableAndStatusBar(ResultTable resultTable, CommandsMenu resultsContextMenu, StatusBar statusBar) {
+ FormData formData = new FormData();
+ formData.top = new FormAttachment(feederArea);
+ formData.left = new FormAttachment(0);
+ formData.right = new FormAttachment(100);
+ formData.bottom = new FormAttachment(statusBar.getComposite(), -3);
+ resultTable.setLayoutData(formData);
+ resultTable.setMenu(resultsContextMenu);
+ }
+
+ private void initFeederArea(Composite feederArea, FeederGUIRegistry feederRegistry) {
+ // feederArea is the placeholder for the visible feeder
+ this.feederArea = feederArea;
+ FormData formData = new FormData();
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(0);
+ feederArea.setLayoutData(formData);
+
+ this.feederRegistry = feederRegistry;
+ }
+
+ /**
+ * This method initializes main controls of the main window
+ */
+ private void initControlsArea(Composite controlsArea, Combo feederSelectionCombo, Button startStopButton, StartStopScanningAction startStopScanningAction) {
+
+ FormData formData = new FormData();
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(feederArea);
+ formData.right = new FormAttachment(100);
+ formData.bottom = new FormAttachment(feederArea, 0, SWT.BOTTOM);
+ controlsArea.setLayoutData(formData);
+
+ RowLayout rowLayout = new RowLayout(SWT.VERTICAL);
+ rowLayout.marginLeft = 7;
+ controlsArea.setLayout(rowLayout);
+
+ // start/stop button
+ shell.setDefaultButton(startStopButton);
+ startStopButton.setLayoutData(new RowData(SWT.DEFAULT, 23));
+ startStopButton.addSelectionListener(startStopScanningAction);
+
+ // feeder selection combobox
+ this.feederSelectionCombo = feederSelectionCombo;
+ feederSelectionCombo.setLayoutData(new RowData(SWT.DEFAULT, 23));
+ for (Iterator i = feederRegistry.iterator(); i.hasNext();) {
+ AbstractFeederGUI feederGUI = (AbstractFeederGUI) i.next();
+ feederSelectionCombo.add(feederGUI.getFeederName());
+ }
+ IPFeederSelectionListener feederSelectionListener = new IPFeederSelectionListener();
+ feederSelectionCombo.addSelectionListener(feederSelectionListener);
+ // initialize the selected feeder GUI
+ feederSelectionCombo.select(Config.getGlobal().activeFeeder);
+ feederSelectionCombo.setToolTipText(Labels.getLabel("combobox.feeder.tooltip"));
+ feederSelectionListener.widgetSelected(null);
+
+ ((RowData)startStopButton.getLayoutData()).height = feederSelectionCombo.getBounds().height;
+ ((RowData)startStopButton.getLayoutData()).width = feederSelectionCombo.getBounds().width;
+ }
+
+ /**
+ * IP Feeder selection listener. Updates the GUI according to the IP Feeder selection.
+ */
+ private final class IPFeederSelectionListener implements SelectionListener {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ feederRegistry.select(feederSelectionCombo.getSelectionIndex());
+
+ // all this 'magic' is needed in order to resize everything properly
+ // and accomodate feeders with different sizes
+ Rectangle bounds = feederRegistry.current().getBounds();
+ FormData feederAreaLayoutData = ((FormData)feederArea.getLayoutData());
+ feederAreaLayoutData.height = bounds.height;
+ feederAreaLayoutData.width = bounds.width;
+ shell.layout();
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/OptionsWindow.java b/ipscan/src/net/azib/ipscan/gui/OptionsWindow.java
new file mode 100755
index 00000000..cead97b5
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/OptionsWindow.java
@@ -0,0 +1,312 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.GlobalConfig;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.net.PingerRegistry;
+
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.TabFolder;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.layout.RowLayout;
+
+/**
+ * OptionsWindow
+ *
+ * @author anton
+ */
+public class OptionsWindow extends AbstractModalDialog {
+
+ private PingerRegistry pingerRegistry;
+
+ private TabFolder tabFolder;
+ private Composite scanningTab;
+ private Composite displayTab;
+ private Text threadDelayText;
+ private Text maxThreadsText;
+ private Button okButton;
+ private Button cancelButton;
+ private Button deadHostsCheckbox;
+ private Text pingingTimeoutText;
+ private Text pingingCountText;
+ private Combo pingersCombo;
+ private Button skipBroadcastsCheckbox;
+ private Composite fetchersTab;
+ private Composite portsTab;
+ private Text portTimeoutText;
+ private Button adaptTimeoutCheckbox;
+ private Text portsText;
+
+ public OptionsWindow(PingerRegistry pingerRegistry) {
+ this.pingerRegistry = pingerRegistry;
+ }
+
+ public void open() {
+ // widgets are created on demand
+ createShell();
+ loadOptions();
+ super.open();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell() {
+ Display currentDisplay = Display.getCurrent();
+ shell = new Shell(currentDisplay != null ? currentDisplay.getActiveShell() : null, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
+ shell.setText(Labels.getLabel("title.options"));
+ createTabFolder();
+ shell.setSize(new Point(350, 423));
+ shell.setLayout(null);
+ okButton = new Button(shell, SWT.NONE);
+ okButton.setBounds(new Rectangle(175, 365, 75, 25));
+ okButton.setText("OK");
+ shell.setDefaultButton(okButton);
+ cancelButton = new Button(shell, SWT.NONE);
+ cancelButton.setBounds(new Rectangle(260, 365, 75, 25));
+ cancelButton.setText("Cancel");
+
+ okButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
+ public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
+ saveOptions();
+ shell.close();
+ }
+ });
+ cancelButton.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
+ public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
+ shell.close();
+ }
+ });
+ }
+
+ /**
+ * This method initializes tabFolder
+ */
+ private void createTabFolder() {
+ tabFolder = new TabFolder(shell, SWT.NONE);
+ createScanningTab();
+ createDisplayTab();
+ createFetchersTab();
+ createPortsTab();
+ tabFolder.setBounds(new Rectangle(5, 5, 330, 355));
+ TabItem tabItem = new TabItem(tabFolder, SWT.NONE);
+ tabItem.setText(Labels.getLabel("title.options.scanning"));
+ tabItem.setControl(scanningTab);
+ TabItem tabItem1 = new TabItem(tabFolder, SWT.NONE);
+ tabItem1.setText(Labels.getLabel("title.options.ports"));
+ tabItem1.setControl(portsTab);
+ TabItem tabItem2 = new TabItem(tabFolder, SWT.NONE);
+ tabItem2.setText(Labels.getLabel("title.options.display"));
+ tabItem2.setControl(displayTab);
+ TabItem tabItem3 = new TabItem(tabFolder, SWT.NONE);
+ tabItem3.setText(Labels.getLabel("title.options.fetchers"));
+ tabItem3.setControl(fetchersTab);
+ }
+
+ /**
+ * This method initializes scanningTab
+ */
+ private void createScanningTab() {
+ RowLayout rowLayout = new RowLayout();
+ rowLayout.type = org.eclipse.swt.SWT.VERTICAL;
+ rowLayout.marginTop = 9;
+ rowLayout.spacing = 9;
+ rowLayout.marginLeft = 11;
+ rowLayout.fill = true;
+ scanningTab = new Composite(tabFolder, SWT.NONE);
+ scanningTab.setLayout(rowLayout);
+
+ GridLayout groupLayout = new GridLayout();
+ groupLayout.numColumns = 2;
+ Group threadsGroup = new Group(scanningTab, SWT.NONE);
+ threadsGroup.setText(Labels.getLabel("options.threads"));
+ threadsGroup.setLayout(groupLayout);
+
+ GridData gridData = new GridData();
+ gridData.widthHint = 80;
+
+ Label label;
+
+ label = new Label(threadsGroup, SWT.NONE);
+ label.setText(Labels.getLabel("options.threads.delay"));
+ threadDelayText = new Text(threadsGroup, SWT.BORDER);
+ threadDelayText.setLayoutData(gridData);
+
+ label = new Label(threadsGroup, SWT.NONE);
+ label.setText(Labels.getLabel("options.threads.maxThreads"));
+ maxThreadsText = new Text(threadsGroup, SWT.BORDER);
+ maxThreadsText.setLayoutData(gridData);
+
+ Group pingingGroup = new Group(scanningTab, SWT.NONE);
+ pingingGroup.setLayout(groupLayout);
+ pingingGroup.setText(Labels.getLabel("options.pinging"));
+
+ label = new Label(pingingGroup, SWT.NONE);
+ label.setText(Labels.getLabel("options.pinging.type"));
+ pingersCombo = new Combo(pingingGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
+ pingersCombo.setLayoutData(gridData);
+ String[] pingerNames = pingerRegistry.getRegisteredNames();
+ for (int i = 0; i < pingerNames.length; i++) {
+ pingersCombo.add(Labels.getLabel(pingerNames[i]));
+ // this is used by saveOptions()
+ pingersCombo.setData(Integer.toString(i), pingerNames[i]);
+ }
+ pingersCombo.select(0);
+
+ label = new Label(pingingGroup, SWT.NONE);
+ label.setText(Labels.getLabel("options.pinging.count"));
+ pingingCountText = new Text(pingingGroup, SWT.BORDER);
+ pingingCountText.setLayoutData(gridData);
+
+ label = new Label(pingingGroup, SWT.NONE);
+ label.setText(Labels.getLabel("options.pinging.timeout"));
+ pingingTimeoutText = new Text(pingingGroup, SWT.BORDER);
+ pingingTimeoutText.setLayoutData(gridData);
+
+ GridData gridData1 = new GridData();
+ gridData1.horizontalSpan = 2;
+ deadHostsCheckbox = new Button(pingingGroup, SWT.CHECK);
+ deadHostsCheckbox.setText(Labels.getLabel("options.pinging.deadHosts"));
+ deadHostsCheckbox.setLayoutData(gridData1);
+
+ Group broadcastGroup = new Group(scanningTab, SWT.NONE);
+ broadcastGroup.setLayout(groupLayout);
+ broadcastGroup.setText(Labels.getLabel("options.broadcast"));
+
+ skipBroadcastsCheckbox = new Button(broadcastGroup, SWT.CHECK);
+ skipBroadcastsCheckbox.setText(Labels.getLabel("options.broadcast.skip"));
+ skipBroadcastsCheckbox.setLayoutData(gridData1);
+ }
+
+ /**
+ * This method initializes displayTab
+ */
+ private void createDisplayTab() {
+ displayTab = new Composite(tabFolder, SWT.NONE);
+ displayTab.setLayout(new GridLayout());
+ }
+
+ /**
+ * This method initializes portsTab
+ */
+ private void createPortsTab() {
+ RowLayout rowLayout = new RowLayout();
+ rowLayout.type = org.eclipse.swt.SWT.VERTICAL;
+ rowLayout.marginTop = 9;
+ rowLayout.spacing = 9;
+ rowLayout.marginLeft = 11;
+ rowLayout.fill = true;
+ portsTab = new Composite(tabFolder, SWT.NONE);
+ portsTab.setLayout(rowLayout);
+
+ GridLayout groupLayout = new GridLayout();
+ groupLayout.numColumns = 2;
+ Group timingGroup = new Group(portsTab, SWT.NONE);
+ timingGroup.setText(Labels.getLabel("options.ports.timing"));
+ timingGroup.setLayout(groupLayout);
+
+ GridData gridData = new GridData();
+ gridData.widthHint = 50;
+
+ Label label;
+
+ label = new Label(timingGroup, SWT.NONE);
+ label.setText(Labels.getLabel("options.ports.timing.timeout"));
+ portTimeoutText = new Text(timingGroup, SWT.BORDER);
+ portTimeoutText.setLayoutData(gridData);
+
+ GridData gridData1 = new GridData();
+ gridData1.horizontalSpan = 2;
+ adaptTimeoutCheckbox = new Button(timingGroup, SWT.CHECK);
+ adaptTimeoutCheckbox.setText(Labels.getLabel("options.ports.timing.adaptTimeout"));
+ adaptTimeoutCheckbox.setLayoutData(gridData1);
+
+ RowLayout portsLayout = new RowLayout(SWT.VERTICAL);
+ portsLayout.fill = true;
+ Group portsGroup = new Group(portsTab, SWT.NONE);
+ portsGroup.setText(Labels.getLabel("options.ports.ports"));
+ portsGroup.setLayout(portsLayout);
+
+ label = new Label(portsGroup, SWT.WRAP);
+ label.setText(Labels.getLabel("options.ports.portsDescription"));
+ label.setLayoutData(new RowData(280, SWT.DEFAULT));
+ portsText = new Text(portsGroup, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL);
+ portsText.setLayoutData(new RowData(260, 60));
+ // TODO: configuration string validation
+
+ }
+
+ /**
+ * This method initializes fetchersTab
+ */
+ private void createFetchersTab() {
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.numColumns = 1;
+ fetchersTab = new Composite(tabFolder, SWT.NONE);
+ fetchersTab.setLayout(gridLayout);
+ Label label = new Label(fetchersTab, SWT.NONE);
+ label.setText(Labels.getLabel("options.fetchers.info"));
+ }
+
+ private void loadOptions() {
+ GlobalConfig global = Config.getGlobal();
+ maxThreadsText.setText(Integer.toString(global.maxThreads));
+ threadDelayText.setText(Integer.toString(global.threadDelay));
+ String[] pingerNames = pingerRegistry.getRegisteredNames();
+ for (int i = 0; i < pingerNames.length; i++) {
+ if (global.selectedPinger.equals(pingerNames[i])) {
+ pingersCombo.select(i);
+ }
+ }
+ pingingCountText.setText(Integer.toString(global.pingCount));
+ pingingTimeoutText.setText(Integer.toString(global.pingTimeout));
+ deadHostsCheckbox.setSelection(global.scanDeadHosts);
+ skipBroadcastsCheckbox.setSelection(global.skipBroadcastAddresses);
+ portTimeoutText.setText(Integer.toString(global.portTimeout));
+ adaptTimeoutCheckbox.setSelection(global.adaptPortTimeout);
+ portsText.setText(global.portString);
+ }
+
+ private void saveOptions() {
+ GlobalConfig global = Config.getGlobal();
+ global.maxThreads = parseIntValue(maxThreadsText);
+ global.threadDelay = parseIntValue(threadDelayText);
+ global.selectedPinger = (String) pingersCombo.getData(Integer.toString(pingersCombo.getSelectionIndex()));
+ global.pingCount = parseIntValue(pingingCountText);
+ global.pingTimeout = parseIntValue(pingingTimeoutText);
+ global.scanDeadHosts = deadHostsCheckbox.getSelection();
+ global.skipBroadcastAddresses = skipBroadcastsCheckbox.getSelection();
+ global.portTimeout = parseIntValue(portTimeoutText);
+ global.adaptPortTimeout = adaptTimeoutCheckbox.getSelection();
+ global.portString = portsText.getText();
+ }
+
+ /**
+ * @return an int from the passed Text control.
+ */
+ private static int parseIntValue(Text text) {
+ try {
+ return Integer.parseInt(text.getText());
+ }
+ catch (NumberFormatException e) {
+ text.forceFocus();
+ throw e;
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/ResultTable.java b/ipscan/src/net/azib/ipscan/gui/ResultTable.java
new file mode 100755
index 00000000..a30b62c4
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/ResultTable.java
@@ -0,0 +1,200 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import java.net.InetAddress;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.ScanningResult;
+import net.azib.ipscan.core.ScanningResultList;
+import net.azib.ipscan.core.ScanningSubject;
+import net.azib.ipscan.fetchers.Fetcher;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+import net.azib.ipscan.fetchers.FetcherRegistryUpdateListener;
+import net.azib.ipscan.gui.MainMenu.ColumnsMenu;
+import net.azib.ipscan.gui.actions.ColumnsActions;
+import net.azib.ipscan.gui.actions.CommandsActions;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+
+/**
+ * Table of scanning results.
+ * TODO: separate fetcher selection logic to some other calls (the registry?)
+ *
+ * @author anton
+ */
+public class ResultTable extends Table implements FetcherRegistryUpdateListener {
+
+ private ScanningResultList scanningResults;
+
+ private Image[] listImages = new Image[4];
+
+ private String feederInfo;
+
+ private Listener columnClickListener;
+
+ private Listener columnResizeListener;
+
+ public ResultTable(Composite parent, ColumnsMenu columnsMenu, FetcherRegistry fetcherRegistry, ScanningResultList scanningResultList) {
+ super(parent, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL);
+ this.scanningResults = scanningResultList;
+
+ setHeaderVisible(true);
+ setLinesVisible(true);
+
+ columnClickListener = new ColumnsActions.ColumnClick(columnsMenu);
+ columnResizeListener = new Listener() {
+ public void handleEvent(Event event) {
+ // save column width
+ TableColumn column = (TableColumn) event.widget;
+ Config.getDimensionsConfig().setColumnWidth(column.getText(), column.getWidth());
+ }
+ };
+
+ fetcherRegistry.addListener(this);
+ handleUpdateOfSelectedFetchers(fetcherRegistry);
+
+ // pre-load button images
+ listImages[ScanningSubject.RESULT_TYPE_UNKNOWN] = new Image(null, Labels.getInstance().getImageAsStream("list.unknown.img"));
+ listImages[ScanningSubject.RESULT_TYPE_DEAD] = new Image(null, Labels.getInstance().getImageAsStream("list.dead.img"));
+ listImages[ScanningSubject.RESULT_TYPE_ALIVE] = new Image(null, Labels.getInstance().getImageAsStream("list.alive.img"));
+ listImages[ScanningSubject.RESULT_TYPE_ADDITIONAL_INFO] = new Image(null, Labels.getInstance().getImageAsStream("list.addinfo.img"));
+
+ Listener detailsListener = new Listener() {
+ CommandsActions.Details detailsListener = new CommandsActions.Details(ResultTable.this);
+ public void handleEvent(Event e) {
+ if (e.type == SWT.MouseDoubleClick || e.detail == SWT.TRAVERSE_RETURN) {
+ detailsListener.handleEvent(e);
+ e.doit = false;
+ }
+ }
+ };
+ addListener(SWT.Traverse, detailsListener);
+ addListener(SWT.MouseDoubleClick, detailsListener);
+
+ addListener(SWT.SetData, new SetDataListener());
+
+// for (int i = 0; i < getColumnCount(); i++) {
+// getColumn(i).add
+// }
+
+ }
+
+ public void handleUpdateOfSelectedFetchers(FetcherRegistry fetcherRegistry) {
+ Collection fetchers = fetcherRegistry.getSelectedFetchers();
+ for (Iterator i = fetchers.iterator(); i.hasNext();) {
+ Fetcher fetcher = (Fetcher) i.next();
+ TableColumn tableColumn = new TableColumn(this, SWT.NONE);
+ String fetcherName = Labels.getLabel(fetcher.getLabel());
+ tableColumn.setWidth(Config.getDimensionsConfig().getColumnWidth(fetcherName));
+ tableColumn.setText(fetcherName);
+ tableColumn.addListener(SWT.Selection, columnClickListener);
+ tableColumn.addListener(SWT.Resize, columnResizeListener);
+ }
+ }
+
+ protected void checkSubclass() {
+ // This method is overriden and does nothing in order to
+ // be able to subclass the Table. We are not going to
+ // override anything important, so this should be safe (tm)
+ }
+
+ public int addResultsRow(final InetAddress address) {
+ if (isDisposed())
+ return 0;
+ final int index = scanningResults.add(address);
+ getDisplay().syncExec(new Runnable() {
+ public void run() {
+ ResultTable.this.setItemCount(index+1);
+ }
+ });
+ return index;
+ }
+
+ public void updateResults(final int index) {
+ if (isDisposed())
+ return;
+ getDisplay().syncExec(new Runnable() {
+ public void run() {
+ // redraw the item
+ ResultTable.this.clear(index);
+ }
+ });
+ }
+
+ /**
+ * Returns the details about the currently selected IP address
+ * @return
+ */
+ public String getIPDetails() {
+ int selectedIndex = getSelectionIndex();
+ return scanningResults.getResultsAsString(selectedIndex);
+ }
+
+ public void remove(int[] indices) {
+ // we need to remove the elements from our real storage as well
+ scanningResults.remove(indices);
+ super.remove(indices);
+ }
+
+ /**
+ * Initializes a new scan.
+ * (clears all elments, etc)
+ * @param newFeederInfo feeder info of the new feeder/settings
+ */
+ public void initNewScan(String newFeederInfo) {
+ // initialize new feeder info
+ this.feederInfo = newFeederInfo;
+ // remove previous results
+ scanningResults.clear();
+ // remove all items from the table
+ removeAll();
+ }
+
+ /**
+ * @return the internal ScanningResultList instance, containing the results.
+ */
+ public ScanningResultList getScanningResults() {
+ return scanningResults;
+ }
+
+ private class SetDataListener implements Listener {
+
+ public void handleEvent(Event event) {
+ TableItem item = (TableItem)event.item;
+ int tableIndex = ResultTable.this.indexOf(item);
+
+ ScanningResult scanningResult = scanningResults.getResult(tableIndex);
+ List values = scanningResult.getValues();
+ String[] resultStrings = new String[values.size()];
+ for (int i = 0; i < values.size(); i++) {
+ Object value = values.get(i);
+ if (value != null)
+ resultStrings[i] = value.toString();
+ }
+ item.setText(resultStrings);
+ item.setImage(0, listImages[scanningResult.getType()]);
+ }
+
+ }
+
+ /**
+ * @return the feeder info, which used in this scan
+ */
+ public String getFeederInfo() {
+ return feederInfo;
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/ScanningResultsConsumer.java b/ipscan/src/net/azib/ipscan/gui/ScanningResultsConsumer.java
new file mode 100755
index 00000000..adfb1d44
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/ScanningResultsConsumer.java
@@ -0,0 +1,37 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import java.net.InetAddress;
+
+import net.azib.ipscan.core.ScanningResult;
+import net.azib.ipscan.core.ScanningResultsCallback;
+
+/**
+ *
+ * @author anton
+ */
+public class ScanningResultsConsumer implements ScanningResultsCallback {
+
+ private ResultTable resultTable;
+
+ public ScanningResultsConsumer(ResultTable resultTable) {
+ this.resultTable = resultTable;
+ }
+
+ /**
+ * @see net.azib.ipscan.core.ScanningResultsCallback#prepareForResults(InetAddress)
+ */
+ public int prepareForResults(InetAddress address) {
+ return resultTable.addResultsRow(address);
+ }
+
+ /**
+ * @see net.azib.ipscan.core.ScanningResultsCallback#consumeResults(int, ScanningResult)
+ */
+ public void consumeResults(int preparationIndex, ScanningResult results) {
+ resultTable.updateResults(preparationIndex);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/SelectFetchersDialog.java b/ipscan/src/net/azib/ipscan/gui/SelectFetchersDialog.java
new file mode 100755
index 00000000..6345a2d8
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/SelectFetchersDialog.java
@@ -0,0 +1,163 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.fetchers.Fetcher;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * SelectFetchersWindow
+ *
+ * @author Anton Keks
+ */
+public class SelectFetchersDialog extends AbstractModalDialog {
+
+ private FetcherRegistry fetcherRegistry;
+
+ private List selectedFetchersList;
+ private List registeredFetchersList;
+ private Map registeredFetcherLabelsByNames = new HashMap();
+
+ public SelectFetchersDialog(FetcherRegistry fetcherRegistry) {
+ this.fetcherRegistry = fetcherRegistry;
+ }
+
+ public void open() {
+ // create controls on demand
+ createShell();
+ super.open();
+ }
+
+ /**
+ * This method initializes shell
+ */
+ private void createShell() {
+ Display currentDisplay = Display.getCurrent();
+ Shell parent = currentDisplay != null ? currentDisplay.getActiveShell() : null;
+ shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
+
+ shell.setText(Labels.getLabel("title.fetchers.select"));
+ shell.setSize(new Point(405, 332));
+ shell.setLayout(null);
+
+ Label messageLabel = new Label(shell, SWT.NONE);
+ messageLabel.setText(Labels.getLabel("text.fetchers.select"));
+ messageLabel.setBounds(new Rectangle(10, 10, 380, 14));
+
+ Label selectedLabel = new Label(shell, SWT.NONE);
+ selectedLabel.setText(Labels.getLabel("text.fetchers.selectedList"));
+ selectedLabel.setBounds(new Rectangle(10, 35, 155, 14));
+
+ selectedFetchersList = new List(shell, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
+ selectedFetchersList.setBounds(new Rectangle(10, 55, 155, 200));
+ Iterator i = fetcherRegistry.getSelectedFetchers().iterator();
+ //i.next(); // skip IP
+ while (i.hasNext()) {
+ Fetcher fetcher = (Fetcher) i.next();
+ String fetcherName = Labels.getLabel(fetcher.getLabel());
+ selectedFetchersList.add(fetcherName);
+ }
+
+ Button upButton = new Button(shell, SWT.NONE);
+ upButton.setText(Labels.getLabel("button.up"));
+ upButton.setBounds(new Rectangle(170, 55, 40, 25));
+
+ Button downButton = new Button(shell, SWT.NONE);
+ downButton.setText(Labels.getLabel("button.down"));
+ downButton.setBounds(new Rectangle(170, 85, 40, 25));
+
+ Button addButton = new Button(shell, SWT.NONE);
+ addButton.setText(Labels.getLabel("button.left"));
+ addButton.setBounds(new Rectangle(170, 130, 40, 25));
+
+ Button removeButton = new Button(shell, SWT.NONE);
+ removeButton.setText(Labels.getLabel("button.right"));
+ removeButton.setBounds(new Rectangle(170, 160, 40, 25));
+
+ Label registeredLabel = new Label(shell, SWT.NONE);
+ registeredLabel.setText(Labels.getLabel("text.fetchers.availableList"));
+ registeredLabel.setBounds(new Rectangle(230, 35, 155, 14));
+
+ registeredFetchersList = new List(shell, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);
+ registeredFetchersList.setBounds(new Rectangle(230, 55, 160, 200));
+ i = fetcherRegistry.getRegisteredFetchers().iterator();
+ //i.next(); // skip IP
+ while (i.hasNext()) {
+ Fetcher fetcher = (Fetcher) i.next();
+ String fetcherName = Labels.getLabel(fetcher.getLabel());
+ registeredFetcherLabelsByNames.put(fetcherName, fetcher.getLabel());
+ if (selectedFetchersList.indexOf(fetcherName) < 0)
+ registeredFetchersList.add(fetcherName);
+ }
+
+ upButton.addListener(SWT.Selection, new UpButtonListener(selectedFetchersList));
+ downButton.addListener(SWT.Selection, new DownButtonListener(selectedFetchersList));
+ addButton.addListener(SWT.Selection, new AddRemoveButtonListener(registeredFetchersList, selectedFetchersList));
+ removeButton.addListener(SWT.Selection, new AddRemoveButtonListener(selectedFetchersList, registeredFetchersList));
+
+ Button closeButton = new Button(shell, SWT.NONE);
+ closeButton.setText(Labels.getLabel("button.close"));
+ closeButton.setBounds(new Rectangle(315, 270, 75, 25));
+
+ shell.addListener(SWT.Close, new Listener() {
+ public void handleEvent(Event e) {
+ saveSelectedFetchers();
+ }
+ });
+ closeButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ shell.close();
+ }
+ });
+ shell.setDefaultButton(closeButton);
+ }
+
+ private void saveSelectedFetchers() {
+ String[] fetchersToRetain = new String[selectedFetchersList.getItemCount()];
+ for (int i = 0; i < fetchersToRetain.length; i++) {
+ fetchersToRetain[i] = (String) registeredFetcherLabelsByNames.get(selectedFetchersList.getItem(i));
+ }
+ fetcherRegistry.updateSelectedFetchers(fetchersToRetain);
+ }
+
+ private static class AddRemoveButtonListener implements Listener {
+
+ private List fromList;
+ private List toList;
+
+ public AddRemoveButtonListener(List fromList, List toList) {
+ this.fromList = fromList;
+ this.toList = toList;
+ }
+
+ public void handleEvent(Event event) {
+ int[] selectedItems = fromList.getSelectionIndices();
+
+ // first, add items back to the registered list
+ for (int i = 0; i < selectedItems.length; i++) {
+ toList.add(fromList.getItem(selectedItems[i]));
+ }
+
+ // now, add remove the items
+ fromList.remove(selectedItems);
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/StatusBar.java b/ipscan/src/net/azib/ipscan/gui/StatusBar.java
new file mode 100755
index 00000000..c464aac2
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/StatusBar.java
@@ -0,0 +1,95 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Labels;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.ProgressBar;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * The status bar of the main window.
+ *
+ * @author anton
+ */
+public class StatusBar {
+
+ private Composite composite;
+
+ private ProgressBar progressBar;
+ private Label statusText;
+ private Label threadsText;
+
+ public StatusBar(Shell shell) {
+ composite = new Composite(shell, SWT.NONE);
+ FormData formData = new FormData();
+ formData.left = new FormAttachment(0);
+ formData.right = new FormAttachment(100);
+ formData.height = 18;
+ formData.bottom = new FormAttachment(100);
+ composite.setLayoutData(formData);
+ RowLayout rowLayout = new RowLayout();
+ rowLayout.fill = true;
+ rowLayout.wrap = false;
+ rowLayout.spacing = 0;
+ composite.setLayout(/*rowLayout*/ new FillLayout());
+
+ statusText = new Label(composite, SWT.BORDER);
+ //statusText.setLayoutData(new RowData(150, SWT.DEFAULT));
+ setStatusText(null);
+
+ threadsText = new Label(composite, SWT.BORDER);
+ //threadsText.setLayoutData(new RowData(50, SWT.DEFAULT));
+ threadsText.setText(Labels.getLabel("text.threads") + "0");
+
+ progressBar = new ProgressBar(composite, SWT.BORDER);
+ //progressBar.setLayoutData(new RowData());
+ progressBar.setSelection(0);
+ }
+
+ /**
+ * Used for the positioning of the controls in the MainWindow
+ */
+ Composite getComposite() {
+ return composite;
+ }
+
+ /**
+ * @return true if the underlying widgets are disposed
+ */
+ public boolean isDisposed() {
+ return composite.isDisposed();
+ }
+
+ /**
+ * Sets the status bar text displayed to the user.
+ * @param statusText the text to set, null to use the default text (Ready)
+ */
+ public void setStatusText(String statusText) {
+ if (statusText == null) {
+ statusText = Labels.getLabel("state.ready");
+ }
+ if (!this.statusText.isDisposed())
+ this.statusText.setText(statusText);
+ }
+
+ public void setRunningThreads(int runningThreads) {
+ if (!threadsText.isDisposed())
+ // TODO: make this more efficient
+ threadsText.setText(Labels.getLabel("text.threads") + runningThreads);
+ }
+
+ public void setProgress(int progress) {
+ if (!progressBar.isDisposed())
+ progressBar.setSelection(progress);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/UserErrorException.java b/ipscan/src/net/azib/ipscan/gui/UserErrorException.java
new file mode 100755
index 00000000..501fbb66
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/UserErrorException.java
@@ -0,0 +1,31 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui;
+
+import net.azib.ipscan.config.Labels;
+
+/**
+ * Exception for throwing in case of user errors.
+ * These generally result in showing an error message.
+ *
+ * @author anton
+ */
+public class UserErrorException extends RuntimeException {
+
+ private static final long serialVersionUID = 123283472834982L;
+
+ public UserErrorException(String label) {
+ super(Labels.getLabel("exception.UserErrorException." + label));
+ }
+
+ public UserErrorException(String label, Throwable cause) {
+ this(label);
+ initCause(cause);
+ }
+
+ public UserErrorException(String label, String rawInfo) {
+ super(Labels.getLabel("exception.UserErrorException." + label) + rawInfo);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/BrowserLauncher.java b/ipscan/src/net/azib/ipscan/gui/actions/BrowserLauncher.java
new file mode 100755
index 00000000..157f222e
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/BrowserLauncher.java
@@ -0,0 +1,51 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.lang.reflect.Method;
+
+import net.azib.ipscan.gui.UserErrorException;
+
+/**
+ * The cross-platform browser launcher
+ *
+ * @author anton
+ */
+public class BrowserLauncher {
+
+ /**
+ * Opens an URL in the default browser.
+ * Supports Linux/Unix, MacOS, and Windows
+ * @param url
+ */
+ public static void openURL(String url) {
+ String osName = System.getProperty("os.name");
+
+ try {
+ if (osName.startsWith("Windows")) {
+ Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
+ }
+ else
+ if (osName.startsWith("Mac OS")) {
+ Class fileMgr = Class.forName("com.apple.eio.FileManager");
+ Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] { String.class });
+ openURL.invoke(null, new Object[] { url });
+ }
+ else { // assume Linux or other Unix
+ // TODO: what if browser is already running as another user, not root?
+ String[] browsers = { "htmlview", "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" };
+ String browser = null;
+ for (int count = 0; count < browsers.length && browser == null; count++)
+ if (Runtime.getRuntime().exec(new String[] { "which", browsers[count] }).waitFor() == 0)
+ browser = browsers[count];
+ if (browser == null)
+ throw new Exception("Could not find web browser");
+ Runtime.getRuntime().exec(new String[] { browser, url });
+ }
+ }
+ catch (Exception e) {
+ throw new UserErrorException("openURL.failed", url);
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/ColumnsActions.java b/ipscan/src/net/azib/ipscan/gui/actions/ColumnsActions.java
new file mode 100755
index 00000000..6a449ba2
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/ColumnsActions.java
@@ -0,0 +1,73 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.gui.MainMenu.ColumnsMenu;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+
+/**
+ * ColumnsActions
+ *
+ * @author anton
+ */
+public class ColumnsActions {
+
+ public static class ColumnClick implements Listener {
+
+ private Menu columnsMenu;
+
+ public ColumnClick(ColumnsMenu columnsMenu) {
+ this.columnsMenu = columnsMenu;
+ }
+
+ public void handleEvent(Event e) {
+ // modify menu text a bit
+ TableColumn tableColumn = (TableColumn) e.widget;
+ MenuItem sortMenuItem = columnsMenu.getItem(0);
+ if (tableColumn.getParent().getSortColumn() == tableColumn) {
+ sortMenuItem.setText(Labels.getLabel("menu.columns.sortDirection"));
+ }
+ else {
+ sortMenuItem.setText(Labels.getLabel("menu.columns.sortBy") + tableColumn.getText());
+ }
+
+ // remember the clicked column (see SortBy below)
+ sortMenuItem.setData(tableColumn);
+
+ // show the menu
+ columnsMenu.setLocation(e.display.getCursorLocation());
+ columnsMenu.setVisible(true);
+ }
+ }
+
+ public static class SortBy implements Listener {
+
+ public void handleEvent(Event event) {
+ // retrieve the clicked column (see ColumnClick above)
+ TableColumn tableColumn = (TableColumn) event.widget.getData();
+
+ Table table = tableColumn.getParent();
+
+ if (table.getSortColumn() != tableColumn) {
+ table.setSortColumn(tableColumn);
+ table.setSortDirection(SWT.UP);
+ }
+ else {
+ table.setSortDirection(table.getSortDirection() == SWT.UP ? SWT.DOWN : SWT.UP);
+ }
+
+ // TODO: execute ScanningResultList.sort() here!!!
+ }
+
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/CommandsActions.java b/ipscan/src/net/azib/ipscan/gui/actions/CommandsActions.java
new file mode 100755
index 00000000..7d905070
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/CommandsActions.java
@@ -0,0 +1,196 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.util.Iterator;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.OpenersConfig.Opener;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+import net.azib.ipscan.gui.DetailsWindow;
+import net.azib.ipscan.gui.EditOpenersDialog;
+import net.azib.ipscan.gui.ResultTable;
+import net.azib.ipscan.gui.StatusBar;
+import net.azib.ipscan.gui.UserErrorException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.Clipboard;
+import org.eclipse.swt.dnd.TextTransfer;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+/**
+ * Commands and Context menu Actions.
+ * All these operate on the items, selected in the results list.
+ * TODO: check for selection everywhere
+ *
+ * @author anton
+ */
+public class CommandsActions {
+
+ public static class Details implements Listener {
+ private ResultTable resultTable;
+
+ public Details(ResultTable resultTable) {
+ this.resultTable = resultTable;
+ }
+
+ public void handleEvent(Event event) {
+ checkSelection(resultTable);
+ new DetailsWindow(resultTable).open();
+ }
+ }
+
+ public static class Delete implements Listener {
+ private ResultTable resultTable;
+
+ public Delete(ResultTable resultTable) {
+ this.resultTable = resultTable;
+ }
+
+ public void handleEvent(Event event) {
+ checkSelection(resultTable);
+ int firstSelection = resultTable.getSelectionIndex();
+ resultTable.remove(resultTable.getSelectionIndices());
+ resultTable.setSelection(firstSelection);
+ }
+ }
+
+ public static class CopyIP implements Listener {
+ private ResultTable resultTable;
+
+ public CopyIP(ResultTable resultTable) {
+ this.resultTable = resultTable;
+ }
+
+ public void handleEvent(Event event) {
+ checkSelection(resultTable);
+ Clipboard clipboard = new Clipboard(event.display);
+ clipboard.setContents(new Object[] {resultTable.getItem(resultTable.getSelectionIndex()).getText()}, new Transfer[] {TextTransfer.getInstance()});
+ clipboard.dispose();
+ }
+ }
+
+ public static class CopyIPDetails implements Listener {
+ private ResultTable resultTable;
+
+ public CopyIPDetails(ResultTable resultTable) {
+ this.resultTable = resultTable;
+ }
+
+ public void handleEvent(Event event) {
+ checkSelection(resultTable);
+ Clipboard clipboard = new Clipboard(event.display);
+ clipboard.setContents(new Object[] {resultTable.getIPDetails()}, new Transfer[] {TextTransfer.getInstance()});
+ clipboard.dispose();
+ }
+ }
+
+ /**
+ * Checks that there is at least one item selected in the results list.
+ * @param mainWindow
+ */
+ private static void checkSelection(ResultTable resultTable) {
+ if (resultTable.getItemCount() <= 0) {
+ throw new UserErrorException("commands.noResults");
+ }
+ else
+ if (resultTable.getSelectionIndex() < 0) {
+ throw new UserErrorException("commands.noSelection");
+ }
+ }
+
+ public static class ShowOpenersMenu implements Listener {
+
+ private Listener openersSelectListener;
+
+ public ShowOpenersMenu(SelectOpener selectOpener) {
+ this.openersSelectListener = selectOpener;
+ }
+
+ public void handleEvent(Event event) {
+ Menu openersMenu = (Menu)event.widget;
+ MenuItem[] menuItems = openersMenu.getItems();
+ for (int i = 2; i < menuItems.length; i++) {
+ menuItems[i].dispose();
+ }
+
+ // update menu items
+ int index = 0;
+ for (Iterator i = Config.getOpenersConfig().iterateNames(); i.hasNext();) {
+ MenuItem menuItem = new MenuItem(openersMenu, SWT.CASCADE);
+ String name = (String)i.next();
+
+ index++;
+ if (index <= 9) {
+ name += "\tCtrl+" + index;
+ menuItem.setAccelerator(SWT.CONTROL | ('0' + index));
+ }
+
+ menuItem.setText(name);
+ menuItem.setData(new Integer(index));
+ menuItem.addListener(SWT.Selection, openersSelectListener);
+ }
+
+ }
+ }
+
+ public static class EditOpeners implements Listener {
+
+ FetcherRegistry fetcherRegistry;
+
+ public EditOpeners(FetcherRegistry fetcherRegistry) {
+ this.fetcherRegistry = fetcherRegistry;
+ }
+
+ public void handleEvent(Event event) {
+ new EditOpenersDialog(fetcherRegistry).open();
+ }
+ }
+
+ public static class SelectOpener implements Listener {
+
+ private StatusBar statusBar;
+ private ResultTable resultTable;
+ private OpenerLauncher openerLauncher;
+
+ public SelectOpener(StatusBar statusBar, ResultTable resultTable, OpenerLauncher openerLauncher) {
+ this.statusBar = statusBar;
+ this.resultTable = resultTable;
+ this.openerLauncher = openerLauncher;
+ }
+
+ public void handleEvent(Event event) {
+ MenuItem menuItem = (MenuItem) event.widget;
+ String name = menuItem.getText();
+ int indexOf = name.lastIndexOf('\t');
+ if (indexOf >= 0) {
+ name = name.substring(0, indexOf);
+ }
+ Opener opener = Config.getOpenersConfig().getOpener(name);
+
+ int selectedItem = resultTable.getSelectionIndex();
+ if (selectedItem < 0) {
+ throw new UserErrorException("commands.noSelection");
+ }
+
+ try {
+ statusBar.setStatusText(Labels.getLabel("state.opening") + name);
+ openerLauncher.launch(opener, selectedItem);
+ // wait a bit to make status visible
+ // TODO: somehow wait until the process is started
+ Thread.sleep(500);
+ }
+ catch (InterruptedException e) {}
+ finally {
+ statusBar.setStatusText(null);
+ }
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/FavoritesActions.java b/ipscan/src/net/azib/ipscan/gui/actions/FavoritesActions.java
new file mode 100755
index 00000000..e8a9bc1e
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/FavoritesActions.java
@@ -0,0 +1,109 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.util.Iterator;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.NamedListConfig;
+import net.azib.ipscan.gui.EditFavoritesDialog;
+import net.azib.ipscan.gui.InputDialog;
+import net.azib.ipscan.gui.UserErrorException;
+import net.azib.ipscan.gui.feeders.FeederGUIRegistry;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+/**
+ * FavoritesActions
+ *
+ * @author anton
+ */
+public class FavoritesActions {
+
+ public static class Add implements Listener {
+ private FeederGUIRegistry feederRegistry;
+
+ public Add(FeederGUIRegistry feederRegistry) {
+ this.feederRegistry = feederRegistry;
+ }
+
+ public void handleEvent(Event event) {
+ String feederInfo = feederRegistry.current().getInfo();
+ InputDialog inputDialog = new InputDialog(
+ Labels.getLabel("title.favorite.add"),
+ Labels.getLabel("text.favorite.add"));
+ String favoriteName = inputDialog.open(feederInfo);
+
+ if (favoriteName != null) {
+ NamedListConfig favoritesConfig = Config.getFavoritesConfig();
+ if (favoritesConfig.get(favoriteName) != null) {
+ throw new UserErrorException("favorite.alreadyExists");
+ }
+ String serializedFeeder = feederRegistry.current().getFeederName() + '\t' + feederRegistry.current().serialize();
+ favoritesConfig.add(favoriteName, serializedFeeder);
+ }
+ }
+ }
+
+ public static class Select implements Listener {
+ private FeederGUIRegistry feederRegistry;
+
+ public Select(FeederGUIRegistry feederRegistry) {
+ this.feederRegistry = feederRegistry;
+ }
+
+ public void handleEvent(Event event) {
+ MenuItem menuItem = (MenuItem) event.widget;
+ String serializedFeeder = Config.getFavoritesConfig().get(menuItem.getText());
+
+ int indexOf = serializedFeeder.indexOf('\t');
+ String feederName = serializedFeeder.substring(0, indexOf);
+ serializedFeeder = serializedFeeder.substring(indexOf + 1);
+
+ feederRegistry.select(feederName);
+ feederRegistry.current().unserialize(serializedFeeder);
+ }
+ }
+
+ public static class Edit implements Listener {
+ public void handleEvent(Event event) {
+ new EditFavoritesDialog().open();
+ }
+ }
+
+ public static class ShowMenu implements Listener {
+ private Listener favoritesSelectListener;
+
+ public ShowMenu(Select favoritesSelectListener) {
+ // the listener for favorites selections from the menu
+ this.favoritesSelectListener = favoritesSelectListener;
+ }
+
+ public void handleEvent(Event event) {
+ Menu favoritesMenu = (Menu) event.widget;
+ // populate favorites in the menu
+ NamedListConfig favoritesConfig = Config.getFavoritesConfig();
+
+ // note: 3 is the number of items in the menu when no favorites exist
+ // dispose old favorites
+ MenuItem[] menuItems = favoritesMenu.getItems();
+ for (int i = 3; i < menuItems.length; i++) {
+ menuItems[i].dispose();
+ }
+
+ // update favorites menu items
+ for (Iterator i = favoritesConfig.iterateNames(); i.hasNext();) {
+ MenuItem menuItem = new MenuItem(favoritesMenu, SWT.CASCADE);
+ menuItem.setText((String) i.next());
+ menuItem.addListener(SWT.Selection, favoritesSelectListener);
+ }
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/FeederActions.java b/ipscan/src/net/azib/ipscan/gui/actions/FeederActions.java
new file mode 100755
index 00000000..4bf78720
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/FeederActions.java
@@ -0,0 +1,155 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.azib.ipscan.core.InetAddressUtils;
+import net.azib.ipscan.feeders.FeederException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * FeederActions
+ * TODO: tests
+ *
+ * @author anton
+ */
+public class FeederActions {
+
+ private static final Logger LOG = Logger.getLogger(FeederActions.class.getName());
+
+ public static class HostnameButton implements SelectionListener, TraverseListener {
+
+ private String localHostname;
+ private Text hostnameText;
+ private Text ipText;
+
+ public HostnameButton(Text hostnameText, Text ipText) {
+ this.hostnameText = hostnameText;
+ this.ipText = ipText;
+ try {
+ this.localHostname = InetAddress.getLocalHost().getHostName();
+ }
+ catch (UnknownHostException e) {
+ // do nothing...
+ }
+ }
+
+ public void widgetDefaultSelected(SelectionEvent event) {
+ widgetSelected(event);
+ }
+
+ public void widgetSelected(SelectionEvent event) {
+ String hostname = hostnameText.getText();
+
+ String address = null;
+ if (hostname.equals(localHostname)) {
+ // retrieve local address(es)
+ askLocalIPAddress();
+ }
+ else {
+ // resolve remote address
+ try {
+ address = InetAddressUtils.getAddressByName(hostname);
+
+ // now update the hostname itself using a reverse lookup
+ try {
+ String realHostname = InetAddress.getByName(address).getCanonicalHostName();
+ if (!address.equals(realHostname)) {
+ // if a hostname was returned, not the same IP adress
+ hostnameText.setText(realHostname);
+ hostnameText.setSelection(realHostname.length());
+ }
+ }
+ catch (UnknownHostException e) {
+ // ignore if this one fails
+ }
+ }
+ catch (UnknownHostException e) {
+ throw new FeederException("invalidHostname");
+ }
+
+ ipText.setText(address);
+ }
+ }
+
+ public void keyTraversed(TraverseEvent e) {
+ if (e.detail == SWT.TRAVERSE_RETURN) {
+ widgetSelected(null);
+ e.doit = false;
+ }
+ }
+
+ /**
+ * Asks user which local IP address they want to use
+ */
+ private void askLocalIPAddress() {
+ try {
+ Menu popupMenu = new Menu(Display.getCurrent().getActiveShell(), SWT.POP_UP);
+ Listener menuItemListener = new Listener() {
+ public void handleEvent(Event event) {
+ MenuItem menuItem = (MenuItem) event.widget;
+ String address = (String) menuItem.getData();
+ ipText.setText(address);
+ menuItem.getParent().dispose();
+ }
+ };
+
+
+ for (Enumeration i = NetworkInterface.getNetworkInterfaces(); i.hasMoreElements(); ) {
+ NetworkInterface networkInterface = (NetworkInterface) i.nextElement();
+ for (Enumeration i2 = networkInterface.getInetAddresses(); i2.hasMoreElements();) {
+ InetAddress currentAddress = (InetAddress) i2.nextElement();
+ // TODO: we would benefit of Java 1.6 here by automatically initializing the netmask, too
+
+ if (!currentAddress.isLoopbackAddress()) {
+ MenuItem menuItem = new MenuItem(popupMenu, 0);
+ menuItem.setText(networkInterface.getDisplayName() + ": " + currentAddress.getHostAddress());
+ menuItem.setData(currentAddress.getHostAddress());
+ menuItem.addListener(SWT.Selection, menuItemListener);
+ }
+ }
+ }
+
+ if (popupMenu.getItemCount() > 1) {
+ popupMenu.setLocation(Display.getCurrent().getCursorLocation());
+ popupMenu.setVisible(true);
+ }
+ else {
+ // emulate click on the single menu item
+ if (popupMenu.getItemCount() == 1) {
+ Event event = new Event();
+ event.widget = popupMenu.getItem(0);
+ menuItemListener.handleEvent(event);
+ popupMenu.dispose();
+ }
+ // otherwise, unable to retrieve any sane local addresses,
+ // leave the field as-is, which probably shows the loopback address already
+ }
+ }
+ catch (SocketException e) {
+ LOG.log(Level.FINE, "Cannot enumerate network interfaces", e);
+ }
+ }
+ }
+
+}
+
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/FileActions.java b/ipscan/src/net/azib/ipscan/gui/actions/FileActions.java
new file mode 100755
index 00000000..e183dc1d
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/FileActions.java
@@ -0,0 +1,124 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.ScanningResult;
+import net.azib.ipscan.exporters.ExportProcessor;
+import net.azib.ipscan.exporters.Exporter;
+import net.azib.ipscan.exporters.ExporterRegistry;
+import net.azib.ipscan.exporters.ExportProcessor.ScanningResultSelector;
+import net.azib.ipscan.gui.ResultTable;
+import net.azib.ipscan.gui.StatusBar;
+import net.azib.ipscan.gui.UserErrorException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Listener;
+
+/**
+ * FileActions
+ *
+ * @author anton
+ */
+public class FileActions {
+
+ public static class Exit implements Listener {
+ public void handleEvent(Event event) {
+ event.display.getActiveShell().close();
+ }
+ }
+
+ private static class SaveResults implements Listener {
+ private ExporterRegistry exporterRegistry;
+ private ResultTable resultTable;
+ private StatusBar statusBar;
+ private boolean isSelection;
+
+ private SaveResults(ExporterRegistry exporterRegistry, ResultTable resultTable, StatusBar statusBar, boolean isSelection) {
+ this.exporterRegistry = exporterRegistry;
+ this.resultTable = resultTable;
+ this.statusBar = statusBar;
+ // TODO: implement isSelection
+ this.isSelection = isSelection;
+ }
+
+ public void handleEvent(Event event) {
+ if (resultTable.getItemCount() <= 0) {
+ throw new UserErrorException("commands.noResults");
+ }
+
+ // create the file dialog
+ FileDialog fileDialog = new FileDialog(resultTable.getShell(), SWT.SAVE);
+
+ // gather lists of extensions and exporter names
+ List extensions = new ArrayList();
+ List descriptions = new ArrayList();
+ StringBuffer labelBuffer = new StringBuffer(Labels.getLabel(isSelection ? "title.saveSelection" : "title.saveAll"));
+ addFileExtensions(extensions, descriptions, labelBuffer);
+
+ // initialize other stuff
+ fileDialog.setText(labelBuffer.toString());
+ fileDialog.setFilterExtensions((String[]) extensions.toArray(new String[extensions.size()]));
+ fileDialog.setFilterNames((String[]) descriptions.toArray(new String[descriptions.size()]));
+
+ // show the dialog and receive the filename
+ String fileName = fileDialog.open();
+
+ // check the received file name
+ if (fileName != null) {
+ // create exporter instance
+ Exporter exporter = exporterRegistry.createExporter(fileName);
+
+ statusBar.setStatusText(Labels.getLabel("state.saving"));
+
+ ExportProcessor exportProcessor = new ExportProcessor(exporter, fileName);
+
+ // in case of isSelection we need to create our ScanningResultSelector
+ ScanningResultSelector scanningResultSelector = null;
+ if (isSelection) {
+ scanningResultSelector = new ScanningResultSelector() {
+ public boolean isResultSelected(int index, ScanningResult result) {
+ return resultTable.isSelected(index);
+ }
+ };
+ }
+
+ exportProcessor.process(resultTable.getScanningResults(), resultTable.getFeederInfo(), scanningResultSelector);
+
+ statusBar.setStatusText(null);
+ }
+ }
+
+ private void addFileExtensions(List extensions, List descriptions, StringBuffer sb) {
+ sb.append(" (");
+ for (Iterator i = exporterRegistry.iterator(); i.hasNext(); ) {
+ Exporter exporter = (Exporter) i.next();
+ extensions.add("*." + exporter.getFilenameExtension());
+ sb.append(exporter.getFilenameExtension()).append(", ");;
+ descriptions.add(Labels.getLabel(exporter.getLabel()));
+ }
+ // strip the last comma
+ sb.delete(sb.length() - 2, sb.length());
+ sb.append(")");
+ }
+ }
+
+ public static class SaveAll extends SaveResults {
+ public SaveAll(ExporterRegistry exporterRegistry, ResultTable resultTable, StatusBar statusBar) {
+ super(exporterRegistry, resultTable, statusBar, false);
+ }
+ }
+
+ public static class SaveSelection extends SaveResults {
+ public SaveSelection(ExporterRegistry exporterRegistry, ResultTable resultTable, StatusBar statusBar) {
+ super(exporterRegistry, resultTable, statusBar, true);
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/GotoActions.java b/ipscan/src/net/azib/ipscan/gui/actions/GotoActions.java
new file mode 100755
index 00000000..d6ba6577
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/GotoActions.java
@@ -0,0 +1,153 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.util.Iterator;
+import java.util.List;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.ScanningResult;
+import net.azib.ipscan.core.ScanningResultList;
+import net.azib.ipscan.core.ScanningSubject;
+import net.azib.ipscan.gui.InputDialog;
+import net.azib.ipscan.gui.ResultTable;
+import net.azib.ipscan.gui.StatusBar;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.widgets.Shell;
+
+/**
+ * GotoActions
+ *
+ * @author anton
+ */
+public class GotoActions {
+
+ private static class NextHost implements Listener {
+
+ private ResultTable resultTable;
+ private int whatToSearchFor;
+
+ NextHost(ResultTable resultTable, int whatToSearchFor) {
+ this.resultTable = resultTable;
+ this.whatToSearchFor = whatToSearchFor;
+ }
+
+ public void handleEvent(Event event) {
+ ScanningResultList results = resultTable.getScanningResults();
+
+ int numElements = resultTable.getItemCount();
+ int startElement = resultTable.getSelectionIndex() + 1;
+
+ for (int i = startElement; i < numElements; i++) {
+ ScanningResult scanningResult = results.getResult(i);
+
+ if (scanningResult.getType() >= whatToSearchFor) {
+ resultTable.setSelection(i);
+ resultTable.setFocus();
+ return;
+ }
+
+ }
+
+ if (startElement > 0) {
+ resultTable.deselectAll();
+ handleEvent(event);
+ }
+ }
+
+ }
+
+ public static class NextAliveHost extends NextHost {
+ public NextAliveHost(ResultTable resultTable) {
+ super(resultTable, ScanningSubject.RESULT_TYPE_ALIVE);
+ }
+ }
+
+ public static class NextDeadHost extends NextHost {
+ public NextDeadHost(ResultTable resultTable) {
+ super(resultTable, ScanningSubject.RESULT_TYPE_DEAD);
+ }
+ }
+
+ public static class NextHostWithInfo extends NextHost {
+ public NextHostWithInfo(ResultTable resultTable) {
+ super(resultTable, ScanningSubject.RESULT_TYPE_ADDITIONAL_INFO);
+ }
+ }
+
+ public static class Find implements Listener {
+
+ private ResultTable resultTable;
+ private StatusBar statusBar;
+ private String lastText = "";
+
+ public Find(StatusBar statusBar, ResultTable resultTable) {
+ this.statusBar = statusBar;
+ this.resultTable = resultTable;
+ }
+
+ public void handleEvent(Event event) {
+ InputDialog dialog = new InputDialog(Labels.getLabel("title.find"), Labels.getLabel("text.find"));
+ String text = dialog.open(lastText);
+ if (text == null) {
+ return;
+ }
+ lastText = text;
+
+ try {
+ statusBar.setStatusText(Labels.getLabel("state.searching"));
+
+ findText(text, event.display.getActiveShell());
+ }
+ finally {
+ statusBar.setStatusText(null);
+ }
+ }
+
+ private void findText(String text, Shell activeShell) {
+ ScanningResultList results = resultTable.getScanningResults();
+
+ int numElements = resultTable.getItemCount();
+ int startElement = resultTable.getSelectionIndex() + 1;
+
+ for (int i = startElement; i < numElements; i++) {
+ ScanningResult scanningResult = results.getResult(i);
+
+ List values = scanningResult.getValues();
+
+ for (Iterator j = values.iterator(); j.hasNext();) {
+ String value = (String) j.next();
+
+ // TODO: case-insensitive search
+ if (value != null && value.indexOf(text) >= 0) {
+ resultTable.setSelection(i);
+ resultTable.setFocus();
+ return;
+ }
+ }
+ }
+
+ if (startElement > 0) {
+ MessageBox messageBox = new MessageBox(activeShell, SWT.YES | SWT.NO | SWT.ICON_QUESTION);
+ messageBox.setText(Labels.getLabel("title.find"));
+ messageBox.setMessage(Labels.getLabel("text.find.notFound") + " " + Labels.getLabel("text.find.restart"));
+ if (messageBox.open() == SWT.YES) {
+ resultTable.deselectAll();
+ findText(text, activeShell);
+ }
+ }
+ else {
+ MessageBox messageBox = new MessageBox(activeShell, SWT.OK | SWT.ICON_INFORMATION);
+ messageBox.setText(Labels.getLabel("title.find"));
+ messageBox.setMessage(Labels.getLabel("text.find.notFound"));
+ messageBox.open();
+ }
+ }
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/HelpActions.java b/ipscan/src/net/azib/ipscan/gui/actions/HelpActions.java
new file mode 100755
index 00000000..db56f87c
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/HelpActions.java
@@ -0,0 +1,109 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.config.Version;
+import net.azib.ipscan.gui.AboutWindow;
+import net.azib.ipscan.gui.GettingStartedWindow;
+import net.azib.ipscan.gui.StatusBar;
+import net.azib.ipscan.gui.UserErrorException;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.MessageBox;
+
+/**
+ * HelpActions
+ *
+ * @author anton
+ */
+public class HelpActions {
+
+ public static class GettingStarted implements Listener {
+ public void handleEvent(Event event) {
+ new GettingStartedWindow().open();
+ }
+ }
+
+ public static class About implements Listener {
+ public void handleEvent(Event event) {
+ new AboutWindow().open();
+ }
+ }
+
+ public static class Website implements Listener {
+ public void handleEvent(Event event) {
+ BrowserLauncher.openURL(Version.WEBSITE);
+ }
+ }
+
+ public static class Forum implements Listener {
+ public void handleEvent(Event event) {
+ BrowserLauncher.openURL(Version.FORUM_URL);
+ }
+ }
+
+ public static class Plugins implements Listener {
+ public void handleEvent(Event event) {
+ BrowserLauncher.openURL(Version.PLUGINS_URL);
+ }
+ }
+
+ public static class CheckVersion implements Listener {
+ private StatusBar statusBar;
+
+ public CheckVersion(StatusBar statusBar) {
+ this.statusBar = statusBar;
+ }
+
+ public void handleEvent(Event event) {
+ BufferedReader reader = null;
+ try {
+ statusBar.setStatusText(Labels.getLabel("state.retrievingVersion"));
+
+ URL url = new URL(Version.LATEST_VERSION_URL);
+ URLConnection conn = url.openConnection();
+ reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+
+ String latestVersion = reader.readLine();
+ latestVersion = latestVersion.substring(latestVersion.indexOf(' ')+1);
+
+ reader.close();
+
+ MessageBox messageBox = new MessageBox(event.display.getActiveShell(), SWT.ICON_INFORMATION);
+ messageBox.setText(Version.FULL_NAME);
+
+ if (!Version.VERSION.equals(latestVersion)) {
+ String message = Labels.getLabel("text.version.old");
+ message = message.replaceFirst("%LATEST", latestVersion);
+ message = message.replaceFirst("%VERSION", Version.VERSION);
+ messageBox.setMessage(message);
+ }
+ else {
+ messageBox.setMessage(Labels.getLabel("text.version.latest"));
+ }
+ messageBox.open();
+ }
+ catch (Exception e) {
+ throw new UserErrorException("version.latestFailed", e);
+ }
+ finally {
+ try {
+ reader.close();
+ }
+ catch (IOException e) {}
+
+ statusBar.setStatusText(null);
+ }
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/OpenerLauncher.java b/ipscan/src/net/azib/ipscan/gui/actions/OpenerLauncher.java
new file mode 100755
index 00000000..5329a631
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/OpenerLauncher.java
@@ -0,0 +1,91 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import net.azib.ipscan.config.OpenersConfig.Opener;
+import net.azib.ipscan.core.ScanningResultList;
+import net.azib.ipscan.fetchers.FetcherRegistry;
+import net.azib.ipscan.gui.UserErrorException;
+
+/**
+ * OpenerLauncher
+ *
+ * @author anton
+ */
+public class OpenerLauncher {
+
+ private FetcherRegistry fetcherRegistry;
+ private ScanningResultList scanningResults;
+
+ public OpenerLauncher(FetcherRegistry fetcherRegistry, ScanningResultList scanningResults) {
+ this.fetcherRegistry = fetcherRegistry;
+ this.scanningResults = scanningResults;
+ }
+
+ public void launch(Opener opener, int selectedItem) {
+ String openerString = prepareOpenerStringForItem(opener.execString, selectedItem);
+
+ // check for URLs
+ if (openerString.startsWith("http:") || openerString.startsWith("https:") || openerString.startsWith("ftp:") || openerString.startsWith("mailto:") || openerString.startsWith("\\\\")) {
+ BrowserLauncher.openURL(openerString);
+ }
+ else {
+ // run a process here
+ try {
+ if (opener.inTerminal) {
+ TerminalLauncher.launchInTerminal(openerString, opener.workingDir);
+ }
+ else {
+ // TODO: we probably need to support shell patterns, etc
+ // TODO: merge launchInTerminal with this code
+ Runtime.getRuntime().exec(openerString, null, opener.workingDir);
+ }
+ }
+ catch (Exception e) {
+ Logger.global.log(Level.WARNING, "opener.failed", e);
+ throw new UserErrorException("opener.failed", openerString);
+ }
+ }
+ }
+
+ /**
+ * Replaces references to scanned values in an opener string.
+ * Refefernces look like ${fetcher_label}
+ * @param openerString
+ * @return opener string with values replaced
+ */
+ String prepareOpenerStringForItem(String openerString, int selectedItem) {
+ Pattern paramsPattern = Pattern.compile("\\$\\{(.+?)\\}");
+ Matcher matcher = paramsPattern.matcher(openerString);
+ StringBuffer sb = new StringBuffer(64);
+ while (matcher.find()) {
+ // resolve the required fetcher
+ String fetcherName = matcher.group(1);
+ int fetcherIndex = fetcherRegistry.getSelectedFetcherIndex(fetcherName);
+ if (fetcherIndex < 0) {
+ throw new UserErrorException("opener.unknownFetcher", fetcherName);
+ }
+
+ // retrieve the scanned value
+ try {
+ String scannedValue = getScannedValue(selectedItem, fetcherIndex);
+ matcher.appendReplacement(sb, scannedValue);
+ }
+ catch (Exception e) {
+ throw new UserErrorException("opener.nullFetcherValue", fetcherName);
+ }
+ }
+ matcher.appendTail(sb);
+ return sb.toString();
+ }
+
+ String getScannedValue(int selectedItem, int fetcherIndex) {
+ return (String) scanningResults.getResult(selectedItem).getValues().get(fetcherIndex);
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java b/ipscan/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java
new file mode 100755
index 00000000..3a67edb6
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/StartStopScanningAction.java
@@ -0,0 +1,137 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.net.InetAddress;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.ScannerThread;
+import net.azib.ipscan.core.ScannerThreadFactory;
+import net.azib.ipscan.core.ScanningStateCallback;
+import net.azib.ipscan.gui.ResultTable;
+import net.azib.ipscan.gui.ScanningResultsConsumer;
+import net.azib.ipscan.gui.StatusBar;
+import net.azib.ipscan.gui.feeders.FeederGUIRegistry;
+
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Start/Stop button action class.
+ * It listens to presses on the buttons as well as updates gui statuses
+ *
+ * @author anton
+ */
+public class StartStopScanningAction implements SelectionListener, ScanningStateCallback {
+
+ private ScannerThreadFactory scannerThreadFactory;
+ private ScannerThread scannerThread;
+
+ private StatusBar statusBar;
+ private ResultTable resultTable;
+ private FeederGUIRegistry feederRegistry;
+ private Button button;
+ private Image[] images = new Image[4];
+
+ private Display display;
+
+ private int state = ScanningStateCallback.STATE_IDLE;
+
+ public StartStopScanningAction(ScannerThreadFactory scannerThreadFactory, ResultTable resultTable, StatusBar statusBar, FeederGUIRegistry feederRegistry, Button startStopButton) {
+ this.scannerThreadFactory = scannerThreadFactory;
+ this.resultTable = resultTable;
+ this.statusBar = statusBar;
+ this.feederRegistry = feederRegistry;
+ this.button = startStopButton;
+ this.display = button.getDisplay();
+
+ // pre-load button images
+ images[ScanningStateCallback.STATE_IDLE] = new Image(null, Labels.getInstance().getImageAsStream("button.start.img"));
+ images[ScanningStateCallback.STATE_SCANNING] = new Image(null, Labels.getInstance().getImageAsStream("button.stop.img"));
+ images[ScanningStateCallback.STATE_STOPPING] = new Image(null, Labels.getInstance().getImageAsStream("button.kill.img"));
+ images[ScanningStateCallback.STATE_KILLING] = images[ScanningStateCallback.STATE_STOPPING];
+
+ // set the defaultimage
+ button.setImage(images[state]);
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ switch (state) {
+ case ScanningStateCallback.STATE_IDLE:
+ // start the scan!
+ resultTable.initNewScan(feederRegistry.current().getInfo());
+ ScanningResultsConsumer resultsConsumer = new ScanningResultsConsumer(resultTable);
+ scannerThread = scannerThreadFactory.createScannerThread(feederRegistry.current().getFeeder());
+ scannerThread.setResultsCallback(resultsConsumer);
+ scannerThread.setStatusCallback(this);
+ scannerThread.start();
+ break;
+ case ScanningStateCallback.STATE_SCANNING:
+ scannerThread.forceStop();
+ break;
+ case ScanningStateCallback.STATE_STOPPING:
+ scannerThread.abort();
+ break;
+ case ScanningStateCallback.STATE_KILLING:
+ break;
+ }
+ }
+
+ public void scannerStateChanged(int status) {
+ this.state = status;
+ if (display.isDisposed())
+ return;
+ display.asyncExec(new Runnable() {
+ public void run() {
+ if (statusBar.isDisposed())
+ return;
+
+ switch (StartStopScanningAction.this.state) {
+ case STATE_IDLE:
+ // reset state text
+ statusBar.setStatusText(null);
+ statusBar.setProgress(0);
+ break;
+ case STATE_STOPPING:
+ statusBar.setStatusText(Labels.getLabel("state.waitForThreads"));
+ break;
+ case STATE_KILLING:
+ statusBar.setStatusText(Labels.getLabel("state.killingThreads"));
+ break;
+ }
+ // change button image
+ button.setImage(images[StartStopScanningAction.this.state]);
+ }
+ });
+ }
+
+ public void updateProgress(final InetAddress currentAddress, final int runningThreads, final int percentageComplete) {
+ if (display.isDisposed())
+ return;
+ display.asyncExec(new Runnable() {
+ public void run() {
+ if (statusBar.isDisposed())
+ return;
+
+ if (currentAddress != null) {
+ statusBar.setStatusText(Labels.getLabel("state.scanning") + currentAddress.getHostAddress());
+ }
+
+ statusBar.setRunningThreads(runningThreads);
+ statusBar.setProgress(percentageComplete);
+
+ // change button image
+ button.setImage(images[StartStopScanningAction.this.state]);
+ }
+ });
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/TerminalLauncher.java b/ipscan/src/net/azib/ipscan/gui/actions/TerminalLauncher.java
new file mode 100755
index 00000000..cf9a38a9
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/TerminalLauncher.java
@@ -0,0 +1,52 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.azib.ipscan.gui.UserErrorException;
+
+/**
+ * The cross-platform terminal launcher
+ *
+ * @author anton
+ */
+public class TerminalLauncher {
+
+ /**
+ * Launches the execString in the terminal.
+ * Supports Linux/Unix, MacOS, and Windows
+ * @param execString the command to launch
+ * @param workingDir the working directory (or null)
+ */
+ public static void launchInTerminal(String execString, File workingDir) {
+ String osName = System.getProperty("os.name");
+
+ try {
+ if (osName.startsWith("Windows")) {
+ // generate a command file :-)
+ File batFile = File.createTempFile("launch", ".cmd");
+ batFile.deleteOnExit();
+ FileWriter writer = new FileWriter(batFile);
+ writer.write("@rem This is a temporary file generated by Angry IP Scanner\n" +
+ "@start cmd /k " + execString);
+ writer.close();
+
+ Runtime.getRuntime().exec(batFile.getAbsolutePath(), null, workingDir);
+ }
+ else { // assume Linux or other Unix
+ // TODO: maybe gnome-terminal, konsole, and MacOS-specific terminal should be tried as well...
+ // TODO: it seems that MacOS can use the $TERM environment variable to launch a terminal
+ Runtime.getRuntime().exec(new String[] {"xterm", "-e", "bash", "-c", execString + ";bash"}, null, workingDir);
+ }
+ }
+ catch (Exception e) {
+ Logger.global.log(Level.WARNING, "openTerminal.failed", e);
+ throw new UserErrorException("openTerminal.failed", execString);
+ }
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/actions/ToolsActions.java b/ipscan/src/net/azib/ipscan/gui/actions/ToolsActions.java
new file mode 100755
index 00000000..618980fe
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/actions/ToolsActions.java
@@ -0,0 +1,45 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.actions;
+
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+import net.azib.ipscan.gui.OptionsWindow;
+import net.azib.ipscan.gui.SelectFetchersDialog;
+
+/**
+ * ToolsActions
+ *
+ * @author anton
+ */
+public class ToolsActions {
+
+ public static class Options implements Listener {
+
+ private OptionsWindow optionsWindow;
+
+ public Options(OptionsWindow optionsWindow) {
+ this.optionsWindow = optionsWindow;
+ }
+
+ public void handleEvent(Event event) {
+ optionsWindow.open();
+ }
+ }
+
+ public static class SelectFetchers implements Listener {
+
+ private SelectFetchersDialog selectFetchersDialog;
+
+ public SelectFetchers(SelectFetchersDialog selectFetchersDialog) {
+ this.selectFetchersDialog = selectFetchersDialog;
+ }
+
+ public void handleEvent(Event event) {
+ selectFetchersDialog.open();
+ }
+
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/feeders/AbstractFeederGUI.java b/ipscan/src/net/azib/ipscan/gui/feeders/AbstractFeederGUI.java
new file mode 100755
index 00000000..8cf0308a
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/feeders/AbstractFeederGUI.java
@@ -0,0 +1,67 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.feeders;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.feeders.Feeder;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * Base class of feeder GUI classes.
+ *
+ * @author anton
+ */
+public abstract class AbstractFeederGUI extends Composite {
+
+ protected Feeder feeder;
+
+ public AbstractFeederGUI(Composite parent) {
+ super(parent, SWT.NONE);
+ setVisible(false);
+ initialize();
+ }
+
+ protected abstract void initialize();
+
+ /**
+ * Initializes a Feeder instance using the parameters, provided by the GUI.
+ * @return initialized feeder instance
+ */
+ public abstract Feeder getFeeder();
+
+ /**
+ * @return the feeder name
+ */
+ public String getFeederName() {
+ return Labels.getLabel(feeder.getLabel());
+ }
+
+ /**
+ * For internal usage, returns the feeder-specific label
+ */
+ protected String getStringLabel(String name) {
+ return Labels.getLabel(feeder.getLabel() + '.' + name);
+ }
+
+ /**
+ * @return the feeder's name and the information about its current settings
+ */
+ public String getInfo() {
+ // getFeeder() will probably double-initialize the feeder, but it is safer
+ return getFeederName() + ": " + getFeeder().getInfo();
+ }
+
+ /**
+ * @return serialized settings to a String
+ */
+ public abstract String serialize();
+
+ /**
+ * Restores previously serialized settings.
+ * @param serialized
+ */
+ public abstract void unserialize(String serialized);
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/feeders/AbstractFeederGUITest.java b/ipscan/src/net/azib/ipscan/gui/feeders/AbstractFeederGUITest.java
new file mode 100755
index 00000000..4c1b25a1
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/feeders/AbstractFeederGUITest.java
@@ -0,0 +1,51 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.feeders;
+
+import org.eclipse.swt.widgets.Shell;
+
+import net.azib.ipscan.feeders.Feeder;
+import net.azib.ipscan.feeders.RangeFeeder;
+import junit.framework.TestCase;
+
+/**
+ * AbstractFeederGUITest
+ *
+ * @author anton
+ */
+public class AbstractFeederGUITest extends TestCase {
+
+ private AbstractFeederGUI feederGUI;
+
+ protected void setUp() throws Exception {
+ feederGUI = new AbstractFeederGUI(new Shell()) {
+
+ protected void initialize() {
+ }
+
+ public String getFeederName() {
+ return "Mega Feeder";
+ }
+
+ public Feeder getFeeder() {
+ feeder = new RangeFeeder();
+ feeder.initialize(new String[] {"127.0.0.1", "127.0.0.2"});
+ return feeder;
+ }
+
+ public String serialize() {
+ return "";
+ }
+
+ public void unserialize(String serialized) {
+ }
+
+ };
+ }
+
+ public void testGetInfo() {
+ assertEquals("Mega Feeder: 127.0.0.1 - 127.0.0.2", feederGUI.getInfo());
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/feeders/FeederGUIRegistry.java b/ipscan/src/net/azib/ipscan/gui/feeders/FeederGUIRegistry.java
new file mode 100755
index 00000000..4f263c81
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/feeders/FeederGUIRegistry.java
@@ -0,0 +1,73 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.feeders;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Combo;
+
+import net.azib.ipscan.config.Config;
+import net.azib.ipscan.feeders.FeederException;
+
+/**
+ * FeederGUIRegistry
+ *
+ * @author anton
+ */
+public class FeederGUIRegistry {
+
+ private List feederGUIList;
+ private Combo feederSelectionCombo;
+
+ private AbstractFeederGUI currentFeederGUI;
+
+ public FeederGUIRegistry(AbstractFeederGUI[] allTheFeeders, Combo feederSelectionCombo) {
+ this.feederGUIList = Arrays.asList(allTheFeeders);
+ this.feederSelectionCombo = feederSelectionCombo;
+ this.currentFeederGUI = allTheFeeders[0];
+ }
+
+ public AbstractFeederGUI current() {
+ return currentFeederGUI;
+ }
+
+ /**
+ * Select a new indexed feeder GUI
+ */
+ public void select(int newActiveFeeder) {
+ // hide current feeder
+ currentFeederGUI.setVisible(false);
+
+ // get new feeder
+ currentFeederGUI = (AbstractFeederGUI) feederGUIList.get(newActiveFeeder);
+ Config.getGlobal().activeFeeder = newActiveFeeder;
+
+ // make new feeder visible
+ currentFeederGUI.setVisible(true);
+ }
+
+ public Iterator iterator() {
+ return feederGUIList.iterator();
+ }
+
+ /**
+ * Select the Feeder GUI by its name, while updating the GUI
+ */
+ public void select(String feederName) {
+ String[] items = feederSelectionCombo.getItems();
+ for (int i = 0; i < items.length; i++) {
+ if (items[i].equals(feederName)) {
+ // select the feeder if found
+ feederSelectionCombo.select(i);
+ feederSelectionCombo.notifyListeners(SWT.Selection, null);
+ return;
+ }
+ }
+ // if not found
+ throw new FeederException("No such feeder found: " + feederName);
+ }
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/feeders/FileFeederGUI.java b/ipscan/src/net/azib/ipscan/gui/feeders/FileFeederGUI.java
new file mode 100755
index 00000000..1556bcaf
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/feeders/FileFeederGUI.java
@@ -0,0 +1,102 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.feeders;
+
+import net.azib.ipscan.feeders.Feeder;
+import net.azib.ipscan.feeders.FileFeeder;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * GUI for initialization of FileFeeder.
+ *
+ * @author anton
+ */
+public class FileFeederGUI extends AbstractFeederGUI {
+
+ private Label fileNameLabel;
+ private Text fileNameText;
+
+ private Button browseButton;
+
+ public FileFeederGUI(Composite parent) {
+ super(parent);
+ }
+
+ protected void initialize() {
+ feeder = new FileFeeder();
+
+ FormLayout formLayout = new FormLayout();
+ formLayout.marginWidth = 3;
+ formLayout.marginHeight = 3;
+ formLayout.spacing = 3;
+ setLayout(formLayout);
+
+ fileNameLabel = new Label(this, SWT.NONE);
+ fileNameText = new Text(this, SWT.BORDER);
+ browseButton = new Button(this, SWT.NONE);
+
+ fileNameLabel.setText(getStringLabel("name") + ":");
+ FormData formData = new FormData();
+ formData.left = new FormAttachment(0);
+ formData.top = new FormAttachment(fileNameText, 0, SWT.CENTER);
+ formData.bottom = new FormAttachment(browseButton, 0, SWT.BOTTOM);
+ fileNameLabel.setLayoutData(formData);
+
+ formData = new FormData(140, SWT.DEFAULT);
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(fileNameLabel);
+ fileNameText.setLayoutData(formData);
+
+ browseButton.setText(getStringLabel("browse"));
+ formData = new FormData();
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(fileNameText);
+ formData.height = 22;
+ browseButton.setLayoutData(formData);
+ browseButton.addSelectionListener(new SelectionListener() {
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ widgetSelected(e);
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ FileDialog dialog = new FileDialog(getShell());
+ dialog.setText(getStringLabel("browse"));
+ String fileName = dialog.open();
+ if (fileName != null) {
+ fileNameText.setText(fileName);
+ fileNameText.setSelection(fileName.length());
+ }
+ }
+
+ });
+
+ pack();
+ }
+
+ public Feeder getFeeder() {
+ ((FileFeeder)feeder).initialize(fileNameText.getText());
+ return feeder;
+ }
+
+ public String serialize() {
+ return fileNameText.getText();
+ }
+
+ public void unserialize(String serialized) {
+ fileNameText.setText(serialized);
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/feeders/RandomFeederGUI.java b/ipscan/src/net/azib/ipscan/gui/feeders/RandomFeederGUI.java
new file mode 100755
index 00000000..7020b32f
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/feeders/RandomFeederGUI.java
@@ -0,0 +1,174 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.feeders;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.InetAddressUtils;
+import net.azib.ipscan.feeders.Feeder;
+import net.azib.ipscan.feeders.RandomFeeder;
+import net.azib.ipscan.gui.actions.FeederActions;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Spinner;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * GUI for initialization of RandomFeeder
+ *
+ * @author anton
+ */
+public class RandomFeederGUI extends AbstractFeederGUI {
+
+ private Label ipPrototypeLabel;
+ private Text ipPrototypeText;
+
+ private Label ipMaskLabel;
+ private Combo ipMaskCombo;
+
+ private Label hostnameLabel;
+ private Text hostnameText;
+
+ private Button ipUpButton;
+
+ private Label countLabel;
+ private Spinner countSpinner;
+
+
+ public RandomFeederGUI(Composite parent) {
+ super(parent);
+ }
+
+ protected void initialize() {
+ feeder = new RandomFeeder();
+
+ FormLayout formLayout = new FormLayout();
+ formLayout.marginWidth = 3;
+ formLayout.marginHeight = 3;
+ formLayout.spacing = 4;
+ setLayout(formLayout);
+
+ ipPrototypeLabel = new Label(this, SWT.NONE);
+ ipPrototypeText = new Text(this, SWT.BORDER);
+ ipMaskLabel = new Label(this, SWT.NONE);
+ ipMaskCombo = new Combo(this, SWT.NONE);
+ hostnameText = new Text(this, SWT.BORDER);
+ hostnameLabel = new Label(this, SWT.NONE);
+ ipUpButton = new Button(this, SWT.NONE);
+ countLabel = new Label(this, SWT.NONE);
+ countSpinner = new Spinner(this, SWT.BORDER);
+
+ ipPrototypeLabel.setText(getStringLabel("prototype"));
+ FormData formData = new FormData();
+ formData.right = new FormAttachment(hostnameLabel, 0, SWT.RIGHT);
+ formData.top = new FormAttachment(ipPrototypeText, 0, SWT.CENTER);
+ ipPrototypeLabel.setLayoutData(formData);
+
+ formData = new FormData(105, SWT.DEFAULT);
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(ipPrototypeLabel);
+ ipPrototypeText.setLayoutData(formData);
+
+ ipMaskLabel.setText(getStringLabel("mask"));
+ formData = new FormData();
+ formData.left = new FormAttachment(ipPrototypeText, 3);
+ formData.top = new FormAttachment(ipPrototypeText, 0, SWT.CENTER);
+ ipMaskLabel.setLayoutData(formData);
+
+ ipMaskCombo.setVisibleItemCount(10);
+ // Warning: IPv4 specific netmasks
+ ipMaskCombo.add("255...128");
+ ipMaskCombo.add("255...0");
+ ipMaskCombo.add("255..0.0");
+ ipMaskCombo.add("255.0.0.0");
+ ipMaskCombo.add("0.0.0.0");
+ ipMaskCombo.add("255..0.255");
+ ipMaskCombo.add("255.0.0.255");
+ ipMaskCombo.select(3);
+ formData = new FormData(105, SWT.DEFAULT);
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(ipMaskLabel);
+ formData.bottom = new FormAttachment(ipPrototypeText, 0, SWT.BOTTOM);
+ ipMaskCombo.setLayoutData(formData);
+
+ FeederActions.HostnameButton hostnameSelectionListener = new FeederActions.HostnameButton(hostnameText, ipPrototypeText);
+ try {
+ hostnameText.setText(InetAddress.getLocalHost().getHostName());
+ }
+ catch (UnknownHostException e1) {
+ // leave hostnameText empty
+ }
+ hostnameText.addTraverseListener(hostnameSelectionListener);
+ formData = new FormData(105, SWT.DEFAULT);
+ formData.top = new FormAttachment(ipPrototypeText);
+ formData.left = new FormAttachment(ipPrototypeText, 0, SWT.LEFT);
+ hostnameText.setLayoutData(formData);
+
+ hostnameLabel.setText(getStringLabel("hostname"));
+ formData = new FormData();
+ formData.left = new FormAttachment(0);
+ formData.top = new FormAttachment(hostnameText, 0, SWT.CENTER);
+ hostnameLabel.setLayoutData(formData);
+
+ ipUpButton.setImage(new Image(getDisplay(), Labels.getInstance().getImageAsStream("button.ipUp.img")));
+ ipUpButton.addSelectionListener(hostnameSelectionListener);
+ formData = new FormData(35, SWT.DEFAULT);
+ formData.top = new FormAttachment(ipPrototypeText);
+ formData.left = new FormAttachment(hostnameText);
+ formData.bottom = new FormAttachment(hostnameText, 0, SWT.BOTTOM);
+ ipUpButton.setLayoutData(formData);
+
+ countLabel.setText(getStringLabel("count"));
+ formData = new FormData();
+ formData.left = new FormAttachment(ipUpButton, 3);
+ formData.top = new FormAttachment(hostnameLabel, 0, SWT.TOP);
+ countLabel.setLayoutData(formData);
+
+ countSpinner.setSelection(100);
+ countSpinner.setMaximum(100000);
+ countSpinner.setMinimum(1);
+ formData = new FormData();
+ formData.left = new FormAttachment(countLabel);
+ formData.top = new FormAttachment(ipUpButton, 0, SWT.CENTER);
+ formData.right = new FormAttachment(ipMaskCombo, 0, SWT.RIGHT);
+ countSpinner.setLayoutData(formData);
+
+ // fill the IP text with local IP address
+ try {
+ ipPrototypeText.setText(InetAddressUtils.getAddressByName(hostnameText.getText()));
+ }
+ catch (UnknownHostException e) {
+ // don't report any errors on initialization
+ }
+
+ pack();
+ }
+
+ public Feeder getFeeder() {
+ ((RandomFeeder)feeder).initialize(ipPrototypeText.getText(), ipMaskCombo.getText(), countSpinner.getSelection());
+ return feeder;
+ }
+
+ public String serialize() {
+ return ipPrototypeText.getText() + ":::" + ipMaskCombo.getText() + ":::" + countSpinner.getSelection();
+ }
+
+ public void unserialize(String serialized) {
+ String[] parts = serialized.split(":::");
+ ipPrototypeText.setText(parts[0]);
+ ipMaskCombo.setText(parts[1]);
+ countSpinner.setSelection(Integer.parseInt(parts[2]));
+ }
+
+}
diff --git a/ipscan/src/net/azib/ipscan/gui/feeders/RangeFeederGUI.java b/ipscan/src/net/azib/ipscan/gui/feeders/RangeFeederGUI.java
new file mode 100755
index 00000000..6cf15a44
--- /dev/null
+++ b/ipscan/src/net/azib/ipscan/gui/feeders/RangeFeederGUI.java
@@ -0,0 +1,249 @@
+/**
+ *
+ */
+package net.azib.ipscan.gui.feeders;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.logging.Logger;
+
+import net.azib.ipscan.config.Labels;
+import net.azib.ipscan.core.InetAddressUtils;
+import net.azib.ipscan.feeders.Feeder;
+import net.azib.ipscan.feeders.FeederException;
+import net.azib.ipscan.feeders.RangeFeeder;
+import net.azib.ipscan.gui.actions.FeederActions;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * GUI for initialization of RangeFeeder.
+ *
+ * TODO: delete button doesn't work well in edit fields
+ *
+ * @author anton
+ */
+public class RangeFeederGUI extends AbstractFeederGUI {
+
+ private static final Logger LOG = Logger.getLogger(RangeFeederGUI.class.getName());
+
+ private Label ipRangeLabel;
+ private Text startIPText;
+
+ private Label toLabel;
+ private Text endIPText;
+ private boolean isEndIPUnedited = true;
+
+ private Label hostnameLabel;
+ private Text hostnameText;
+
+ private Button ipUpButton;
+
+ private Combo netmaskCombo;
+
+ public RangeFeederGUI(Composite parent) {
+ super(parent);
+ }
+
+ protected void initialize() {
+ feeder = new RangeFeeder();
+
+ FormLayout formLayout = new FormLayout();
+ formLayout.marginWidth = 3;
+ formLayout.marginHeight = 3;
+ formLayout.marginBottom = 2;
+ formLayout.spacing = 4;
+ setLayout(formLayout);
+
+ ipRangeLabel = new Label(this, SWT.NONE);
+ startIPText = new Text(this, SWT.BORDER);
+ toLabel = new Label(this, SWT.NONE);
+ endIPText = new Text(this, SWT.BORDER);
+ hostnameLabel = new Label(this, SWT.NONE);
+ hostnameText = new Text(this, SWT.BORDER);
+ ipUpButton = new Button(this, SWT.NONE);
+ netmaskCombo = new Combo(this, SWT.NONE);
+
+ ipRangeLabel.setText(getStringLabel("startIP"));
+ FormData formData = new FormData();
+ formData.right = new FormAttachment(hostnameLabel, 0, SWT.RIGHT);
+ formData.top = new FormAttachment(startIPText, 0, SWT.CENTER);
+ ipRangeLabel.setLayoutData(formData);
+
+ formData = new FormData(105, SWT.DEFAULT);
+ formData.top = new FormAttachment(0);
+ formData.left = new FormAttachment(ipRangeLabel);
+ startIPText.setLayoutData(formData);
+ startIPText.addModifyListener(new StartIPModifyListener());
+
+ toLabel.setText(getStringLabel("endIP"));
+ formData = new FormData();
+ formData.left = new FormAttachment(startIPText);
+ formData.top = new FormAttachment(startIPText, 0, SWT.CENTER);
+ toLabel.setLayoutData(formData);
+
+ try {
+ endIPText.setText(InetAddress.getLocalHost().getHostAddress());
+ }
+ catch (UnknownHostException e) {
+ // leave endIPText empty
+ }
+ formData = new FormData(105, SWT.DEFAULT);
+ formData.left = new FormAttachment(toLabel);
+ endIPText.setLayoutData(formData);
+ endIPText.addKeyListener(new EndIPKeyListener());
+
+ FeederActions.HostnameButton hostnameListener = new FeederActions.HostnameButton(hostnameText, startIPText) {
+ public void widgetSelected(SelectionEvent event) {
+ // raise the flag
+ isEndIPUnedited = true;
+ // reset the netmask combo
+ netmaskCombo.setText(getStringLabel("netmask"));
+ // now do the stuff
+ super.widgetSelected(event);
+ }
+ };
+ try {
+ hostnameText.setText(InetAddress.getLocalHost().getHostName());
+ }
+ catch (UnknownHostException e1) {
+ // leave hostnameText empty
+ }
+ hostnameText.addTraverseListener(hostnameListener);
+ formData = new FormData(105, SWT.DEFAULT);
+ formData.top = new FormAttachment(startIPText);
+ formData.left = new FormAttachment(startIPText, 0, SWT.LEFT);
+ hostnameText.setLayoutData(formData);
+ hostnameText.setToolTipText(Labels.getLabel("feeder.range.hostname.tooltip"));
+
+ hostnameLabel.setText(getStringLabel("hostname"));
+ formData = new FormData();
+ formData.left = new FormAttachment(0);
+ formData.top = new FormAttachment(hostnameText, 0, SWT.CENTER);
+ hostnameLabel.setLayoutData(formData);
+
+ ipUpButton.setImage(new Image(getDisplay(), Labels.getInstance().getImageAsStream("button.ipUp.img")));
+ ipUpButton.addSelectionListener(hostnameListener);
+ formData = new FormData(35, SWT.DEFAULT);
+ formData.top = new FormAttachment(endIPText);
+ formData.left = new FormAttachment(hostnameText);
+ formData.bottom = new FormAttachment(hostnameText, 0, SWT.BOTTOM);
+ ipUpButton.setLayoutData(formData);
+
+ netmaskCombo.setText(getStringLabel("netmask"));
+ netmaskCombo.setVisibleItemCount(10);
+ // Warning: IPv4 specific netmasks
+ netmaskCombo.add("/16"); // TODO: implement these
+ netmaskCombo.add("/24");
+ netmaskCombo.add("/28");
+ netmaskCombo.add("255...240");
+ netmaskCombo.add("255...224");
+ netmaskCombo.add("255...192");
+ netmaskCombo.add("255...128");
+ netmaskCombo.add("255...0");
+ netmaskCombo.add("255..0.0");
+ netmaskCombo.add("255.0.0.0");
+ NetmaskSelectionListener netmaskSelectionListener = new NetmaskSelectionListener();
+ netmaskCombo.addSelectionListener(netmaskSelectionListener);
+ netmaskCombo.addTraverseListener(netmaskSelectionListener);
+ formData = new FormData();
+ formData.top = new FormAttachment(startIPText);
+ formData.left = new FormAttachment(ipUpButton, 5);
+ formData.right = new FormAttachment(endIPText, 0, SWT.RIGHT);
+ formData.bottom = new FormAttachment(hostnameText, 0, SWT.BOTTOM);
+ netmaskCombo.setLayoutData(formData);
+ netmaskCombo.setToolTipText(Labels.getLabel("feeder.range.netmask.tooltip"));
+
+ // fill the IP text with local IP address
+ try {
+ startIPText.setText(InetAddressUtils.getAddressByName(hostnameText.getText()));
+ endIPText.setText(startIPText.getText());
+ }
+ catch (UnknownHostException e) {
+ // don't report any errors on initialization
+ LOG.fine(e.toString());
+ }
+
+ pack();
+ }
+
+ public Feeder getFeeder() {
+ ((RangeFeeder)feeder).initialize(startIPText.getText(), endIPText.getText());
+ return feeder;
+ }
+
+ public String serialize() {
+ return startIPText.getText() + ":::" + endIPText.getText();
+ }
+
+ public void unserialize(String serialized) {
+ String[] parts = serialized.split(":::");
+ startIPText.setText(parts[0]);
+ endIPText.setText(parts[1]);
+ // reset the netmask combo
+ netmaskCombo.setText(getStringLabel("netmask"));
+ }
+
+ private final class EndIPKeyListener implements KeyListener {
+ public void keyPressed(KeyEvent e) {
+ isEndIPUnedited = false;
+ }
+
+ public void keyReleased(KeyEvent e) {
+ }
+ }
+
+ private final class StartIPModifyListener implements ModifyListener {
+ public void modifyText(ModifyEvent e) {
+ if (isEndIPUnedited) {
+ endIPText.setText(startIPText.getText());
+ }
+ }
+ }
+
+ private final class NetmaskSelectionListener implements SelectionListener, TraverseListener {
+ public void widgetDefaultSelected(SelectionEvent event) {
+ widgetSelected(event);
+ }
+
+ public void widgetSelected(SelectionEvent event) {
+ try {
+ String netmaskString = netmaskCombo.getText();
+ InetAddress netmask = InetAddressUtils.parseNetmask(netmaskString);
+ InetAddress startIP = InetAddress.getByName(startIPText.getText());
+
+ startIPText.setText(InetAddressUtils.startRangeByNetmask(startIP, netmask).getHostAddress());
+ endIPText.setText(InetAddressUtils.endRangeByNetmask(startIP, netmask).getHostAddress());
+ isEndIPUnedited = false;
+ }
+ catch (UnknownHostException e) {
+ throw new FeederException("invalidNetmask");
+ }
+ }
+
+ public void keyTraversed(TraverseEvent e) {
+ if (e.detail == SWT.TRAVERSE_RETURN) {
+ widgetSelected(null);
+ e.doit = false;
+ }
+ }
+ }
+
+} // @jve:decl-index=0:visual-constraint="10,10"
diff --git a/ipscan/start.sh b/ipscan/start.sh
new file mode 100755
index 00000000..821e962b
--- /dev/null
+++ b/ipscan/start.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+su -c 'java -Djava.library.path=../swt/lib:ext/rocksaw/lib -jar dist/ipscan.jar'