Skip to content

Instantly share code, notes, and snippets.

@aurorapar
Created April 6, 2018 10:18
Show Gist options
  • Save aurorapar/c87ddd8859f9b5ae46bf1be43189bfc9 to your computer and use it in GitHub Desktop.
Save aurorapar/c87ddd8859f9b5ae46bf1be43189bfc9 to your computer and use it in GitHub Desktop.
TFTP Client Rev RFC 1350
// Client
import java.net.*;
import java.io.*;
import java.util.*;
import java.nio.charset.*;
import java.time.LocalTime;
public class Client
{
public static int serverPort = 69;
public static int blockNumber = 0;
public static String serverAddress;
public static int timeouts = 0;
public static int missedPackets = 0;
public static int filePosition = 0;
public static int clientPort = 0;
public static FileInputStream fileStream;
public static final int TIMEOUTS_MAX = 6;
public static final int MISSED_PACKET_MAX = 6;
public static final int BUFFER_SIZE = 516;
public static final int TIMEOUT_INTERVAL = 2000;
public static final boolean DEBUG = false;
public static void main(String args[])
{
if(args.length != 3 && args.length != 4)
{
System.out.println("Improper argument length. Correct options are:\n\t <host> <PUT|GET> <file> [binary]");
return;
}
serverAddress = args[0];
String operation = args[1].toUpperCase();
String fileName = args[2];
String mode = "netascii";
if(!"PUT".equals(operation) && !"GET".equals(operation))
{
System.out.println(String.format("Could not handle \'operation\' %s\nAccepted operations are:\n\tPUT\tGET\n",operation));
return;
}
File file = new File(fileName);
if(!file.exists() || file.isDirectory())
{
System.out.println(String.format("Could not handle request\n\t%s\ndoes not exist", fileName));
return;
}
long fileSize = file.length();
if(fileSize > 65536 * 512)
{
System.out.println("\nFile over maximum. Consider packaging file into 32Mb archives.");
//return;
}
if(args.length == 4)
if("binary".equals(args[3]))
{
mode = "octet";
try{fileStream = new FileInputStream(file);}
catch(Exception e){}
}
byte OPCode;
if("GET".equals(operation))
OPCode = 0x1;
else
{
OPCode = 0x2;
}
// Communications
byte[] receiveData = new byte[Client.BUFFER_SIZE];
try
{
ByteArrayOutputStream sendStream = new ByteArrayOutputStream(Client.BUFFER_SIZE);
ByteArrayOutputStream receiveStream = new ByteArrayOutputStream(Client.BUFFER_SIZE);
int replyPort = 0;
int randomPort = new Random().nextInt(64000) + 1024;
Client.clientPort = randomPort;
sendStream.write(0x0);
sendStream.write(OPCode);
sendStream.write(fileName.getBytes("US-ASCII"));
sendStream.write(0x0);
sendStream.write(mode.getBytes("US-ASCII"));
sendStream.write(0x0);
DatagramSocket client = new DatagramSocket(randomPort);
client.setSoTimeout(Client.TIMEOUT_INTERVAL);
DatagramPacket receivedPacket = new DatagramPacket(receiveData, receiveData.length);
while(true)
{
try
{
send(sendStream.toByteArray(), client, Client.serverPort);
client.receive(receivedPacket);
replyPort = receivedPacket.getPort();
receiveData = receivedPacket.getData();
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. No ACK received");
return;
}
System.out.println("\nTimed out, retrying...\nNumber of attemps: " + Client.timeouts);
send(sendStream.toByteArray(), client, replyPort);
}
}
//debug("'" + new String(receiveData, "US-ASCII") + "'");
if(receiveData[1] == 5)
{
System.out.println("Error:\n\t" + new String(Arrays.copyOfRange(receiveData, 4, receiveData.length), StandardCharsets.US_ASCII));
return;
}
if(0x2 == OPCode && receiveData[1] == 4)
{
// State information
debug("Acknowledged write request");
int blockOne = 1;
int blockTwo = 0;
int sentData = 0;
byte[] lastSentPacket = sendStream.toByteArray();
byte[] lastReceivedPacket = receiveData;
//Date date = new Date();
long start = new Date().getTime();
int dataInterval = 0;
double rate = 0;
while(sentData < fileSize)
{
Client.timeouts = 0;
missedPackets = 0;
debug("Creating packet");
sendStream.reset();
sendStream.write(0x0);
sendStream.write(0x3);
sendStream.write((byte) blockTwo);
sendStream.write((byte) blockOne);
read(sendStream, mode);
debug(""+sendStream.size());
sentData += sendStream.size() - 4; // Don't do buffer size because of last f
dataInterval += sendStream.size() - 4;
if(blockOne % 50 == 0)
{
start = new Date().getTime();
dataInterval = 0;
}
if(blockOne % 50 == 49)
rate = (double) dataInterval / (new Date().getTime() - start);
String status = String.format("\rStatus:\t%.2f%%", (double) sentData/fileSize*100);
status += String.format("\t\tRate: \t%.0fkbps", rate);
System.out.print(status);
debug("Sending data now");
send(sendStream.toByteArray(), client, replyPort);
lastSentPacket = sendStream.toByteArray();
while(true)
{
try
{
client.receive(receivedPacket);
receiveData = receivedPacket.getData();
lastReceivedPacket = receiveData;
// TFTP resets at 128 to block 63 ???
//if(block==128 && receiveData[3] == 63)
// block = 62;
replyPort = receivedPacket.getPort();
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. Ending program.");
return;
}
System.out.println("\nTimed out, retrying...\nNumber of attemps: " + Client.timeouts);
send(lastSentPacket, client, replyPort);
}
}
if(receiveData[3] != blockOne || receiveData[2] != blockTwo)
{
if(receiveData[1] == 5)
{
System.out.println("Error:\n\t" + new String(Arrays.copyOfRange(receiveData, 4, receiveData.length), StandardCharsets.US_ASCII));
return;
}
if(receiveData[3] == blockOne - 1 && receiveData[2] == blockTwo)
{
while(true)
{
try
{
Client.missedPackets++;
if(Client.missedPackets > Client.MISSED_PACKET_MAX)
{
System.out.println("Maximum number of missed packets reached. Ending program.");
return;
}
System.out.println("\nMissed packet...\n"+ Client.missedPackets + " missed packets");
send(lastSentPacket, client, replyPort);
client.receive(receivedPacket);
receiveData = receivedPacket.getData();
lastReceivedPacket = receiveData;
/*
if(block==128 && receiveData[3] == 63)
block = 62;
*/
replyPort = receivedPacket.getPort();
break;
}
catch(SocketTimeoutException e)
{
Client.timeouts++;
if(Client.timeouts>Client.TIMEOUTS_MAX)
{
System.out.println("Maximum number of attempts reached. Ending program.");
return;
}
System.out.println("Timed out, retrying...\nNumber of attemps: " + Client.timeouts);
}
}
}
}
//System.out.println("\nBlock "+block+"\n");
blockOne++;
if((byte) blockOne == 0 && (blockOne > 0 || blockTwo>0))
blockTwo++;
if((byte) blockTwo == 0xff && blockOne == 0xff && fileStream.available() > 0)
{
System.out.println("\nMaximum transfer size reached. Consider packaging file into 32Mb archives.");
return;
}
}
System.out.println("\nFile transfer complete.");
}
}
catch(Exception e)
{
System.out.println(e.toString());
}
return;
}
public static void read(ByteArrayOutputStream sendStream, String mode)
{
switch(mode)
{
case "octet":
try
{
int b = 0;
while(sendStream.size() < Client.BUFFER_SIZE)
{
if((b = fileStream.read()) > -1)
{
sendStream.write(b);
}
else
{
break;
}
}
}
catch(Exception e)
{
debug(e.toString());
}
debug("Last bit read");
break;
case "netascii":
debug("Netascii mode");
break;
default:
System.out.println("Invalid mode specified");
return;
}
}
public static void send(byte[] response, DatagramSocket client, int port)
{
try
{
InetAddress IP = InetAddress.getByName(Client.serverAddress);
DatagramPacket packetToSend = new DatagramPacket(
response,
response.length,
IP,
port
);
client.send(packetToSend);
}
catch(Exception e)
{
System.out.println(e.toString());
}
}
public static void debug(String output)
{
if(Client.DEBUG)
System.out.println(output);
}
public static void debug(byte[] array)
{
if(Client.DEBUG)
{
String debug = "";
for(int x = 0; x < array.length; x++)
{
debug += " " + array[x];
}
System.out.println(debug);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment