Extract Conversation to separate file (i.e. it is no longer an inner class). Add...
[pingpong.git] / Code / Projects / SmartPlugDetector / src / main / java / edu / uci / iotproject / Conversation.java
1 package edu.uci.iotproject;
2
3 import org.pcap4j.core.PcapPacket;
4 import org.pcap4j.packet.IpV4Packet;
5 import org.pcap4j.packet.TcpPacket;
6
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Objects;
10
11 /**
12  * Models a (TCP) conversation/connection/session/flow (packet's belonging to the same session between a client and a
13  * server).
14  * Holds a list of {@link PcapPacket}s identified as pertaining to the flow. Note that this list is <em>not</em>
15  * considered when determining equality of two {@code Conversation} instances in order to allow for a
16  * {@code Conversation} to function as a key in data structures such as {@link java.util.Map} and {@link java.util.Set}.
17  * See {@link #equals(Object)} for the definition of equality.
18  */
19 public class Conversation {
20
21     private final String mClientIp;
22     private final int mClientPort;
23     private final String mServerIp;
24     private final int mServerPort;
25     private final List<PcapPacket> mPackets;
26
27     /**
28      * Constructs a new {@code Conversation}.
29      * @param clientIp The IP of the host that is considered the client (i.e. the host that initiates the conversation)
30      *                 in the conversation.
31      * @param clientPort The port number used by the client for the conversation.
32      * @param serverIp The IP of the host that is considered the server (i.e. is the responder) in the conversation.
33      * @param serverPort The port number used by the server for the conversation.
34      */
35     public Conversation(String clientIp, int clientPort, String serverIp, int serverPort) {
36         this.mClientIp = clientIp;
37         this.mClientPort = clientPort;
38         this.mServerIp = serverIp;
39         this.mServerPort = serverPort;
40         this.mPackets = new ArrayList<>();
41     }
42
43     /**
44      * Add a packet to the list of packets associated with this conversation.
45      * @param packet The packet that is to be added to (associated with) this conversation.
46      */
47     public void addPacket(PcapPacket packet) {
48         // Apply precondition to preserve class invariant: all packets in mPackets must match the 4 tuple that
49         // defines the conversation.
50         // ==== Precondition: verify that packet does indeed pertain to conversation. ====
51         IpV4Packet ipPacket = Objects.requireNonNull(packet.get(IpV4Packet.class));
52         // For now we only support TCP flows.
53         TcpPacket tcpPacket = Objects.requireNonNull(packet.get(TcpPacket.class));
54         String ipSrc = ipPacket.getHeader().getSrcAddr().getHostAddress();
55         String ipDst = ipPacket.getHeader().getDstAddr().getHostAddress();
56         int srcPort = tcpPacket.getHeader().getSrcPort().valueAsInt();
57         int dstPort = tcpPacket.getHeader().getDstPort().valueAsInt();
58         String clientIp, serverIp;
59         int clientPort, serverPort;
60         if (ipSrc.equals(mClientIp)) {
61             clientIp = ipSrc;
62             clientPort = srcPort;
63             serverIp = ipDst;
64             serverPort = dstPort;
65         } else {
66             clientIp = ipDst;
67             clientPort = dstPort;
68             serverIp = ipSrc;
69             serverPort = srcPort;
70         }
71         if (!(clientIp.equals(mClientIp) && clientPort == mClientPort &&
72                 serverIp.equals(mServerIp) && serverPort == mServerPort)) {
73             throw new IllegalArgumentException(
74                     String.format("Attempt to add packet that does not pertain to %s",
75                             Conversation.class.getSimpleName()));
76         }
77         // ================================================================================
78         mPackets.add(packet);
79     }
80
81     // =========================================================================================================
82     // We simply reuse equals and hashCode methods of String.class to be able to use this class as a key
83     // in a Map.
84
85     /**
86      * <em>Note:</em> currently, equality is determined based on pairwise equality of the elements of the four tuple
87      * ({@link #mClientIp}, {@link #mClientPort}, {@link #mServerIp}, {@link #mServerPort}) for {@code this} and
88      * {@code obj}.
89      * @param obj The object to test for equality with {@code this}.
90      * @return {@code true} if {@code obj} is considered equal to {@code this} based on the definition of equality given above.
91      */
92     @Override
93     public boolean equals(Object obj) {
94         return obj instanceof Conversation && this.toString().equals(obj.toString());
95     }
96
97     @Override
98     public int hashCode() {
99         return toString().hashCode();
100     }
101     // =========================================================================================================
102
103     @Override
104     public String toString() {
105         return String.format("%s:%d %s:%d", mClientIp, mClientPort, mServerIp, mServerPort);
106     }
107 }