b323d846e289f7b888ddeed33791dac777833b9f
[pingpong.git] / Code / Projects / SmartPlugDetector / src / main / java / edu / uci / iotproject / Main.java
1 package edu.uci.iotproject;
2
3
4 import org.pcap4j.core.*;
5 import org.pcap4j.packet.*;
6 import org.pcap4j.packet.DnsPacket;
7 import org.pcap4j.packet.namednumber.DnsResourceRecordType;
8
9 import java.io.EOFException;
10 import java.net.Inet4Address;
11 import java.net.UnknownHostException;
12 import java.util.*;
13 import java.util.concurrent.TimeoutException;
14
15 /**
16  * This is a system that reads PCAP files to compare
17  * patterns of DNS hostnames, packet sequences, and packet
18  * lengths with training data to determine certain events
19  * or actions for smart home devices.
20  *
21  * @author Janus Varmarken
22  * @author Rahmadi Trimananda (rtrimana@uci.edu)
23  * @version 0.1
24  */
25 public class Main {
26
27
28     public static void main(String[] args) throws PcapNativeException, NotOpenException, EOFException, TimeoutException, UnknownHostException {
29         final String fileName = "/users/varmarken/Desktop/wlan1.local.dns.pcap";
30         List<DnsPacket> dnsPackets = extractDnsAnswerPackets(fileName);
31         Map<String, Set<String>> ipToHostnameMap = constructIpToHostnameMap(dnsPackets);
32 //        ipToHostnameMap.forEach((k,v) -> System.out.println(String.format("%s => %s", k, v.toString())));
33
34
35         // ====== Debug code ======
36         PcapHandle handle;
37         try {
38             handle = Pcaps.openOffline(fileName, PcapHandle.TimestampPrecision.NANO);
39         } catch (PcapNativeException pne) {
40             handle = Pcaps.openOffline(fileName);
41         }
42         FlowPatternFinder fpf = new FlowPatternFinder(ipToHostnameMap);
43         fpf.findFlowPattern(handle, FlowPattern.TP_LINK_LOCAL_ON);
44
45         // ========================
46     }
47
48     /**
49      * Opens a PCAP file and extracts all DNS reply packets with non-empty answer sections.
50      * @param pcapFileName The name of the PCAP file.
51      * @return A list of DNS reply packets.
52      * @throws PcapNativeException
53      * @throws NotOpenException
54      * @throws TimeoutException
55      */
56     private static List<DnsPacket> extractDnsAnswerPackets(String pcapFileName) throws PcapNativeException, NotOpenException, TimeoutException {
57         PcapHandle handle;
58         try {
59             handle = Pcaps.openOffline(pcapFileName, PcapHandle.TimestampPrecision.NANO);
60         } catch (PcapNativeException pne) {
61             handle = Pcaps.openOffline(pcapFileName);
62         }
63         // Apparently BPFs don't support "dns" protocol filter, so have to filter by port.
64         handle.setFilter("port 53", BpfProgram.BpfCompileMode.OPTIMIZE);
65         ArrayList<DnsPacket> result = new ArrayList<>();
66         try {
67             Packet packet;
68             while ((packet = handle.getNextPacketEx()) != null) {
69                 DnsPacket dnsPacket = packet.get(DnsPacket.class);
70                 // We only care about DNS answers.
71                 if (dnsPacket != null && dnsPacket.getHeader().getAnswers().size() != 0) {
72                     result.add(dnsPacket);
73                 }
74             }
75         } catch (EOFException eof) {
76             // (Note have to resort to EOFException as handle.getStats().getNumPacketsCaptured() only works on Windows)
77             // Clean up.
78             handle.close();
79         }
80         System.out.println(String.format("Found %d DNS answers", result.size()));
81         return result;
82     }
83
84     /**
85      * Based on the information found in a list of DNS replies, this method constructs a {@link Map} that maps from an
86      * IP to a {@link Set} of hostnames associated with that IP.
87      *
88      * @param dnsPackets A list of DNS <em>reply</em> packets.
89      * @return A {@link Map} that maps from an IP to a {@link Set} of hostnames associated with that IP
90      * @throws UnknownHostException If an IP found in a {@code DnsPacket} is of incorrect length.
91      */
92     private static Map<String, Set<String>> constructIpToHostnameMap(List<DnsPacket> dnsPackets) throws UnknownHostException {
93         HashMap<String, Set<String>> result = new HashMap<>();
94         for(DnsPacket dnsPacket : dnsPackets) {
95             // The hostname that this DNS reply provides answers for.
96             // TODO: safe to assume only one question?
97             String hostname = dnsPacket.getHeader().getQuestions().get(0).getQName().getName();
98             for(DnsResourceRecord answer : dnsPacket.getHeader().getAnswers()) {
99                 // We only care about type A records
100                 if (!answer.getDataType().equals(DnsResourceRecordType.A)) {
101                     continue;
102                 }
103                 // Sanity check. For some reason the hostname appears to be the empty string in the answer .
104                 // We hence have to assume that all answers correspond to a single question that holds the hostname as part of its object tree.
105                 // Therefore, if there are more questions in one query-reply exchange, we are in trouble.
106                 if (!answer.getName().getName().equals("") && !answer.getName().getName().equals(hostname)) {
107                     throw new RuntimeException("[DNS parser] mismatch between hostname in question and hostname in answer");
108                 }
109                 // The IP in byte representation.
110                 byte[] ipBytes = answer.getRData().getRawData();
111                 // Convert to string representation.
112                 String ip = Inet4Address.getByAddress(ipBytes).getHostAddress();
113                 HashSet<String> hostnameSet = new HashSet<>();
114                 hostnameSet.add(hostname);
115                 // Update or insert depending on presence of key:
116                 // Concat the existing set and the new set if ip already present as key,
117                 // otherwise add an entry for ip pointing to new set.
118                 result.merge(ip, hostnameSet, (v1, v2) -> { v1.addAll(v2); return v1; });
119             }
120         }
121         return result;
122     }
123
124
125
126 //    /**
127 //     * Private class properties
128 //     */
129 //    private Pcap pcap;
130 //    private List<Pcap.Packet> listPacket;
131 //    private Map<String, String> mapIPAddressToHostname;
132 //
133 //    /**
134 //     * Private class constants
135 //     */
136 //    private static final int DNS_PORT = 53;
137 //
138 //    /**
139 //     * Constructor
140 //     *
141 //     * @param   file    name of the analyzed PCAP file
142 //     */
143 //    public Main(String file) throws IOException {
144 //
145 //        pcap = Pcap.fromFile(file);
146 //        listPacket = pcap.packets();
147 //        mapIPAddressToHostname = new HashMap<String, String>();
148 //    }
149 //
150 //
151 //
152 //
153 //
154 //    /**
155 //     * Private method that maps DNS hostnames to their
156 //     * respected IP addresses. This method iterates
157 //     * through the List<Pcap.Packet>, gets DNS packets,
158 //     * and gets the IP addresses associated with them.
159 //     */
160 //    private void mapHostnamesToIPAddresses() {
161 //
162 //        int counter = 1;
163 //        for(Pcap.Packet packet : listPacket) {
164 //            System.out.print("# " + counter++);
165 //            // Check the packet type
166 //            if (packet._root().hdr().network() == Pcap.Linktype.ETHERNET) {
167 //                EthernetFrame ethFrame = (EthernetFrame) packet.body();
168 //                if (ethFrame.etherType() == EthernetFrame.EtherTypeEnum.IPV4) {
169 //                    Ipv4Packet ip4Packet = (Ipv4Packet) ethFrame.body();
170 //
171 //                    System.out.print(" - Protocol: " + ip4Packet.protocol());
172 //                    if (ip4Packet.protocol() == Ipv4Packet.ProtocolEnum.UDP) {
173 //                        // DNS is UDP port 53
174 //                        UdpDatagram udpData = (UdpDatagram) ip4Packet.body();
175 //                        System.out.print(" - Source Port: " + udpData.srcPort());
176 //                        System.out.print(" - Dest Port: " + udpData.dstPort());
177 //
178 //                        // Source port 53 means this is DNS response
179 //                        if (udpData.srcPort() == DNS_PORT) {
180 //                            KaitaiStream dnsStream = new ByteBufferKaitaiStream(udpData.body());
181 //                            DnsPacket dnsPacket = new DnsPacket(dnsStream);
182 //                            ArrayList<DnsPacket.Query> queries = dnsPacket.queries();
183 //                            ArrayList<DnsPacket.Answer> answers = dnsPacket.answers();
184 //                            String strDomainName = new String();
185 //                            for(DnsPacket.Query query : queries) {
186 //                                System.out.print(" - Queries: ");
187 //                                DnsPacket.DomainName domainName = query.name();
188 //                                ArrayList<DnsPacket.Label> labels = domainName.name();
189 //                                for(int i = 0; i < labels.size(); i++) {
190 //                                    System.out.print(labels.get(i).name());
191 //                                    strDomainName = strDomainName + labels.get(i).name();
192 //                                    if(i < labels.size()-2) {
193 //                                        System.out.print(".");
194 //                                        strDomainName = strDomainName + ".";
195 //                                    }
196 //                                }
197 //                                break;  // We are assuming that there is only one query
198 //                            }
199 //                            System.out.print(" - Answers " + answers.size());
200 //                            for(DnsPacket.Answer answer : answers) {
201 //                                System.out.print(" - TypeType: " + answer.type());
202 //                                System.out.print(" - ClassType: " + answer.answerClass());
203 //                                System.out.print("\n - Answers: ");
204 //                                DnsPacket.Address address = answer.address();
205 //                                if (answer.type() == DnsPacket.TypeType.A) {
206 //                                    String strAnswer = new String();
207 //                                    ArrayList<Integer> ipList = address.ip();
208 //                                    for(int i = 0; i < ipList.size(); i++) {
209 //                                        System.out.print(ipList.get(i));
210 //                                        strAnswer = strAnswer + Integer.toString(ipList.get(i));
211 //                                        if(i < ipList.size()-1) {
212 //                                            System.out.print(".");
213 //                                            strAnswer = strAnswer + ".";
214 //                                        }
215 //                                    }
216 //                                    mapIPAddressToHostname.put(strAnswer, strDomainName);
217 //                                }
218 //                            }
219 //                        }
220 //                    }
221 //                }
222 //            }
223 //            System.out.println();
224 //        }
225 ////        for(Map.Entry<String, String> entry : mapIPAddressToHostname.entrySet()) {
226 ////            if (entry.getValue().equals("devs.tplinkcloud.com")) {
227 ////                System.out.println(entry.getKey() + " - " + entry.getValue());
228 ////            }
229 ////        }
230 //        System.out.println("Total map size: " + mapIPAddressToHostname.size());
231 //        System.out.println("Answer for 13.33.41.8: " + mapIPAddressToHostname.get("13.33.41.8"));
232 //        System.out.println("Answer for 34.226.240.125: " + mapIPAddressToHostname.get("34.226.240.125"));
233 //    }
234 //
235 //    /*private String cloudIPAddress(String hostName) {
236 //        if (hostName.equals("events.tplinkra.com"))
237 //            return "205.251.203.26";
238 //        else
239 //            return null;
240 //    }*/
241 //
242 //    // TODO move to separate class
243 //    // Add parameter that is the trace to be analyzed (most like the pcap library's representation of a flow)
244 //    public String findPattern(Map<String, List<Integer>> hostnameToPacketLengths, String smartPlugIp) {
245 //
246 //        // No difference, output "Complete match"
247 //        // If difference, output <Packet no, deviation from expected> for each packet
248 //        return null;
249 //    }
250 //
251 //    public static void main(String[] args) {
252 //        System.out.println("it works");
253 //
254 //        //String file = "/home/rtrimana/pcap_processing/smart_home_traffic/Code/Projects/SmartPlugDetector/pcap/wlan1.local.dns.pcap";
255 //        String file = "/home/rtrimana/pcap_processing/smart_home_traffic/Code/Projects/SmartPlugDetector/pcap/wlan1.remote.dns.pcap";
256 //
257 //        try {
258 //            Main main = new Main(file);
259 //            main.mapHostnamesToIPAddresses();
260 //
261 //            /*Pcap data = Pcap.fromFile(file);
262 //            List<Pcap.Packet> listPacket = data.packets();
263 //            System.out.println("Number of packets: " + listPacket.size());
264 //            System.out.println("===================");
265 //            for(Pcap.Packet packet : listPacket) {
266 //                if (packet._root().hdr().network() == Pcap.Linktype.ETHERNET) {
267 //                    EthernetFrame eFrame = (EthernetFrame) packet.body();
268 //                    if (eFrame.etherType() == EthernetFrame.EtherTypeEnum.IPV4) {
269 //                        Ipv4Packet ip4Packet = (Ipv4Packet) eFrame.body();
270 //                        byte[] srcIp = ip4Packet.srcIpAddr();
271 //                        byte[] dstIp = ip4Packet.dstIpAddr();
272 //                        System.out.println("Byte length source: " + srcIp.length + " Byte length dest: " + dstIp.length);
273 //                        System.out.print("Source: ");
274 //                        for(int i = 0; i < srcIp.length; i++) {
275 //                            System.out.print(Byte.toUnsignedInt(srcIp[i]));
276 //                            if(i < srcIp.length-1)
277 //                                System.out.print(".");
278 //                        }
279 //                        System.out.print(" - Dest: ");
280 //                        for(int i = 0; i < dstIp.length; i++) {
281 //                            System.out.print(Byte.toUnsignedInt(dstIp[i]));
282 //                            if(i < dstIp.length-1)
283 //                                System.out.print(".");
284 //                            else
285 //                                System.out.println("\n");
286 //                        }
287 //                    }
288 //                }
289 //            }*/
290 //
291 //        } catch (Exception e) {
292 //            e.printStackTrace();
293 //        }
294 //    }
295 }