/*
* Copyright 2011 Future Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.tftp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ServerGetProcessor {
private Logger logger = LoggerFactory.getLogger(ServerGetProcessor.class.getName());
private static final int MAXIMUM_TFTP_PAYLOAD_SIZE = 512;
private static final int TIMEOUT = 60 * 1000; // 1min timeout
private DatagramSocket socket;
private InetAddress clientIp;
private int clientPort;
private FileInputStream is;
private int blockNum = 0;
private int expectedAckNum = 1;
private boolean isFileNotFound = false;
private boolean is512 = false;
public ServerGetProcessor(DatagramPacket p, String fileName) {
try {
socket = new DatagramSocket();
socket.setSoTimeout(TIMEOUT);
clientIp = p.getAddress();
clientPort = p.getPort();
is = new FileInputStream(new File(fileName));
} catch (SocketException e) {
logger.warn("kraken tftp: cannot handle get request", e);
} catch (FileNotFoundException e) {
logger.warn("kraken tftp: requested file not found", e);
handleFileNotFound();
isFileNotFound = true;
}
}
public void start() {
try {
if (isFileNotFound)
return;
byte[] data;
do {
blockNum++;
if (blockNum > 65535) {
handleExceed();
break;
}
data = new byte[MAXIMUM_TFTP_PAYLOAD_SIZE];
int remain = is.available();
/* send & receive */
if (remain == 0) {
if (!is512)
break;
send(null);
receive();
break;
} else if (remain < 512) {
byte[] finalData = new byte[remain];
sendAndReceive(finalData);
} else if (remain == 512) {
is512 = true;
sendAndReceive(data);
} else {
sendAndReceive(data);
}
} while (true);
} catch (IOException e) {
logger.warn("kraken tftp: cannot handle get request, stopped", e);
}
}
private void sendAndReceive(byte[] data) throws IOException {
while (true) {
is.read(data);
send(data);
int ackNum = receive();
if (ackNum == expectedAckNum) {
expectedAckNum++;
break;
}
}
}
private void send(byte[] data) throws IOException {
byte[] b;
if (data == null)
b = setData(blockNum);
else
b = setData(blockNum, data);
DatagramPacket outgoing = new DatagramPacket(b, b.length, clientIp, clientPort);
socket.send(outgoing);
}
private int receive() throws IOException {
try {
byte[] inbuf = new byte[200];
DatagramPacket incoming = new DatagramPacket(inbuf, inbuf.length);
socket.receive(incoming);
byte[] tftpData = incoming.getData();
return (int) (((int) 0 << 24) | ((int) 0 << 16) | ((tftpData[2] << 8) & 0xFFFF) | (tftpData[3] & 0xFF));
} catch (SocketTimeoutException e) {
// try next time
return -1;
}
}
private byte[] setData(int blockNum, byte[] b) {
ByteBuffer bb = ByteBuffer.allocate(b.length + 4);
bb.putShort((short) 3);
bb.putShort((short) blockNum);
bb.put(b);
bb.flip();
int length = bb.limit();
byte[] b1 = new byte[length];
bb.get(b1);
return b1;
}
private byte[] setData(int blockNum) {
ByteBuffer bb = ByteBuffer.allocate(4);
bb.putShort((short) 3);
bb.putShort((short) blockNum);
bb.flip();
byte[] b1 = new byte[4];
bb.get(b1);
return b1;
}
private void handleFileNotFound() {
try {
/* generate tftp error packet(File not found) */
byte[] b = new byte[] { 0x00, 0x05, 0x00, 0x01, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x66,
0x6f, 0x75, 0x6e, 0x64, 0x00 };
DatagramPacket outgoing = new DatagramPacket(b, b.length, clientIp, clientPort);
socket.send(outgoing);
} catch (IOException e) {
logger.warn("kraken tftp: cannot send file not found error", e);
}
}
private void handleExceed() {
try {
/* generate tftp error packet(File size limit exceed) */
byte[] b = new byte[] { 0x00, 0x05, 0x00, 0x03, 0x46, 0x69, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x20,
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x20, 0x65, 0x78, 0x63, 0x65, 0x65, 0x64, 0x00 };
DatagramPacket outgoing = new DatagramPacket(b, b.length, clientIp, clientPort);
socket.send(outgoing);
} catch (IOException e) {
logger.warn("kraken tftp: cannot send file size limit exceeded error", e);
}
}
}