Hi,
I constructed an applet according to your specifications.
This applet opens a socket on a given port, then contacts a server
script which will test the connection on that port. If the server
returns a response containing the string "SUCCESS" (the response can
be plain text, xml or soap as long as it contains this string in case
of success), the applet will redirect the user to the success page,
else the user will be redirected to the failure page. If the applet
fails to contact the server script, the user will be redirected to the
error page.
The applet will pass the client ip address to the script as a
parameter "ip", however this will only work if the applet is signed
(which you're free to do). Even if the applet is signed, however, it
is still possible an internal network address is returned in stead of
the internet ip. Ideally, the script will determine the actual client
ip from the request.
I included a sample servlet script, implemented as a java servlet (and
an alternative in the form of a jsp page), which tests the socket
connection and retrieves the actual client ip from the request. The
client ip will usually be correct, except if a non-transparant proxy
is used by the client. Notice that the server where the script is
hosted needs to allow direct (non-http) tcp connections, for the
script to be able to contact the client.
You can retrieve the applet archive, as well as the source code to the
applet and scripts here:
http://users.pandora.be/rami/tcp/
The applet can be used in debug mode, in which case it will not
redirect, but simply display the parameters, result and error messages
(if any). Debug mode
can be used with the "debug" parameter:
<APPLET CODE="TCPApplet" ARCHIVE="TCPApplet.jar" WIDTH=600
HEIGHT=200>
<param name="listening_port" value="1234">
<param name="detection_script_url" value="script.jsp">
<param name="success_url" value="success.html">
<param name="failure_url" value="failure.html">
<param name="error_url" value="error.html">
<param name="socket_timeout" value="5000">
<param name="debug" value="true">
</APPLET>
To enable the redirect mode, use the applet as follows:
<APPLET CODE="TCPApplet" ARCHIVE="TCPApplet.jar" WIDTH=0 HEIGHT=0>
<param name="listening_port" value="1234">
<param name="detection_script_url" value="script.jsp">
<param name="success_url" value="success.html">
<param name="failure_url" value="failure.html">
<param name="error_url" value="error.html">
<param name="socket_timeout" value="5000">
</APPLET>
The "socket_timeout" parameter is optional (defaults to 10000) and
defines the time the socket remains open (in ms).
If the applet is not signed, the script url should be hosted on the
same server the applet was downloaded from! Use relative path names in
this case (the applet will automaticall append the code base to the
url, except if the url starts with "http://").
Below you'll find a printout of the applet source:
//--------------------- START APPLET --------------------- //
import java.applet.*;
import java.io.*;
import java.net.*;
import java.awt.*;
/**
* This applet opens a socket on a given port, then contacts a server
script
* which will test the connection on that port. If the server returns
a response
* containing the string "SUCCESS", the applet will redirect the user
to the success
* page, else the user will be redirected to the failure page. If the
applet fails
* to contact the server script, the user will be redirected to the
error page.
* The applet will pass the client ip address to the script as a
parameter "ip",
* however this will only work if the applet is signed and may be a
network
* address. Ideally, the script will determine the actual client ip
from the request.
*/
public class TCPApplet
extends Applet {
// applet parameters names
private static final String PARAM_PORT = "listening_port";
private static final String PARAM_SCRIPT = "detection_script_url";
private static final String PARAM_SUCCESS = "success_url";
private static final String PARAM_FAILURE = "failure_url";
private static final String PARAM_ERROR = "error_url";
private static final String PARAM_TIMEOUT = "socket_timeout";
private static final String PARAM_DEBUG = "debug";
// the expected success string to be contained in the server script
response
private static final String RESPONSE_SUCCESS = "SUCCESS";
// applet parameters
private String listenPort;
private String scriptUrl;
private String successUrl;
private String failureUrl;
private String errorUrl;
private boolean debug;
private int socketTimeOut;
/**
* This thread opens a connection for a time period defined by
* "socket_timeout" and excepts connections from a server script.
*/
private class PortListnerThread
implements Runnable {
public void run() {
ServerSocket socket = null;
Socket connectionSocket = null;
try {
// open a socket on the defined port
socket = new ServerSocket(Integer.parseInt(listenPort));
socket.setSoTimeout(socketTimeOut);
connectionSocket = socket.accept();
BufferedReader in =
new BufferedReader(new
InputStreamReader(connectionSocket.getInputStream()));
// read some input from socket, to allow server script to test
connection
in.readLine();
}
catch (Exception exc) {
System.out.println(exc.getMessage());
}
finally {
if (connectionSocket != null) {
try {
connectionSocket.close();
}
catch (Exception exc) {}
}
if (socket != null) {
try {
socket.close();
}
catch (Exception exc) {}
}
}
}
}
/**
* Initializes the applet and retrieves the applet parameters
*/
public void init() {
listenPort = getParameter(PARAM_PORT);
scriptUrl = getParameter(PARAM_SCRIPT);
successUrl = getParameter(PARAM_SUCCESS);
failureUrl = getParameter(PARAM_FAILURE);
errorUrl = getParameter(PARAM_ERROR);
debug = "true".equalsIgnoreCase(getParameter(PARAM_DEBUG));
try {
socketTimeOut = Integer.parseInt(getParameter(PARAM_TIMEOUT));
} catch (Exception exc) {
// default socket timeout: 10 seconds
socketTimeOut = 10000;
}
// lookup local ip address and add it to the script url
String ipAddress = null;
try {
// this will only work if the applet is signed! Else
127.0.0.1 will be returned
// furthermore, on networks, the internal network address
will be returned
InetAddress ia = InetAddress.getLocalHost();
ipAddress = ia.getHostAddress();
} catch (Exception exc) {
// unable to determine local ip address
ipAddress = "127.0.0.1";
}
scriptUrl += "?ip="+ipAddress;
}
/**
* Writes debug info in debug mode or redirects to the correct url
otherwise
*/
public void paint(Graphics g) {
if (debug) {
// Show debug info
g.drawString("Port is: " + listenPort, 20, 20);
g.drawString("Script url is: " + scriptUrl, 20, 40);
g.drawString("Success url is: " + successUrl, 20, 60);
g.drawString("Failure url is: " + failureUrl, 20, 80);
g.drawString("Error url is: " + errorUrl, 20, 100);
g.drawString("Socket timeout is: " + socketTimeOut + "ms", 20,
120);
try {
g.drawString("Result is: " + checkPort(), 20, 140);
}
catch (Exception exc) {
g.drawString("Exception: " + exc.getMessage(), 20, 140);
}
} else {
// perform redirect
try {
String result = checkPort();
URL redirectURL;
if (result.startsWith("http://"))
redirectURL = new URL(scriptUrl);
else
redirectURL = new URL(getCodeBase(),result);
getAppletContext().showDocument(redirectURL);
}
catch (MalformedURLException mue) {
mue.printStackTrace();
}
}
}
/**
* Starts a thread listner on the defined port and queries the
server script.
* If the script response contains "SUCCESS", the success url is
returned,
* otherwise the failure url is returned. If the server cannot be
contacted,
* the error url is returned.
*/
private String checkPort() {
// open a socket listner thread for the server to connect to
Thread listner = new Thread(new PortListnerThread());
listner.start();
try {
// contact the server script
StringBuffer scriptResponse = new StringBuffer();
URL url;
if (scriptUrl.startsWith("http://"))
url = new URL(scriptUrl);
else
url = new URL(getCodeBase(),scriptUrl);
URLConnection conn = url.openConnection();
conn.setRequestProperty("Accept", "text/*");
// read script response
BufferedReader reader =
new BufferedReader(new
InputStreamReader(conn.getInputStream()));
String line = null;
while ( (line = reader.readLine()) != null) {
scriptResponse.append(line);
}
reader.close();
if (scriptResponse.toString().toUpperCase().indexOf(RESPONSE_SUCCESS)>0)
return successUrl;
else return failureUrl;
} catch (Exception exc) {
exc.printStackTrace();
return errorUrl;
}
}
}
//--------------------- END APPLET --------------------- //
A sample server script JSP might look something like this:
<%
// Sample server script
try{
// open a socket to client
java.net.Socket clientSocket = new
java.net.Socket(request.getRemoteAddr(), 1234);
java.io.DataOutputStream outToServer =
new java.io.DataOutputStream(clientSocket.getOutputStream());
// write anything to the socket to test the connection
outToServer.writeBytes("socket test");
clientSocket.close();
%>SOCKET CONNECT SUCCESS<%
}
catch (Exception exc) {
%>SOCKET CONNECT FAILED<%
}
%>
A Java servlet version of the same script would be:
//--------------------- START SERVLET --------------------- //
import java.io.*;
import java.util.*;
import java.net.*;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* Sample servlet to test the applet functionality
*/
public class TCPServlet
extends HttpServlet {
private static final String PARAM_IP = "ip";
private static final int PORT = 1234;
/**
* Handle applet requests
*/
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws
IOException, ServletException {
try {
// the ip returned by the applet (might be network address)
String ip = req.getParameter(PARAM_IP);
// the actual client request ip
String clientIp = req.getRemoteAddr();
// open a socket
Socket clientSocket = new Socket(clientIp, PORT);
DataOutputStream outToServer =
new DataOutputStream(clientSocket.getOutputStream());
// write anything to the socket to test the connection
outToServer.writeBytes("socket test");
clientSocket.close();
// return a response containing the "SUCCESS" string to the
applet
res.getOutputStream().print("SOCKET CONNECT SUCCESS");
}
catch (Exception exc) {
res.getOutputStream().print("SOCKET CONNECT FAILED");
}
res.getOutputStream().close();
}
}
//--------------------- END SERVLET --------------------- //
The applet has been tested using the Java Virtual Machine plugin. I
haven't had the chance of testing it on the Microsoft Virtual Machine
yet. I'll do that as soon as possible and post an updated version if
problems would occur.
I hope this allows you to complete your project. If you have any
questions or problems feel free to ask for clarification!
Kind regards,
rhansenne-ga. |
Request for Answer Clarification by
bennetthaselton-ga
on
11 Aug 2003 16:18 PDT
OK, I went to
http://forum.java.sun.com/thread.jsp?thread=174214&forum=63&message=1883852
and followed the instructions to get the TCPApplet.jar file
self-signed. Now, when you open the signed TCPApplet.jar file in
WinZip, it contains two new files, Tstkey.rsa and Tstkey.sf. So I
uploaded it and loaded it from this page using Netscape 7:
http://www.peacefire.org/holder/java-port-listener-2/loader.html
(To use your detection script, I had to set "detection_script_url" to
theh absolute URL for your script,
"http://www.mycgiserver.com/~rhansenne/tcp/script.jsp". This worked
fine, even though they're on different servers.)
When I do that, Netscape gives me a prompt with the info:
>>>
Do you want to install and run signed applet distributed by "Bennett
Haselton"?
Publisher authenticity verified by: "Peacefire"
- The security certificate was issued by a company that is not
trusted.
- The security certificate has not expired and is still valid.
[etc.]
>>>
which is what you'd expect for a self-signed applet. I pick the
option to say, Yes, install and run the applet. When I do that, the
applet outputs "Result is: success.html", as expected.
However, I then shut down the browser and turned on the Windows XP
firewall, to block incoming connections, then went back to
http://www.peacefire.org/holder/java-port-listener-2/loader.html
using Netscape 7, it correctly displays my IP address, but this time,
no Result: line is outputted. In fact the applet hangs, so that if I
drag some other window over the applet region, an image of that
window's contents will remain in that region. If I look in the
Netscape 7 java console, however, it displays the line, "Accept timed
out".
Presumably this is a bug -- if the socket connection times out, then
the debug area of the applet should display "Result is: failure.html".
Same results if I load the JAR file from a page without the debug
parameter:
http://www.peacefire.org/holder/java-port-listener-2/loader-non-debug.html
If I load the page in Netscape 7 with the firewall turned off, then I
get redirected to "success.html" (which is currently 404 Not Found,
I'll put it there later) as expected. But if I load it with the
firewall turned on, then "Accept timed out" gets written to the Java
console, and the applet hangs.
The other problem is that I still can't get IE to recognize it as a
signed applet. Going to
http://www.peacefire.org/holder/java-port-listener-2/loader.html
still displays "ip=127.0.0.1" at the end of the "Script url" debug
line. I already signed the JAR file using the steps at
http://forum.java.sun.com/thread.jsp?thread=174214&forum=63&message=1883852
and I even opened the tstcert.crt file (the one used to sign
TCPApplet.jar) and "installed" it into IE, so that now when I
double-click the local tstcert.crt file, it says it's trusted. How do
I get IE to recognize the applet as signed, so it will allow it to
perform the requested operations?
|