Purge warnings from prism2 drivers
[gpxe.git] / contrib / t2hproxy / T2hproxy.java
blobcfe1d1a7938ee0236206b1587d108d034d7b25f7
1 /*
2 * TFTP to HTTP proxy in Java
4 * Copyright Ken Yap 2003
5 * Released under GPL2
6 */
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.FileInputStream;
10 import java.io.BufferedInputStream;
11 import java.io.UnsupportedEncodingException;
12 import java.lang.String;
13 import java.lang.StringBuffer;
14 import java.lang.Thread;
15 import java.lang.NumberFormatException;
16 import java.net.DatagramPacket;
17 import java.net.DatagramSocket;
18 import java.net.InetAddress;
19 import java.net.SocketException;
20 import java.net.SocketTimeoutException;
21 import java.nio.Buffer;
22 import java.nio.ByteBuffer;
23 import java.nio.BufferUnderflowException;
24 import java.util.HashMap;
25 import java.util.Properties;
27 import org.apache.commons.httpclient.Credentials;
28 import org.apache.commons.httpclient.Header;
29 import org.apache.commons.httpclient.HostConfiguration;
30 import org.apache.commons.httpclient.HttpClient;
31 import org.apache.commons.httpclient.HttpException;
32 import org.apache.commons.httpclient.HttpMethod;
33 import org.apache.commons.httpclient.UsernamePasswordCredentials;
34 import org.apache.commons.httpclient.methods.GetMethod;
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
39 /**
40 * Description of the Class
42 *@author ken
43 *@created 24 September 2003
45 public class T2hproxy implements Runnable {
46 /**
47 * Description of the Field
49 public final static String NAME = T2hproxy.class.getName();
50 /**
51 * Description of the Field
53 public final static String VERSION = "0.1";
54 /**
55 * Description of the Field
57 public final static int MTU = 1500;
58 /**
59 * Description of the Field
61 public final static short TFTP_RRQ = 1;
62 /**
63 * Description of the Field
65 public final static short TFTP_DATA = 3;
66 /**
67 * Description of the Field
69 public final static short TFTP_ACK = 4;
70 /**
71 * Description of the Field
73 public final static short TFTP_ERROR = 5;
74 /**
75 * Description of the Field
77 public final static short TFTP_OACK = 6;
78 /**
79 * Description of the Field
81 public final static short ERR_NOFILE = 1;
82 /**
83 * Description of the Field
85 public final static short ERR_ILLOP = 4;
86 /**
87 * Description of the Field
89 public final static int MAX_RETRIES = 5;
90 /**
91 * TFTP timeout in milliseconds
93 public final static int TFTP_ACK_TIMEOUT = 2000;
94 /**
95 * Description of the Field
97 public final static int DEFAULT_PROXY_PORT = 3128;
99 private static Log log = LogFactory.getLog(T2hproxy.class);
101 * The members below must be per thread and must not share any storage with
102 * the main thread
104 private DatagramSocket responsesocket;
105 private DatagramPacket response;
106 private InetAddress iaddr;
107 private int port;
108 private byte[] req;
109 private String prefix;
110 private String proxy = null;
111 private int timeout;
112 private HashMap options = new HashMap();
113 private int blocksize = 512;
114 private HttpClient client = new HttpClient();
115 private HttpMethod method;
116 private BufferedInputStream bstream = null;
117 private String message;
121 * Constructor for the T2hproxy object
123 *@param i Description of the Parameter
124 *@param p Description of the Parameter
125 *@param b Description of the Parameter
126 *@param pf Description of the Parameter
127 *@param pr Description of the Parameter
128 *@param t Timeout for HTTP GET
130 public T2hproxy(InetAddress i, int p, byte[] b, String pf, String pr, int t) {
131 iaddr = i;
132 port = p;
133 // make a copy of the request buffer
134 req = new byte[b.length];
135 System.arraycopy(b, 0, req, 0, b.length);
136 prefix = pf;
137 // proxy can be null
138 proxy = pr;
139 timeout = t;
144 * Extract an asciz string from bufer
146 *@param buffer Description of the Parameter
147 *@return The asciz value
149 private String getAsciz(ByteBuffer buffer) {
150 StringBuffer s = new StringBuffer();
151 try {
152 byte b;
153 while ((b = buffer.get()) != 0) {
154 s.append((char) b);
156 } catch (BufferUnderflowException e) {
157 } finally {
158 return (s.toString());
164 * Convert a string of digits to a number, invalid => 0
166 *@param s Description of the Parameter
167 *@return Description of the Return Value
169 private int atoi(String s) {
170 if (s == null) {
171 return (0);
173 int value = 0;
174 try {
175 value = (new Integer(s)).intValue();
176 } catch (NumberFormatException e) {
178 return (value);
183 * Wait for ack packet with timeout
185 *@return Return block number acked
187 private int waitForAck() {
188 DatagramPacket ack = new DatagramPacket(new byte[MTU], MTU);
189 try {
190 do {
191 responsesocket.setSoTimeout(TFTP_ACK_TIMEOUT);
192 responsesocket.receive(ack);
193 } while (!ack.getAddress().equals(iaddr) || ack.getPort() != port);
194 } catch (SocketTimeoutException e) {
195 return (-1);
196 } catch (Exception e) {
197 log.info(e.toString(), e);
199 ByteBuffer buffer = ByteBuffer.wrap(ack.getData(), ack.getOffset(), ack.getLength() - ack.getOffset());
200 short op;
201 if ((op = buffer.getShort()) == TFTP_ACK) {
202 return ((int) buffer.getShort());
203 } else if (op == TFTP_ERROR) {
204 return (-2);
206 return (-3);
211 * Description of the Method
213 *@param error Description of the Parameter
214 *@param message Description of the Parameter
216 private void sendError(short error, String message) {
217 ByteBuffer buffer = ByteBuffer.wrap(response.getData());
218 buffer.putShort(TFTP_ERROR).putShort(error).put(message.getBytes());
219 response.setLength(buffer.position());
220 try {
221 responsesocket.send(response);
222 } catch (Exception e) {
223 log.info(e.toString(), e);
229 * Description of the Method
231 *@return Description of the Return Value
233 private boolean sendOackRecvAck() {
234 ByteBuffer buffer = ByteBuffer.wrap(response.getData());
235 buffer.putShort(TFTP_OACK).put("blksize".getBytes()).put((byte) 0).put(String.valueOf(blocksize).getBytes()).put((byte) 0);
236 response.setLength(buffer.position());
237 int retry;
238 for (retry = 0; retry < MAX_RETRIES; retry++) {
239 try {
240 responsesocket.send(response);
241 } catch (Exception e) {
242 log.info(e.toString(), e);
244 if (waitForAck() == 0) {
245 log.debug("Ack received");
246 break;
249 return (retry < MAX_RETRIES);
254 * Description of the Method
256 *@param block Description of the Parameter
257 *@return Description of the Return Value
259 private boolean sendDataBlock(int block) {
260 int retry;
261 for (retry = 0; retry < MAX_RETRIES; retry++) {
262 try {
263 responsesocket.send(response);
264 } catch (Exception e) {
265 log.info(e.toString(), e);
267 int ablock;
268 if ((ablock = waitForAck()) == block) {
269 log.debug("Ack received for " + ablock);
270 break;
271 } else if (ablock == -1) {
272 log.info("Timeout waiting for ack");
273 } else if (ablock == -2) {
274 return (false);
275 } else {
276 log.info("Unknown opcode from ack");
279 return (retry < MAX_RETRIES);
284 * Description of the Method
286 *@param buffer Description of the Parameter
287 *@return Description of the Return Value
289 private boolean handleOptions(ByteBuffer buffer) {
290 for (; ; ) {
291 String option = getAsciz(buffer);
292 String value = getAsciz(buffer);
293 if (option.equals("") || value.equals("")) {
294 break;
296 log.info(option + " " + value);
297 options.put(option, value);
299 blocksize = atoi((String) options.get("blksize"));
300 if (blocksize < 512) {
301 blocksize = 512;
303 if (blocksize > 1432) {
304 blocksize = 1432;
306 return (sendOackRecvAck());
311 * Description of the Method
313 *@param url Description of the Parameter
315 private void makeStream(String url) {
316 // establish a connection within timeout milliseconds
317 client.setConnectionTimeout(timeout);
318 if (proxy != null) {
319 String[] hostport = proxy.split(":");
320 int port = DEFAULT_PROXY_PORT;
321 if (hostport.length > 1) {
322 port = atoi(hostport[1]);
323 if (port == 0) {
324 port = DEFAULT_PROXY_PORT;
327 log.info("Proxy is " + hostport[0] + ":" + port);
328 client.getHostConfiguration().setProxy(hostport[0], port);
330 // create a method object
331 method = new GetMethod(url);
332 method.setFollowRedirects(true);
333 method.setStrictMode(false);
334 try {
335 int status;
336 if ((status = client.executeMethod(method)) != 200) {
337 log.info(message = method.getStatusText());
338 return;
340 bstream = new BufferedInputStream(method.getResponseBodyAsStream());
341 } catch (HttpException he) {
342 message = he.getMessage();
343 } catch (IOException ioe) {
344 message = "Unable to get " + url;
350 * Reads a block of data from URL stream
352 *@param stream Description of the Parameter
353 *@param data Description of the Parameter
354 *@param blocksize Description of the Parameter
355 *@param offset Description of the Parameter
356 *@return Number of bytes read
358 private int readBlock(BufferedInputStream stream, byte[] data, int offset, int blocksize) {
359 int status;
360 int nread = 0;
361 while (nread < blocksize) {
362 try {
363 status = stream.read(data, offset + nread, blocksize - nread);
364 } catch (Exception e) {
365 return (-1);
367 if (status < 0) {
368 return (nread);
370 nread += status;
372 return (nread);
377 * Description of the Method
379 *@param filename Description of the Parameter
381 private void doRrq(String filename) {
382 String url = prefix + filename;
383 log.info("GET " + url);
384 makeStream(url);
385 if (bstream == null) {
386 log.info(message);
387 sendError(ERR_NOFILE, message);
388 return;
390 // read directly into send buffer to avoid buffer copying
391 byte[] data;
392 ByteBuffer buffer = ByteBuffer.wrap(data = response.getData());
393 // dummy puts to get start position of data
394 buffer.putShort(TFTP_DATA).putShort((short) 0);
395 int start = buffer.position();
396 int length;
397 int block = 1;
398 do {
399 length = readBlock(bstream, data, start, blocksize);
400 block &= 0xffff;
401 log.debug("Block " + block + " " + length);
402 // fill in the block number
403 buffer.position(0);
404 buffer.putShort(TFTP_DATA).putShort((short) block);
405 response.setLength(start + length);
406 if (!sendDataBlock(block)) {
407 break;
409 buffer.position(start);
410 block++;
411 } while (length >= blocksize);
412 log.info("Closing TFTP session");
413 // clean up the connection resources
414 method.releaseConnection();
415 method.recycle();
420 * Main processing method for the T2hproxy object
422 public void run() {
423 ByteBuffer buffer = ByteBuffer.wrap(req);
424 buffer.getShort();
425 String filename = getAsciz(buffer);
426 String mode = getAsciz(buffer);
427 log.info(filename + " " + mode);
428 response = new DatagramPacket(new byte[MTU], MTU, iaddr, port);
429 try {
430 responsesocket = new DatagramSocket();
431 } catch (SocketException e) {
432 log.info(e.toString(), e);
433 return;
435 if (!handleOptions(buffer)) {
436 return;
438 doRrq(filename);
443 * Description of the Method
445 *@param s Description of the Parameter
446 *@param r Description of the Parameter
447 *@param prefix Description of the Parameter
448 *@param proxy Description of the Parameter
449 *@param timeout Description of the Parameter
451 public static void handleRequest(DatagramSocket s, DatagramPacket r, String prefix, String proxy, int timeout) {
452 log.info("Connection from " + r.getAddress().getCanonicalHostName() + ":" + r.getPort());
453 ByteBuffer buffer = ByteBuffer.wrap(r.getData(), r.getOffset(), r.getLength() - r.getOffset());
454 if (buffer.getShort() != TFTP_RRQ) {
455 DatagramPacket error = new DatagramPacket(new byte[MTU], MTU);
456 ByteBuffer rbuf = ByteBuffer.wrap(error.getData());
457 rbuf.putShort(TFTP_ERROR).putShort(ERR_ILLOP).put("Illegal operation".getBytes());
458 error.setLength(rbuf.position());
459 try {
460 s.send(error);
461 } catch (Exception e) {
462 log.info(e.toString(), e);
464 return;
466 // fork thread
467 new Thread(new T2hproxy(r.getAddress(), r.getPort(), r.getData(), prefix, proxy, timeout)).start();
472 * The main program for the T2hproxy class
474 *@param argv The command line arguments
475 *@exception IOException Description of the Exception
477 public static void main(String[] argv) throws IOException {
478 log.info(T2hproxy.NAME + "." + T2hproxy.VERSION);
479 int port = Integer.getInteger(T2hproxy.NAME + ".port", 69).intValue();
480 String prefix = System.getProperty(T2hproxy.NAME + ".prefix", "http://localhost/");
481 String proxy = System.getProperty(T2hproxy.NAME + ".proxy");
482 int timeout = Integer.getInteger(T2hproxy.NAME + ".timeout", 5000).intValue();
483 String propfile = System.getProperty(T2hproxy.NAME + ".properties");
484 if (propfile != null) {
485 FileInputStream pf = new FileInputStream(propfile);
486 Properties p = new Properties(System.getProperties());
487 p.load(pf);
488 // set the system properties
489 System.setProperties(p);
491 DatagramSocket requestsocket;
492 try {
493 requestsocket = new DatagramSocket(port);
494 } catch (SocketException e) {
495 log.info(e.toString(), e);
496 return;
498 DatagramPacket request = new DatagramPacket(new byte[MTU], MTU);
499 for (; ; ) {
500 try {
501 requestsocket.receive(request);
502 handleRequest(requestsocket, request, prefix, proxy, timeout);
503 } catch (Exception e) {
504 log.info(e.toString(), e);