package org.krakenapps.pcap.decoder.tftp;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Set;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.udp.UdpPacket;
import org.krakenapps.pcap.decoder.udp.UdpProcessor;
import org.krakenapps.pcap.decoder.udp.UdpProtocolMapper;
import org.krakenapps.pcap.util.Buffer;
import org.krakenapps.pcap.util.BufferInputStream;
public class TftpDecoder implements UdpProcessor {
private final UdpProtocolMapper mapper;
private Set<TftpSession> sessions;
private Set<TftpProcessor> callbacks;
public TftpDecoder(UdpProtocolMapper mapper) {
this.mapper = mapper;
sessions = new HashSet<TftpSession>();
callbacks = new HashSet<TftpProcessor>();
}
public void register(TftpProcessor processor) {
callbacks.add(processor);
}
public void unregister(TftpProcessor processor) {
callbacks.remove(processor);
}
public void registerTftp(InetSocketAddress sockAddr) {
mapper.registerTemporaryMapping(sockAddr, Protocol.TFTP);
}
public void unregisterTftp(InetSocketAddress sockAddr) {
mapper.unregisterTemporaryMapping(sockAddr);
}
@Override
public void process(UdpPacket p) {
Buffer tftpData = p.getData();
short opCode = tftpData.getShort();
if (opCode == 1 || opCode == 2) {
createSession(opCode, p, tftpData);
registerTftp(p.getSource());
} else if (opCode == 3) {
handleDataPacket(p, tftpData);
} else if (opCode == 4) {
handleAckPacket(p, tftpData);
} else {
/* error case */
}
}
private void createSession(int opCode, UdpPacket p, Buffer tftpData) {
int len = tftpData.bytesBefore(new byte[] { 0x00 });
if (len == 0)
return;
byte[] fileName = new byte[len];
tftpData.gets(fileName);
/* skip 00 */
tftpData.get();
int len2 = tftpData.bytesBefore(new byte[] { 0x00 });
if (len2 == 0)
return;
byte[] mode = new byte[len2];
tftpData.gets(mode);
/* skip 00 */
tftpData.get();
TftpSession session = new TftpSession(opCode, p.getSource(), p.getDestination().getAddress(), fileName, mode);
sessions.add(session);
dispatchCommand(session.toString());
}
private TftpSession getSession(UdpPacket p) {
InetSocketAddress src = p.getSource();
InetSocketAddress dest = p.getDestination();
for (TftpSession s : sessions) {
if (s.equals(src, dest.getAddress()) || s.equals(dest, src.getAddress()))
return s;
}
return null;
}
private void terminate(TftpSession session) {
unregisterTftp(session.getSrcAddress());
session = null;
}
private void handleDataPacket(UdpPacket p, Buffer tftpData) {
/* retrieve tftp session */
TftpSession session = getSession(p);
if (session == null)
return;
int block = tftpData.getShort();
if (block == (session.getSendNum() + 1)) {
/* valid sequence */
int remain = tftpData.readableBytes();
byte[] data = new byte[remain];
tftpData.gets(data);
session.putData(data);
session.incSendNum();
if (remain < 512) {
/* last data packet */
Buffer b = session.getData();
BufferInputStream is = new BufferInputStream(b);
dispatchFile(is, session.getFileName());
terminate(session);
}
} else {
terminate(session);
}
}
private void handleAckPacket(UdpPacket p, Buffer tftpData) {
/* retrieve tftp session */
TftpSession session = getSession(p);
if (session == null)
return;
int block = tftpData.getShort();
if ((block == 0) && (session.getAckNum() == 0))
return;
if (block == (session.getAckNum() + 1)) {
/* valid sequence */
session.incAckNum();
} else {
terminate(session);
}
}
private void dispatchCommand(String command) {
for (TftpProcessor processor : callbacks) {
processor.onCommand(command);
}
}
private void dispatchFile(InputStream is, String fileName) {
for (TftpProcessor processor : callbacks) {
processor.onExtractFile(is, fileName);
}
}
}