2
0
mirror of https://github.com/xcat2/xNBA.git synced 2024-11-23 10:01:47 +00:00
xNBA/contrib/t2hproxy/T2hproxy.java
2005-05-17 16:44:57 +00:00

509 lines
13 KiB
Java

/*
* TFTP to HTTP proxy in Java
*
* Copyright Ken Yap 2003
* Released under GPL2
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.UnsupportedEncodingException;
import java.lang.String;
import java.lang.StringBuffer;
import java.lang.Thread;
import java.lang.NumberFormatException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.BufferUnderflowException;
import java.util.HashMap;
import java.util.Properties;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Description of the Class
*
*@author ken
*@created 24 September 2003
*/
public class T2hproxy implements Runnable {
/**
* Description of the Field
*/
public final static String NAME = T2hproxy.class.getName();
/**
* Description of the Field
*/
public final static String VERSION = "0.1";
/**
* Description of the Field
*/
public final static int MTU = 1500;
/**
* Description of the Field
*/
public final static short TFTP_RRQ = 1;
/**
* Description of the Field
*/
public final static short TFTP_DATA = 3;
/**
* Description of the Field
*/
public final static short TFTP_ACK = 4;
/**
* Description of the Field
*/
public final static short TFTP_ERROR = 5;
/**
* Description of the Field
*/
public final static short TFTP_OACK = 6;
/**
* Description of the Field
*/
public final static short ERR_NOFILE = 1;
/**
* Description of the Field
*/
public final static short ERR_ILLOP = 4;
/**
* Description of the Field
*/
public final static int MAX_RETRIES = 5;
/**
* TFTP timeout in milliseconds
*/
public final static int TFTP_ACK_TIMEOUT = 2000;
/**
* Description of the Field
*/
public final static int DEFAULT_PROXY_PORT = 3128;
private static Log log = LogFactory.getLog(T2hproxy.class);
/**
* The members below must be per thread and must not share any storage with
* the main thread
*/
private DatagramSocket responsesocket;
private DatagramPacket response;
private InetAddress iaddr;
private int port;
private byte[] req;
private String prefix;
private String proxy = null;
private int timeout;
private HashMap options = new HashMap();
private int blocksize = 512;
private HttpClient client = new HttpClient();
private HttpMethod method;
private BufferedInputStream bstream = null;
private String message;
/**
* Constructor for the T2hproxy object
*
*@param i Description of the Parameter
*@param p Description of the Parameter
*@param b Description of the Parameter
*@param pf Description of the Parameter
*@param pr Description of the Parameter
*@param t Timeout for HTTP GET
*/
public T2hproxy(InetAddress i, int p, byte[] b, String pf, String pr, int t) {
iaddr = i;
port = p;
// make a copy of the request buffer
req = new byte[b.length];
System.arraycopy(b, 0, req, 0, b.length);
prefix = pf;
// proxy can be null
proxy = pr;
timeout = t;
}
/**
* Extract an asciz string from bufer
*
*@param buffer Description of the Parameter
*@return The asciz value
*/
private String getAsciz(ByteBuffer buffer) {
StringBuffer s = new StringBuffer();
try {
byte b;
while ((b = buffer.get()) != 0) {
s.append((char) b);
}
} catch (BufferUnderflowException e) {
} finally {
return (s.toString());
}
}
/**
* Convert a string of digits to a number, invalid => 0
*
*@param s Description of the Parameter
*@return Description of the Return Value
*/
private int atoi(String s) {
if (s == null) {
return (0);
}
int value = 0;
try {
value = (new Integer(s)).intValue();
} catch (NumberFormatException e) {
}
return (value);
}
/**
* Wait for ack packet with timeout
*
*@return Return block number acked
*/
private int waitForAck() {
DatagramPacket ack = new DatagramPacket(new byte[MTU], MTU);
try {
do {
responsesocket.setSoTimeout(TFTP_ACK_TIMEOUT);
responsesocket.receive(ack);
} while (!ack.getAddress().equals(iaddr) || ack.getPort() != port);
} catch (SocketTimeoutException e) {
return (-1);
} catch (Exception e) {
log.info(e.toString(), e);
}
ByteBuffer buffer = ByteBuffer.wrap(ack.getData(), ack.getOffset(), ack.getLength() - ack.getOffset());
short op;
if ((op = buffer.getShort()) == TFTP_ACK) {
return ((int) buffer.getShort());
} else if (op == TFTP_ERROR) {
return (-2);
}
return (-3);
}
/**
* Description of the Method
*
*@param error Description of the Parameter
*@param message Description of the Parameter
*/
private void sendError(short error, String message) {
ByteBuffer buffer = ByteBuffer.wrap(response.getData());
buffer.putShort(TFTP_ERROR).putShort(error).put(message.getBytes());
response.setLength(buffer.position());
try {
responsesocket.send(response);
} catch (Exception e) {
log.info(e.toString(), e);
}
}
/**
* Description of the Method
*
*@return Description of the Return Value
*/
private boolean sendOackRecvAck() {
ByteBuffer buffer = ByteBuffer.wrap(response.getData());
buffer.putShort(TFTP_OACK).put("blksize".getBytes()).put((byte) 0).put(String.valueOf(blocksize).getBytes()).put((byte) 0);
response.setLength(buffer.position());
int retry;
for (retry = 0; retry < MAX_RETRIES; retry++) {
try {
responsesocket.send(response);
} catch (Exception e) {
log.info(e.toString(), e);
}
if (waitForAck() == 0) {
log.debug("Ack received");
break;
}
}
return (retry < MAX_RETRIES);
}
/**
* Description of the Method
*
*@param block Description of the Parameter
*@return Description of the Return Value
*/
private boolean sendDataBlock(int block) {
int retry;
for (retry = 0; retry < MAX_RETRIES; retry++) {
try {
responsesocket.send(response);
} catch (Exception e) {
log.info(e.toString(), e);
}
int ablock;
if ((ablock = waitForAck()) == block) {
log.debug("Ack received for " + ablock);
break;
} else if (ablock == -1) {
log.info("Timeout waiting for ack");
} else if (ablock == -2) {
return (false);
} else {
log.info("Unknown opcode from ack");
}
}
return (retry < MAX_RETRIES);
}
/**
* Description of the Method
*
*@param buffer Description of the Parameter
*@return Description of the Return Value
*/
private boolean handleOptions(ByteBuffer buffer) {
for (; ; ) {
String option = getAsciz(buffer);
String value = getAsciz(buffer);
if (option.equals("") || value.equals("")) {
break;
}
log.info(option + " " + value);
options.put(option, value);
}
blocksize = atoi((String) options.get("blksize"));
if (blocksize < 512) {
blocksize = 512;
}
if (blocksize > 1432) {
blocksize = 1432;
}
return (sendOackRecvAck());
}
/**
* Description of the Method
*
*@param url Description of the Parameter
*/
private void makeStream(String url) {
// establish a connection within timeout milliseconds
client.setConnectionTimeout(timeout);
if (proxy != null) {
String[] hostport = proxy.split(":");
int port = DEFAULT_PROXY_PORT;
if (hostport.length > 1) {
port = atoi(hostport[1]);
if (port == 0) {
port = DEFAULT_PROXY_PORT;
}
}
log.info("Proxy is " + hostport[0] + ":" + port);
client.getHostConfiguration().setProxy(hostport[0], port);
}
// create a method object
method = new GetMethod(url);
method.setFollowRedirects(true);
method.setStrictMode(false);
try {
int status;
if ((status = client.executeMethod(method)) != 200) {
log.info(message = method.getStatusText());
return;
}
bstream = new BufferedInputStream(method.getResponseBodyAsStream());
} catch (HttpException he) {
message = he.getMessage();
} catch (IOException ioe) {
message = "Unable to get " + url;
}
}
/**
* Reads a block of data from URL stream
*
*@param stream Description of the Parameter
*@param data Description of the Parameter
*@param blocksize Description of the Parameter
*@param offset Description of the Parameter
*@return Number of bytes read
*/
private int readBlock(BufferedInputStream stream, byte[] data, int offset, int blocksize) {
int status;
int nread = 0;
while (nread < blocksize) {
try {
status = stream.read(data, offset + nread, blocksize - nread);
} catch (Exception e) {
return (-1);
}
if (status < 0) {
return (nread);
}
nread += status;
}
return (nread);
}
/**
* Description of the Method
*
*@param filename Description of the Parameter
*/
private void doRrq(String filename) {
String url = prefix + filename;
log.info("GET " + url);
makeStream(url);
if (bstream == null) {
log.info(message);
sendError(ERR_NOFILE, message);
return;
}
// read directly into send buffer to avoid buffer copying
byte[] data;
ByteBuffer buffer = ByteBuffer.wrap(data = response.getData());
// dummy puts to get start position of data
buffer.putShort(TFTP_DATA).putShort((short) 0);
int start = buffer.position();
int length;
int block = 1;
do {
length = readBlock(bstream, data, start, blocksize);
block &= 0xffff;
log.debug("Block " + block + " " + length);
// fill in the block number
buffer.position(0);
buffer.putShort(TFTP_DATA).putShort((short) block);
response.setLength(start + length);
if (!sendDataBlock(block)) {
break;
}
buffer.position(start);
block++;
} while (length >= blocksize);
log.info("Closing TFTP session");
// clean up the connection resources
method.releaseConnection();
method.recycle();
}
/**
* Main processing method for the T2hproxy object
*/
public void run() {
ByteBuffer buffer = ByteBuffer.wrap(req);
buffer.getShort();
String filename = getAsciz(buffer);
String mode = getAsciz(buffer);
log.info(filename + " " + mode);
response = new DatagramPacket(new byte[MTU], MTU, iaddr, port);
try {
responsesocket = new DatagramSocket();
} catch (SocketException e) {
log.info(e.toString(), e);
return;
}
if (!handleOptions(buffer)) {
return;
}
doRrq(filename);
}
/**
* Description of the Method
*
*@param s Description of the Parameter
*@param r Description of the Parameter
*@param prefix Description of the Parameter
*@param proxy Description of the Parameter
*@param timeout Description of the Parameter
*/
public static void handleRequest(DatagramSocket s, DatagramPacket r, String prefix, String proxy, int timeout) {
log.info("Connection from " + r.getAddress().getCanonicalHostName() + ":" + r.getPort());
ByteBuffer buffer = ByteBuffer.wrap(r.getData(), r.getOffset(), r.getLength() - r.getOffset());
if (buffer.getShort() != TFTP_RRQ) {
DatagramPacket error = new DatagramPacket(new byte[MTU], MTU);
ByteBuffer rbuf = ByteBuffer.wrap(error.getData());
rbuf.putShort(TFTP_ERROR).putShort(ERR_ILLOP).put("Illegal operation".getBytes());
error.setLength(rbuf.position());
try {
s.send(error);
} catch (Exception e) {
log.info(e.toString(), e);
}
return;
}
// fork thread
new Thread(new T2hproxy(r.getAddress(), r.getPort(), r.getData(), prefix, proxy, timeout)).start();
}
/**
* The main program for the T2hproxy class
*
*@param argv The command line arguments
*@exception IOException Description of the Exception
*/
public static void main(String[] argv) throws IOException {
log.info(T2hproxy.NAME + "." + T2hproxy.VERSION);
int port = Integer.getInteger(T2hproxy.NAME + ".port", 69).intValue();
String prefix = System.getProperty(T2hproxy.NAME + ".prefix", "http://localhost/");
String proxy = System.getProperty(T2hproxy.NAME + ".proxy");
int timeout = Integer.getInteger(T2hproxy.NAME + ".timeout", 5000).intValue();
String propfile = System.getProperty(T2hproxy.NAME + ".properties");
if (propfile != null) {
FileInputStream pf = new FileInputStream(propfile);
Properties p = new Properties(System.getProperties());
p.load(pf);
// set the system properties
System.setProperties(p);
}
DatagramSocket requestsocket;
try {
requestsocket = new DatagramSocket(port);
} catch (SocketException e) {
log.info(e.toString(), e);
return;
}
DatagramPacket request = new DatagramPacket(new byte[MTU], MTU);
for (; ; ) {
try {
requestsocket.receive(request);
handleRequest(requestsocket, request, prefix, proxy, timeout);
} catch (Exception e) {
log.info(e.toString(), e);
}
}
}
}