package com.limegroup.gnutella.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.StringTokenizer;
import com.limegroup.gnutella.Assert;
import com.limegroup.gnutella.ByteReader;
import com.limegroup.gnutella.ErrorService;
// extends AssertCmparisons just to get useful methods.
public class FakeProxyServer extends AssertComparisons {
/**
* The server which we tell limewire is the proxy
*/
private ServerSocket _proxyServer;
/**
* The socket limewire wants to connect to
*/
private ServerSocket _destinationServer;
private boolean _proxyOn;
private int _proxyVersion;
private boolean _authentication;
private boolean _makeError = false;
private boolean _isHTTPRequest = false;
final static String USER = "Sumeet";
final static String PASS = "Thadani";
public FakeProxyServer(int proxyPort, int destinationPort) {
super("fake");
_proxyOn = false;
_proxyVersion = ProxyTest.NONE;
_authentication = false;
startServers(proxyPort, destinationPort);
}
private void startServers(int proxyPort, int destPort) {
try {
_proxyServer = new ServerSocket();
_proxyServer.bind(new InetSocketAddress(proxyPort));
_destinationServer = new ServerSocket();
_destinationServer.bind(new InetSocketAddress(destPort));
} catch(IOException iox) {
ErrorService.error(iox);
}
Thread proxyThread = new ManagedThread() {
public void managedRun() {
proxyLoop();
}
};
proxyThread.setDaemon(true);
proxyThread.start();
Thread destThread = new ManagedThread() {
public void managedRun() {
destLoop();
}
};
destThread.setDaemon(true);
destThread.start();
}
private void proxyLoop() {
try {
while(true) {
Socket incomingProxy = null;
incomingProxy = _proxyServer.accept();
if(!_proxyOn)
fail("LimeWire connected to proxy server instead of directly");
InputStream is = incomingProxy.getInputStream();
OutputStream os = incomingProxy.getOutputStream();
if(_proxyVersion == ProxyTest.SOCKS4)
checkSOCKS4(is, os);
else if(_proxyVersion == ProxyTest.SOCKS5)
checkSOCKS5(is, os);
else if(_proxyVersion == ProxyTest.HTTP)
checkHTTP(is, os);
else
assertTrue("test not set up correctly, incorrect proxy version",
_isHTTPRequest);
int a = 0;
if(_isHTTPRequest) {
consumeHttpHeaders(is);
writeHTTPBack(os);
try {
Thread.sleep(1000);
} catch (InterruptedException x) { }
}
else {
//os.write('x');
while(a != -1)
a = is.read();
}
if(!incomingProxy.isClosed())
incomingProxy.close();
}
} catch(IOException iox) {
ErrorService.error(iox);
}
}
private void checkSOCKS4(InputStream is, OutputStream os)
throws IOException {
byte currByte = (byte)is.read();
assertEquals("Wrong version sent by LW to proxy", 4, currByte);
assertEquals( "connect command not sent", 1, is.read());
//TODO: make sure port is correct
is.read();
is.read();
//check IP
assertEquals("0th byte of ip wrong", 127, is.read());
assertEquals("1st byte of ip wrong", 0, is.read());
assertEquals("2nd byte of ip wrong", 0, is.read());
assertEquals("3rd byte of ip wrong", 1, is.read());
if(_authentication) {
byte[] u = new byte[USER.length()];
is.read(u);
assertEquals("LW sent wrong user", USER, new String(u));
}
assertEquals("LW did not send terminating 0", 0, is.read());
os.write((byte)4);//send version
if(_makeError)
os.write(0x33);//write wrong status code
else
os.write(0x5A);//write correct status code
//write out random ip port
byte[] ip = {(byte)1,(byte)1,(byte)1,(byte)1,(byte)1, (byte)1};
os.write(ip);
os.flush();
}
private void checkSOCKS5(InputStream is, OutputStream os)
throws IOException {
byte currByte = (byte)is.read();
assertEquals("Wrong version sent by LW to proxy", 5, currByte);
currByte = (byte)is.read();
if(_authentication)
assertEquals("should support 2 auth methods", 2, currByte);
else
assertEquals("should support 1 auth method", 1, currByte);
currByte = (byte)is.read();
assertEquals("we always support no auth", 0, currByte);
if(_authentication) {
currByte = (byte)is.read();
assertEquals("should support user/passwd", 2, currByte);
}
os.write((byte)5);//confirm that we are supporting version 5
if(_authentication)
os.write((byte)2);
else
os.write((byte)0);
if(_authentication) {//do all the checking
assertEquals("wrong auth version", 1, is.read());
assertEquals("wrong user len", USER.length(), is.read());
byte[] u = new byte[USER.length()];
is.read(u);
assertEquals("Wrong user sent", USER, new String(u));
assertEquals("wrong pass len", PASS.length(), is.read());
byte[] p = new byte[PASS.length()];
is.read(p);
assertEquals("Wrong pass sent", PASS, new String(p));
os.write((byte)1);//send version
os.write((byte)0); //send success.
}
assertEquals("no version sent at end", 5, is.read());
assertEquals("no connect command sent", 1, is.read());
assertEquals("no reserved byte sent", 0, is.read());
assertEquals("IPv4 marker not sent", 1, is.read());
assertEquals("wrong 0th ip byte", 127, is.read());
assertEquals("wrong 1st ip byte", 0, is.read());
assertEquals("wrong 2nd ip byte", 0, is.read());
assertEquals("wrong 3rd ip byte", 1, is.read());
//TODO2: check if port bytes are correct
is.read();
is.read();
os.write((byte)5);//write version again
if(_makeError)
os.write((byte)8); //make error send bad status
else
os.write((byte)0);//status
os.write((byte)1);//write reserved byte
os.write((byte)1);//ip v4
byte[] ip = {(byte)1,(byte)1,(byte)1,(byte)1,(byte)1, (byte)1};
os.write(ip);
os.flush();
}
private void checkHTTP(InputStream is, OutputStream os) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line = reader.readLine();
StringTokenizer tok = new StringTokenizer(line, " :");
assertEquals("connect string not sent", "CONNECT", tok.nextToken());
assertEquals("LW sent wrong host", "127.0.0.1", tok.nextToken());
assertEquals("LW sent wrong port", "" + ProxyTest.DEST_PORT, tok.nextToken());
assertEquals("LW didn't send http string", "HTTP/1.0", tok.nextToken());
if(_makeError)
os.write("503 Busy\r\n\r\n".getBytes());
else
os.write("200 OK\r\n\r\n".getBytes());
}
private void destLoop() {
try {
while(true) {
Socket incomingDest = null;
incomingDest = _destinationServer.accept();
if(_proxyOn)
fail("Limewire connected to desination instead of proxy");
if(_isHTTPRequest) {
writeHTTPBack(incomingDest.getOutputStream());
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
if(!incomingDest.isClosed())
incomingDest.close();
}
} catch(IOException iox) {
ErrorService.error(iox);
}
}
private void writeHTTPBack(OutputStream os) throws IOException {
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Server: limewire \r\n".getBytes());
os.write("Content-Type: txt/html \r\n".getBytes());
os.write("Content-Length: 5 \r\n".getBytes());
os.write("\r\n".getBytes());
os.write("hello".getBytes());
os.flush();
}
private void consumeHttpHeaders(InputStream is) throws IOException {
ByteReader reader = new ByteReader(is);
String line = " ";
while(!line.equals("")) {
line = reader.readLine();
//System.out.println("\t\t"+line);
}
}
void killServers() {
try {
if(_proxyServer != null)
_proxyServer.close();
if(_destinationServer != null)
_destinationServer.close();
} catch(IOException iox) {}
}
void setProxyOn(boolean proxyOn) {
_proxyOn = proxyOn;
}
void setAuthentication(boolean auth) {
_authentication = auth;
}
void setProxyVersion(int ver) {
_proxyVersion = ver;
}
void setMakeError(boolean b) {
_makeError = b;
}
void setHttpRequest(boolean b) {
_isHTTPRequest = b;
}
}