/** * Copyright 2007-2015, Kaazing Corporation. All rights reserved. * * 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.kaazing.k3po.pcap.converter.internal.author.composer; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; import org.kaazing.k3po.pcap.converter.internal.author.RptScriptsCreatorFailureException; import org.kaazing.k3po.pcap.converter.internal.author.emitter.Emitter; import org.kaazing.k3po.pcap.converter.internal.author.emitter.EmitterFactory; import org.kaazing.k3po.pcap.converter.internal.author.emitter.OutputType; import org.kaazing.k3po.pcap.converter.internal.author.script.AbstractScript; import org.kaazing.k3po.pcap.converter.internal.author.script.ScriptState; import org.kaazing.k3po.pcap.converter.internal.author.script.TcpScript; import org.kaazing.k3po.pcap.converter.internal.packet.Packet; /** * Composes the rupert scripts by having several script fragments linked to port locations and in different states * */ public class TcpClientComposer extends AbstractComposer { private final Map<Integer, TcpClientScript> scripts = new HashMap<>(); private final static Logger LOG = Logger.getLogger(TcpClientComposer.class.getName()); protected final static OutputType OUTPUT_TYPE = OutputType.TCP_CLIENT_SCRIPT; public TcpClientComposer(EmitterFactory emitterFactory, Emitter emitter, String ipaddress) { super(emitterFactory, emitter, ipaddress); LOG.fine("Creating tcp client composer for " + this.ipaddress); } @Override public void emitConversation(Packet packet) { if ( packet.isTcpFlagsAck() && packet.isTcpFlagsSyn() ) { processSynAckPacket(packet); return; } int destPort = packet.getDestPort(); int srcPort = packet.getSrcPort(); long sequenceNumber = packet.getTcpSequenceNumber(); if ( packet.getDestIpAddr().equals(ipaddress) && scripts.containsKey(destPort) ) { // Inbound packet TcpClientScript script = scripts.get(destPort); if ( packet.getTcpPayloadSize() > 0 ) { if ( !script.recordSeqNumAndReturnTrueOnNewEntry(sequenceNumber, packet.getTcpPayloadSize()) ) { LOG.fine("Replayed tcp packet at packet:" + packet.getPacketNumber()); return; } script.readPayloadOfTcpPacket(packet); } if ( packet.isTcpFlagsFin() ) { script.setClosingReadAck(sequenceNumber); } if ( script.isClosingWriteAck(packet.getTcpAcknowledgementNumber()) && packet.isTcpFlagsAck() ) { script.writeCloseWrite(packet.getTimeInMicroSecondsFromEpoch()); } } else if ( ipaddress.equals(packet.getSrcIpAddr()) && scripts.containsKey(srcPort) ) { // Outbound packet TcpClientScript script = scripts.get(srcPort); if ( script.getState() == ScriptState.CONNECT ) { if ( packet.isTcpFlagsAck() ) { script.writeConnected(packet.getTimeInMicroSecondsFromEpoch()); } } if ( packet.getTcpPayloadSize() > 0 ) { if ( !script.recordSeqNumAndReturnTrueOnNewEntry(sequenceNumber, packet.getTcpPayloadSize()) ) { LOG.fine("Replayed tcp packet at packet:" + packet.getPacketNumber()); return; } script.writePayloadOfTcpPacket(packet); } if ( packet.isTcpFlagsFin() ) { script.setClosingWriteAck(sequenceNumber); } if ( script.isClosingReadAck(packet.getTcpAcknowledgementNumber()) ) { script.writeCloseRead(packet.getTimeInMicroSecondsFromEpoch()); } } } @Override public void writeToFile() { for (TcpClientScript iter : scripts.values()) { iter.writeBufferToFile(); } addScriptFragmentsIntoBuffer(); commitToFile(); } @Override public String getScript() { addScriptFragmentsIntoBuffer(); return getBuffer(); } @Override public boolean isFinished() { for (Integer i : scripts.keySet()) { if ( scripts.get(i).getState() != ScriptState.CLOSED && scripts.get(i).getState() != ScriptState.NOT_INITED ) { return false; } } return true; } /** * Process a connection packet which is read as an syn-ack * @param packet */ protected void processSynAckPacket(Packet packet) { Integer clientPort = packet.getDestPort(); Integer serverPort = packet.getSrcPort(); String serverIp = packet.getSrcIpAddr(); TcpClientScript scriptFragmentWriter = scripts.get(clientPort); if ( scriptFragmentWriter == null ) { scriptFragmentWriter = new TcpClientScript(emitterFactory.getRptScriptEmitter(OUTPUT_TYPE, "tcp-server-" + serverIp + "-" + serverPort + "-client-" + ipaddress + "-" + clientPort + "-ClientSide")); } else if ( scriptFragmentWriter.getState() != ScriptState.CLOSED ) { throw new RptScriptsCreatorFailureException( "Attempting to open already opened tcp connection from client port:" + clientPort); } scriptFragmentWriter.writeConnect(serverIp, serverPort, packet); scripts.put(clientPort, scriptFragmentWriter); } private void addScriptFragmentsIntoBuffer() { clearBuffer(); for (AbstractScript rw1 : scripts.values()) { addToScript(rw1.getBuffer()); } } private class TcpClientScript extends TcpScript { public TcpClientScript(Emitter emitter) { super(emitter); } public void writeConnect(String serverIp, int serverPort, Packet packet) { super.setLastActionTime(packet.getTimeInMicroSecondsFromEpoch()); writeMetaData("Connect occured at epoch " + packet.getTimeStamp() + " - " + packet.getTimeInMicroSecondsFromEpoch() ); setState(ScriptState.CONNECT); writeln("connect tcp://" + serverIp + ":" + serverPort); } } protected static String formatFragmentName(String ipAddr, Integer clientPort) { return formatFragmentName(ipAddr, clientPort.toString()); } protected static String formatFragmentName(String ipAddr, String clientPort) { return ipAddr + SEP + clientPort; } }