package org.limewire.http;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
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.net.SocketException;
import java.nio.charset.Charset;
import java.util.StringTokenizer;
import org.apache.http.protocol.HTTP;
import org.limewire.concurrent.ThreadExecutor;
import org.limewire.io.ByteReader;
import org.limewire.net.ProxySettings.ProxyType;
import org.limewire.service.ErrorService;
import org.limewire.util.StringUtils;
// extends AssertCmparisons just to get useful methods.
public class FakeProxyServer {
/**
* The server which we tell limewire is the proxy
*/
private ServerSocket _proxyServer;
/**
* The socket limewire wants to connect to
*/
private ServerSocket _destinationServer;
private volatile boolean _proxyOn;
private volatile ProxyType _proxyVersion;
private volatile boolean _authentication;
private volatile boolean _makeError = false;
private boolean _isHTTPRequest = false;
private Thread proxyThread;
private Thread destThread;
final static String USER = "Sumeet";
final static String PASS = "Thadani";
private final int _destinationPort;
public FakeProxyServer(int proxyPort, int destinationPort) {
this._proxyOn = false;
_proxyVersion = ProxyType.NONE;
_authentication = false;
this._destinationPort = destinationPort;
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);
}
proxyThread = ThreadExecutor.newManagedThread(new Runnable() {
public void run() {
proxyLoop();
}
});
proxyThread.setDaemon(true);
proxyThread.start();
destThread = ThreadExecutor.newManagedThread(new Runnable() {
public void run() {
destLoop();
}
});
destThread.setDaemon(true);
destThread.start();
}
private void proxyLoop() {
try {
while(true) {
Socket incomingProxy = null;
incomingProxy = _proxyServer.accept();
// value has to be saved because field might have been reset in the test code
// before the exception is thrown in this thread
boolean savedMakeError = _makeError;
if(!_proxyOn)
fail("LimeWire connected to proxy server instead of directly");
InputStream is = incomingProxy.getInputStream();
OutputStream os = incomingProxy.getOutputStream();
if(_proxyVersion == ProxyType.SOCKS4)
checkSOCKS4(is, os);
else if(_proxyVersion == ProxyType.SOCKS5)
checkSOCKS5(is, os);
else if(_proxyVersion == ProxyType.HTTP)
checkHTTP(is, os);
else
assertTrue("test not set up correctly, incorrect proxy version",
_isHTTPRequest);
int a = 0;
try {
if(_isHTTPRequest) {
consumeHttpHeaders(is);
writeHTTPBack(os);
try {
Thread.sleep(1000);
} catch (InterruptedException x) { }
}
else {
//os.write('x');
while(a != -1)
a = is.read();
}
}
catch (SocketException se) {
// in error case socket might have been closed from the other side
// swallow exception then
if (!savedMakeError) {
throw se;
}
}
if(!incomingProxy.isClosed())
incomingProxy.close();
}
} catch(IOException iox) {
if (!_proxyServer.isClosed()) {
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[StringUtils.toUTF8Bytes(USER).length];
is.read(u);
assertEquals("LW sent wrong user", USER, StringUtils.getUTF8String(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[StringUtils.toUTF8Bytes(USER).length];
is.read(u);
assertEquals("Wrong user sent", USER, StringUtils.getUTF8String(u));
assertEquals("wrong pass len", PASS.length(), is.read());
byte[] p = new byte[StringUtils.toUTF8Bytes(PASS).length];
is.read(p);
assertEquals("Wrong pass sent", PASS, StringUtils.getUTF8String(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, Charset.forName(HTTP.DEFAULT_PROTOCOL_CHARSET)));
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", "" + _destinationPort, tok.nextToken());
assertEquals("LW didn't send http string", "HTTP/1.0", tok.nextToken());
if(_makeError)
os.write(StringUtils.toAsciiBytes("503 Busy\r\nHeader: Value\r\n\r\n"));
else
os.write(StringUtils.toAsciiBytes("200 OK\r\nHeader: Value\r\n\r\n"));
}
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) {
if (!_destinationServer.isClosed()) {
ErrorService.error(iox);
}
}
}
private void writeHTTPBack(OutputStream os) throws IOException {
os.write(StringUtils.toAsciiBytes("HTTP/1.1 200 OK\r\n"));
os.write(StringUtils.toAsciiBytes("Server: limewire \r\n"));
os.write(StringUtils.toAsciiBytes("Content-Type: txt/html \r\n"));
os.write(StringUtils.toAsciiBytes("Content-Length: 5 \r\n"));
os.write(StringUtils.toAsciiBytes("\r\n"));
os.write(StringUtils.toAsciiBytes("hello"));
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() throws InterruptedException {
try {
if(_proxyServer != null)
_proxyServer.close();
if(_destinationServer != null)
_destinationServer.close();
} catch(IOException iox) {}
if (proxyThread != null) {
proxyThread.join();
proxyThread = null;
}
if (destThread != null) {
destThread.join();
destThread = null;
}
}
void setProxyOn(boolean proxyOn) {
_proxyOn = proxyOn;
}
void setAuthentication(boolean auth) {
_authentication = auth;
}
void setProxyVersion(ProxyType ver) {
_proxyVersion = ver;
}
void setMakeError(boolean b) {
_makeError = b;
}
void setHttpRequest(boolean b) {
_isHTTPRequest = b;
}
}