/** * 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 TcpServerComposer extends AbstractComposer { private final Map<String, TcpServerScript> scriptFragments = new HashMap<>();; private final static Logger LOG = Logger.getLogger(TcpServerComposer.class.getName()); protected final static OutputType OUTPUT_TYPE = OutputType.TCP_SERVER_SCRIPT; public TcpServerComposer(EmitterFactory emitterFactory, Emitter emitter, String ipaddress) { super(emitterFactory, emitter, ipaddress); LOG.fine("Creating tcp server composer for " + ipaddress); } @Override public void emitConversation(Packet packet) { if ( packet.isTcpFlagsAck() && packet.isTcpFlagsSyn() ) { processSynAckPacket(packet); return; } String destId = makeClientId(packet.getDestIpAddr(), packet.getDestPort()); String srcId = makeClientId(packet.getSrcIpAddr(), packet.getSrcPort()); long sequenceNumber = packet.getTcpSequenceNumber(); if ( scriptFragments.containsKey(destId) ) { TcpServerScript fragment = scriptFragments.get(destId); // Outbound Packet if ( packet.getTcpPayloadSize() > 0 ) { if ( !fragment.recordSeqNumAndReturnTrueOnNewEntry(sequenceNumber, packet.getTcpPayloadSize()) ) { LOG.fine("Replayed tcp packet at packet: " + packet.getPacketNumber()); return; } fragment.writePayloadOfTcpPacket(packet); } if ( packet.isTcpFlagsFin() ) { fragment.setClosingWriteAck(sequenceNumber); } if ( fragment.isClosingReadAck(packet.getTcpAcknowledgementNumber()) ) { fragment.writeCloseRead(packet.getTimeInMicroSecondsFromEpoch()); } } else if ( scriptFragments.containsKey(srcId) ) { // Inbound Packet TcpServerScript fragment = scriptFragments.get(srcId); if ( fragment.getState() == ScriptState.ACCEPT && packet.isTcpFlagsAck() ) { fragment.writeConnected(packet.getTimeInMicroSecondsFromEpoch()); } if ( packet.getTcpPayloadSize() > 0 ) { if ( !fragment.recordSeqNumAndReturnTrueOnNewEntry(sequenceNumber, packet.getTcpPayloadSize()) ) { LOG.info("Replayed tcp packet at packet: " + packet.getPacketNumber()); return; } fragment.readPayloadOfTcpPacket(packet); } if ( packet.isTcpFlagsFin() ) { fragment.setClosingReadAck(sequenceNumber); } if ( fragment.isClosingWriteAck(packet.getTcpAcknowledgementNumber()) ) { fragment.writeCloseWrite(packet.getTimeInMicroSecondsFromEpoch()); } } } @Override public boolean isFinished() { for (String i : scriptFragments.keySet()) { if ( scriptFragments.get(i).getState() != ScriptState.CLOSED && scriptFragments.get(i).getState() != ScriptState.NOT_INITED ) { return false; } } return true; } @Override public void writeToFile() { for (TcpServerScript iter : scriptFragments.values()) { iter.writeBufferToFile(); } addScriptFragmentsIntoBuffer(); commitToFile(); } @Override public String getScript() { addScriptFragmentsIntoBuffer(); return getBuffer(); } /** * Processes the syn-ack packet which notes the start of tcp conversation * @param packet */ protected void processSynAckPacket(Packet packet) { String serverIp = packet.getSrcIpAddr(); int serverPort = packet.getSrcPort(); String clientIp = packet.getDestIpAddr(); int clientPort = packet.getDestPort(); String clientId = makeClientId(clientIp, clientPort); TcpServerScript serverFragmentWriter = scriptFragments.get(clientId); if ( serverFragmentWriter == null ) { serverFragmentWriter = new TcpServerScript(emitterFactory.getRptScriptEmitter(OUTPUT_TYPE, "tcp-server-" + serverIp + "-" + serverPort + "-client-" + clientIp + "-" + clientPort + "-ServerSide")); } else if ( serverFragmentWriter.getState() != ScriptState.CLOSED ) { throw new RptScriptsCreatorFailureException( "Attempting to open already opened tcp connection from server composer:" + clientId); } serverFragmentWriter.writeAccept(serverIp, serverPort, packet); scriptFragments.put(clientId, serverFragmentWriter); } private void addScriptFragmentsIntoBuffer() { clearBuffer(); for (AbstractScript rw1 : scriptFragments.values()) { addToScript(rw1.getBuffer()); } } private static String makeClientId(String clientIp, int port) { return clientIp + ":" + port; } private class TcpServerScript extends TcpScript { public TcpServerScript(Emitter emitter) { super(emitter); } public void writeAccept(String ipAddress, int port, Packet packet) { setLastActionTime(packet.getTimeInMicroSecondsFromEpoch()); writeMetaData("Accepting at epoch " + packet.getTimeStamp() + " - " + packet.getTimeInMicroSecondsFromEpoch()); writeln("accept tcp://" + ipAddress + ":" + port); writeln("accepted"); setState(ScriptState.ACCEPT); } } protected static String formatFragmentName(String ipAddr, Integer serverPort) { return formatFragmentName(ipAddr, serverPort.toString()); } protected static String formatFragmentName(String ipAddr, String serverPort) { return ipAddr + SEP + serverPort; } }