I’ve just spent the past day trying to get my flash app talking to another device on my network via socket 23. I found some sample telnet code (which operates on port 23) and allowed me to “talk” to the RFID reader. It worked fine as a new project in Flex Builder and being served from a local file. The moment I served the application from a web server (tomcat) on my laptop, I get crossdomain issues. Flash won’t open a socket that is different from the one that served your application unless that socket authorizes it. I will spare you the details that took many hours of my day. If you’re trying to talk to another web server on a different port, no problem.. just put the crossdomain.xml file on that server that authorizes the connection. In this case, I was trying to connect to another host and another port (which runs telnet, not http). The RFID reader can’t be modified to serve up a crossdomain.xml file, so I had to get creative.
My solution was to run a TCP proxy on my web server machine that proxied requests to the RFID reader. I made it listen on port 8023 and forward requests to 23 on the RFID reader. This was the start because I still got errors about that localhost:8023 not being authorized. It turns out that when you try the connection, flash connects to the socket and sends 23 bytes which contain “<policy-file-request/>”. Flash expects whatever is running at that port to respond with the policy string (that would have been in the crossdomain.xml file). So, I modified this little proxy class I got off the internet to recognize the proxy request and respond with a proxy string (null terminated.. that is very important!). Once I had this set up right, I was able to communicate from my flash app to my RFID reader. Not the most elegant solution, but this is something temporary for a demo.
To run the code below, compile with javac and invoke “java -classpath <class.file.location> ProxyThread 8023 192.168.1.39 23”. Those options are what I used to talk to my RFID reader, but you’ll likely use different values.
import java.net.*;
import java.io.*;
/*
Java Transparent Proxy
Copyright (C) 1999 by Didier Frick (http://www.dfr.ch/)
This software is provided under the GNU general public license (http://www.gnu.org/copyleft/gpl.html).
*/
public class ProxyThread extends Thread {
protected class StreamCopyThread extends Thread {
private Socket inSock;
private Socket outSock;
private boolean done=false;
private StreamCopyThread peer;
private boolean inFromLocal; // in from local port
private OutputStream out;
private String policy = "<cross-domain-policy>\n<allow-access-from domain=\"*\" to-ports=\"8023\"/>\n</cross-domain-policy>";
public StreamCopyThread(Socket inSock, Socket outSock, boolean in) {
this.inSock=inSock;
this.outSock=outSock;
this.inFromLocal = in;
}
public void sendPolicy() {
try {
out.write(policy.getBytes());
System.err.println("Sent policy");
} catch (IOException ex) {
System.err.println("Error sending policy file");
}
}
public void run() {
byte[] buf=new byte[bufSize];
int count=-1;
try {
InputStream in=inSock.getInputStream();
out=outSock.getOutputStream();
try {
while(((count=in.read(buf))>0)&&!isInterrupted()) {
if (inFromLocal && count==23 && new String(buf).startsWith("<policy-file-request/>")) {
// send policy file back.. don't forward this to other port
System.err.println("Got policy request");
peer.sendPolicy();
}
else {
out.write(buf,0,count);
//System.err.println(count+" bytes "+(inFromLocal?"sent":"received"));
}
}
} catch(Exception xc) {
if(debug) {
// FIXME
// It's very difficult to sort out between "normal"
// exceptions (occuring when one end closes the connection
// normally), and "exceptional" exceptions (when something
// really goes wrong)
// Therefore we only log exceptions occuring here if the debug flag
// is true, in order to avoid cluttering up the log.
err.println(header+":"+xc);
xc.printStackTrace();
}
} finally {
// The input and output streams will be closed when the sockets themselves
// are closed.
out.flush();
}
} catch(Exception xc) {
err.println(header+":"+xc);
xc.printStackTrace();
}
synchronized(lock) {
done=true;
try {
if((peer==null)||peer.isDone()) {
// Cleanup if there is only one peer OR
// if _both_ peers are done
inSock.close();
outSock.close();
}
else
// Signal the peer (if any) that we're done on this side of the connection
peer.interrupt();
} catch(Exception xc) {
err.println(header+":"+xc);
xc.printStackTrace();
} finally {
connections.removeElement(this);
}
}
}
public boolean isDone() {
return done;
}
public void setPeer(StreamCopyThread peer) {
this.peer=peer;
}
}
// Holds all the currently active StreamCopyThreads
private java.util.Vector connections=new java.util.Vector();
// Used to synchronize the connection-handling threads with this thread
private Object lock=new Object();
// The address to forward connections to
private InetAddress dstAddr;
// The port to forward connections to
private int dstPort;
// Backlog parameter used when creating the ServerSocket
protected static final int backLog=100;
// Timeout waiting for a StreamCopyThread to finish
public static final int threadTimeout=2000; //ms
// Linger time
public static final int lingerTime=180; //seconds (?)
// Size of receive buffer
public static final int bufSize=2048;
// Header to prepend to log messages
private String header;
// This proxy's server socket
private ServerSocket srvSock;
// Debug flag
private boolean debug=false;
// Log streams for output and error messages
private PrintStream out;
private PrintStream err;
private static final String
argsMessage="Arguments: ( [source_address] source_port dest_address dest_port ) | config_file";
private static final String
propertyPrefix="proxy";
public ProxyThread(InetAddress srcAddr,int srcPort,
InetAddress dstAddr,int dstPort, PrintStream out, PrintStream err)
throws IOException {
this.out=out;
this.err=err;
this.srvSock=(srcAddr==null) ? new ServerSocket(srcPort,backLog) :
new ServerSocket(srcPort,backLog,srcAddr);
this.dstAddr=dstAddr;
this.dstPort=dstPort;
this.header=(srcAddr==null ? "" : srcAddr.toString())+":"+srcPort+" <-> "+dstAddr+":"+dstPort;
start();
}
public void run() {
out.println(header+" : starting");
try {
while(!isInterrupted()) {
Socket serverSocket=srvSock.accept();
try {
serverSocket.setSoLinger(true,lingerTime);
Socket clientSocket=new Socket(dstAddr,dstPort);
clientSocket.setSoLinger(true,lingerTime);
StreamCopyThread sToC=new StreamCopyThread(serverSocket,clientSocket, true);
StreamCopyThread cToS=new StreamCopyThread(clientSocket,serverSocket, false);
sToC.setPeer(cToS);
cToS.setPeer(sToC);
synchronized(lock) {
connections.addElement(cToS);
connections.addElement(sToC);
sToC.start();
cToS.start();
}
} catch(Exception xc) {
err.println(header+":"+xc);
if(debug)
xc.printStackTrace();
}
}
srvSock.close();
} catch(IOException xc) {
err.println(header+":"+xc);
if(debug)
xc.printStackTrace();
} finally {
cleanup();
out.println(header+" : stopped");
}
}
private void cleanup() {
synchronized(lock) {
try {
while(connections.size()>0) {
StreamCopyThread sct=(StreamCopyThread)connections.elementAt(0);
sct.interrupt();
sct.join(threadTimeout);
}
} catch(InterruptedException xc) {
}
}
}
private static ProxyThread addProxy(String src,String srcPort, String dst, String dstPort,
PrintStream out, PrintStream err) throws
UnknownHostException, IOException
{
InetAddress srcAddr=(src==null) ? null : InetAddress.getByName(src);
return new ProxyThread(srcAddr,Integer.parseInt(srcPort),
InetAddress.getByName(dst),Integer.parseInt(dstPort),out,err);
}
private static java.util.Vector parseConfigFile(String fileName,PrintStream out,PrintStream err) throws
FileNotFoundException, IOException, UnknownHostException
{
java.util.Vector result=new java.util.Vector();
FileInputStream in=new FileInputStream(fileName);
java.util.Properties props= new java.util.Properties();
props.load(in);
in.close();
for(int i=0;;i++) {
String srcAddr=props.getProperty(propertyPrefix+"."+i+".sourceAddr");
String srcPort=props.getProperty(propertyPrefix+"."+i+".sourcePort");
if(srcPort==null)
break;
String dstAddr=props.getProperty(propertyPrefix+"."+i+".destAddr");
String dstPort=props.getProperty(propertyPrefix+"."+i+".destPort");
if(dstAddr==null) {
throw new IllegalArgumentException("Missing destination address for proxy "+i);
}
if(dstPort==null) {
throw new IllegalArgumentException("Missing destination port for proxy "+i);
}
result.addElement(addProxy(srcAddr,srcPort,dstAddr,dstPort,out,err));
}
return result;
}
static java.util.Vector parseArguments(String[] argv,PrintStream out,PrintStream err) throws
FileNotFoundException, IOException, UnknownHostException
{
java.util.Vector result=null;
int argBase=0;
String src=null;
if(argv.length>1) {
if(argv.length>3) {
argBase=1;
src=argv[0];
}
result=new java.util.Vector();
result.addElement(addProxy(src,argv[argBase++],argv[argBase++],argv[argBase++],out,err));
} else if(argv.length==1) {
result=parseConfigFile(argv[0],out,err);
} else {
throw new IllegalArgumentException(argsMessage);
}
return result;
}
public static void main(String[] argv) throws Exception {
System.out.println("Java Transparent Proxy");
System.out.println("Copyright (C) 1999 by Didier Frick (http://www.dfr.ch/)");
System.out.println("This software is provided under the GNU general public license"+
" (http://www.gnu.org/copyleft/gpl.html)");
try {
parseArguments(argv,System.out,System.err);
} catch(IllegalArgumentException xc) {
System.err.println(xc.getMessage());
System.exit(1);
}
}
}
The initial ProxyThread code came from here: http://www.dfr.ch/en/proxy.html