/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.network.protocols;
import static jpcsp.network.protocols.InternetChecksum.computeInternetChecksum;
import java.io.EOFException;
import jpcsp.util.Utilities;
public class TCP {
// TCP packet structure, see https://en.wikipedia.org/wiki/Transmission_Control_Protocol
public int sourcePort;
public int destinationPort;
public int sequenceNumber;
public int acknowledgmentNumber;
public int dataOffset;
public int reserved;
public boolean flagNS; // ECN-nonce concealment protection
public boolean flagCWR; // Congestion Window Reduced
public boolean flagECE; // ECN-Echo has a dual role, depending on the value of the SYN flag
public boolean flagURG; // indicates that the Urgent pointer field is significant
public boolean flagACK; // indicates that the Acknowledgment field is significant
public boolean flagPSH; // Push function
public boolean flagRST; // Reset the connection
public boolean flagSYN; // Synchronize sequence numbers
public boolean flagFIN; // Last package from sender
public int windowSize;
public int checksum;
public int urgentPointer;
public byte[] options;
public byte[] data;
public TCP() {
dataOffset = 5;
windowSize = 0x4000;
}
public TCP(TCP tcp) {
sourcePort = tcp.sourcePort;
destinationPort = tcp.destinationPort;
sequenceNumber = tcp.sequenceNumber;
acknowledgmentNumber = tcp.acknowledgmentNumber;
dataOffset = tcp.dataOffset;
reserved = tcp.reserved;
flagNS = tcp.flagNS;
flagCWR = tcp.flagCWR;
flagECE = tcp.flagECE;
flagURG = tcp.flagURG;
flagACK = tcp.flagACK;
flagPSH = tcp.flagPSH;
flagRST = tcp.flagRST;
flagSYN = tcp.flagSYN;
flagFIN = tcp.flagFIN;
windowSize = tcp.windowSize;
checksum = tcp.checksum;
urgentPointer = tcp.urgentPointer;
options = tcp.options;
data = tcp.data;
}
public void swapSourceAndDestination() {
int port = sourcePort;
sourcePort = destinationPort;
destinationPort = port;
}
public void computeChecksum(IPv4 ipv4) throws EOFException {
// Computes the checksum with 0 at the checksum field
checksum = 0;
// The checksum also covers a 12 bytes pseudo header
NetPacket checksumPacket = new NetPacket(12 + sizeOf());
// Pseudo header:
// - source IP address (4 bytes)
// - destination IP address (4 bytes)
// - 0 (1 byte)
// - protocol (1 byte)
// - TCP length (2 bytes)
checksumPacket.writeBytes(ipv4.sourceIPAddress);
checksumPacket.writeBytes(ipv4.destinationIPAddress);
checksumPacket.write8(0);
checksumPacket.write8(ipv4.protocol);
checksumPacket.write16(sizeOf());
write(checksumPacket);
checksum = computeInternetChecksum(checksumPacket.getBuffer(), 0, checksumPacket.getOffset());
}
private int getOptionsLength() {
return Math.max((dataOffset - 5) * 4, 0);
}
public void read(NetPacket packet) throws EOFException {
sourcePort = packet.read16();
destinationPort = packet.read16();
sequenceNumber = packet.read32();
acknowledgmentNumber = packet.read32();
dataOffset = packet.readBits(4);
reserved = packet.readBits(3);
flagNS = packet.readBoolean();
flagCWR = packet.readBoolean();
flagECE = packet.readBoolean();
flagURG = packet.readBoolean();
flagACK = packet.readBoolean();
flagPSH = packet.readBoolean();
flagRST = packet.readBoolean();
flagSYN = packet.readBoolean();
flagFIN = packet.readBoolean();
windowSize = packet.read16();
checksum = packet.read16();
urgentPointer = packet.read16();
options = packet.readBytes(getOptionsLength());
data = packet.readBytes(packet.getLength());
}
public NetPacket write(NetPacket packet) throws EOFException {
packet.write16(sourcePort);
packet.write16(destinationPort);
packet.write32(sequenceNumber);
packet.write32(acknowledgmentNumber);
packet.writeBits(dataOffset, 4);
packet.writeBits(reserved, 3);
packet.writeBoolean(flagNS);
packet.writeBoolean(flagCWR);
packet.writeBoolean(flagECE);
packet.writeBoolean(flagURG);
packet.writeBoolean(flagACK);
packet.writeBoolean(flagPSH);
packet.writeBoolean(flagRST);
packet.writeBoolean(flagSYN);
packet.writeBoolean(flagFIN);
packet.write16(windowSize);
packet.write16(checksum);
packet.write16(urgentPointer);
packet.writeBytes(options, 0, getOptionsLength());
packet.writeBytes(data);
return packet;
}
public NetPacket write() throws EOFException {
return write(new NetPacket(sizeOf()));
}
public int sizeOf() {
int size = dataOffset * 4;
if (data != null) {
size += data.length;
}
return size;
}
private void addFlagString(StringBuilder s, String flagName, boolean flagValue) {
if (flagValue) {
if (s.length() > 0) {
s.append("|");
}
s.append(flagName);
}
}
private String getFlagsString() {
StringBuilder s = new StringBuilder();
addFlagString(s, "NS", flagNS);
addFlagString(s, "CWR", flagCWR);
addFlagString(s, "ECE", flagECE);
addFlagString(s, "URG", flagURG);
addFlagString(s, "ACK", flagACK);
addFlagString(s, "PSH", flagPSH);
addFlagString(s, "RST", flagRST);
addFlagString(s, "SYN", flagSYN);
addFlagString(s, "FIN", flagFIN);
return s.toString();
}
@Override
public String toString() {
return String.format("sourcePort=0x%X, destinationPort=0x%X, sequenceNumber=0x%X, acknowledgmentNumber=0x%X, dataOffset=0x%X, reserved=0x%X, flags=%s, windowSize=0x%X, checksum=0x%04X, urgentPointer=0x%X, options=%s, data=%s", sourcePort, destinationPort, sequenceNumber, acknowledgmentNumber, dataOffset, reserved, getFlagsString(), windowSize, checksum, urgentPointer, Utilities.getMemoryDump(options, 0, getOptionsLength()), Utilities.getMemoryDump(data));
}
}