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