/*
* Copyright 2010 NCHOVY
*
* 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.pcap.decoder.tcp;
import java.util.Collection;
import org.krakenapps.pcap.decoder.ip.IpProcessor;
import org.krakenapps.pcap.decoder.ip.Ipv4Packet;
import org.krakenapps.pcap.decoder.ipv6.Ipv6Packet;
import org.krakenapps.pcap.decoder.ipv6.Ipv6Processor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TcpDecoder implements IpProcessor, Ipv6Processor {
private TcpSegmentCallbacks segmentCallbacks;
private TcpProtocolMapper mapper;
private TcpSessionTable sessionTable;
private TcpFlagHandler flagHandler;
private TcpPacketHandler packetHandler;
private TcpSackHandler sackHandler;
private final Logger logger = LoggerFactory.getLogger(TcpDecoder.class.getName());
public TcpDecoder(TcpProtocolMapper mapper) {
this.mapper = mapper;
segmentCallbacks = new TcpSegmentCallbacks();
sessionTable = new TcpSessionTable(mapper);
flagHandler = new TcpFlagHandler(mapper);
packetHandler = new TcpPacketHandler();
sackHandler = new TcpSackHandler();
}
public TcpProtocolMapper getProtocolMapper() {
return mapper;
}
public Collection<? extends TcpSession> getCurrentSessions() {
return sessionTable.getCurrentSessions();
}
public void registerSegmentCallback(TcpSegmentCallback callback) {
segmentCallbacks.register(callback);
}
public void unregisterSegmentCallback(TcpSegmentCallback callback) {
segmentCallbacks.unregister(callback);
}
public void process(Ipv4Packet packet) {
TcpPacket newTcp = TcpPacket.parse(packet);
if (newTcp.isJumbo()) {
TcpSessionImpl session = sessionTable.getSession(newTcp.getSessionKey());
if (session != null) {
sessionTable.abnormalClose(session.getKey());
logger.error("session terminate: find jumbo packet ");
}
} else {
handle(newTcp);
}
}
@Override
public void process(Ipv6Packet packet) {
// TODO: next header handling
TcpPacket newTcp = TcpPacket.parse(packet);
if (newTcp.isJumbo()) {
TcpSessionImpl session = sessionTable.getSession(newTcp.getSessionKey());
if (session != null) {
sessionTable.abnormalClose(session.getKey());
logger.error("session terminate: find jumbo packet ");
}
} else {
handle(newTcp);
}
}
private void handle(TcpPacket pkt) {
/* get session */
TcpSessionImpl session = sessionTable.getSession(pkt.getSessionKey());
flagHandler.handle(sessionTable, session, pkt);
session = sessionTable.getSession(pkt.getSessionKey());
if (pkt.isGarbage() || session == null) {
if (logger.isDebugEnabled())
logger.debug("kraken pcap: null session for tcp [{}]", pkt);
return;
}
pkt.setDirection(session);
TcpDirection direction = pkt.getDirection();
/* find and set SACK option */
int flags = pkt.getFlags();
if (flags == TcpFlag.SYN || flags == (TcpFlag.SYN + TcpFlag.ACK)) {
if (isSack(pkt)) {
if (direction == TcpDirection.ToServer)
session.setClientStreamOption(TcpStreamOption.SACK);
else
session.setServerStreamOption(TcpStreamOption.SACK);
}
}
/* handle TCP segment */
TcpStreamOption streamOption;
if (direction == TcpDirection.ToServer)
streamOption = session.getServerStreamOption();
else
streamOption = session.getClientStreamOption();
if (streamOption == TcpStreamOption.SACK)
sackHandler.handle(sessionTable, session, pkt);
else
packetHandler.handle(sessionTable, session, pkt);
segmentCallbacks.fireReceiveCallbacks(session, pkt);
}
private boolean isSack(TcpPacket packet) {
if (packet.getOptions() == null)
return false;
else {
byte[] options = packet.getOptions();
int offset = 0;
while (offset < options.length) {
switch (options[offset]) {
/* skip 1 byte option */
case 0x00:
case 0x01:
offset++;
continue;
/* skip 4 bytes option(MSS) */
case 0x02:
offset += 4;
continue;
/* skip 3 bytes option */
case 0x03:
case 0x0e:
offset += 3;
continue;
/* skip 10 bytes option(TimeStamps) */
case 0x08:
offset += 10;
continue;
case 0x04:
return true;
}
}
return false;
}
}
}