mirror of
				https://github.com/xcat2/xNBA.git
				synced 2025-10-30 19:02:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			509 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			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);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |