123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- /*
- * 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);
- }
- }
- }
- }
|