32e9dcbd3b0e87a8520414305a69cb3c120f4a6a
[anna.git] / main.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <pcap.h>
10 #include <stdlib.h>
11 #include <netinet/ip.h>
12 #include <arpa/inet.h>
13 #include <iostream>
14 #include <fstream>
15
16 // STL
17 #include <string>
18 #include <map>
19
20 #include <anna/core/DataBlock.hpp>
21 #include <anna/core/util/Tokenizer.hpp>
22 #include <anna/core/functions.hpp>
23 #include <anna/core/tracing/Logger.hpp>
24 #include <anna/core/tracing/TraceWriter.hpp>
25 #include <anna/core/RuntimeException.hpp>
26
27
28 using namespace anna;
29
30 // Payload and frame metadata /////////////////////////////////////////////////////////////////////////////
31 class Payload {
32
33   std::string _sourceIP;
34   std::string _destinationIP;
35   time_t _timestamp;
36   int _timestampU; // usecs
37   std::string _data;
38   size_t _diameterLength;
39
40 public:
41   Payload() {
42     reset();
43   }
44
45   void setDiameterLength(size_t dl) {
46     if(_diameterLength == -1) {
47       _diameterLength = dl;
48       LOGDEBUG(
49         Logger::debug(anna::functions::asString("Diameter message length: %d bytes", dl), ANNA_FILE_LOCATION));
50     }
51   }
52
53   void setSourceIP(const std::string &srcIP) throw() {
54     _sourceIP = srcIP;
55   }
56   void setDestinationIP(const std::string &dstIP) throw() {
57     _destinationIP = dstIP;
58   }
59   void setTimestamp(time_t ts) throw() {
60     _timestamp = ts;
61   }
62   void setTimestampU(int tsu) throw() {
63     _timestampU = tsu;
64   }
65   // Returns true if completed:
66   bool appendData(const char *data, size_t size) throw(RuntimeException) {
67     LOGDEBUG(
68       Logger::debug(anna::functions::asString("Appending %d bytes", size), ANNA_FILE_LOCATION));
69     _data.append(data, size);
70
71     if(_data.size() > _diameterLength)
72       throw RuntimeException(
73         "Data overflow (unexpected offset exceed diameter message length)",
74         ANNA_FILE_LOCATION);
75
76     if(_data.size() < _diameterLength)
77       return false;
78
79     LOGDEBUG(anna::Logger::debug("Completed!", ANNA_FILE_LOCATION));
80     return true;
81   }
82
83   void reset() throw() {
84     _sourceIP = "";
85     _destinationIP = "";
86     _timestamp = 0;
87     _timestampU = 0;
88     _data = "";
89     _diameterLength = -1; // not calculated yet
90   }
91
92   const std::string &getSourceIP() const throw() {
93     return _sourceIP;
94   }
95   const std::string &getDestinationIP() const throw() {
96     return _destinationIP;
97   }
98   time_t getTimestamp() const throw() {
99     return _timestamp;
100   }
101   int getTimestampU() const throw() {
102     return _timestampU;
103   }
104   const std::string &getData() const throw() {
105     return _data;
106   }
107   std::string getDataAsHex() const throw() {
108     return anna::functions::asHexString(
109              anna::DataBlock(_data.c_str(), _data.size()));
110   }
111 };
112
113 // Data maps //////////////////////////////////////////////////////////////////////////////////////////////
114 typedef std::map < int /* frame */, Payload > payloads_t;
115 typedef std::map < int /* frame */, Payload >::const_iterator payloads_it;
116 payloads_t G_payloads;
117
118 // Sniffing structures ////////////////////////////////////////////////////////////////////////////////////
119
120 /* ethernet headers are always exactly 14 bytes */
121 #define SIZE_ETHERNET 14
122 /* Ethernet addresses are 6 bytes */
123 #define ETHER_ADDR_LEN  6
124
125 /* Ethernet header */
126 struct sniff_ethernet {
127   u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
128   u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
129   u_short ether_type; /* IP? ARP? RARP? etc */
130 };
131
132 /* IP header */
133 struct sniff_ip {
134   u_char ip_vhl; /* version << 4 | header length >> 2 */
135   u_char ip_tos; /* type of service */
136   u_short ip_len; /* total length */
137   u_short ip_id; /* identification */
138   u_short ip_off; /* fragment offset field */
139 #define IP_RF 0x8000    /* reserved fragment flag */
140 #define IP_DF 0x4000    /* dont fragment flag */
141 #define IP_MF 0x2000    /* more fragments flag */
142 #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
143   u_char ip_ttl; /* time to live */
144   u_char ip_p; /* protocol */
145   u_short ip_sum; /* checksum */
146   struct in_addr ip_src, ip_dst; /* source and dest address */
147 };
148 #define IP_OFF(ip)              (((ip)->ip_off) & IP_OFFMASK)
149 #define IP_DF_VAL(ip)           ((((ip)->ip_off) & IP_DF) >> 14)
150 #define IP_MF_VAL(ip)           ((((ip)->ip_off) & IP_MF) >> 13)
151 #define IP_HL(ip)   (((ip)->ip_vhl) & 0x0f)
152 #define IP_V(ip)    (((ip)->ip_vhl) >> 4)
153
154 /* TCP header */
155 typedef u_int tcp_seq;
156
157 struct sniff_tcp {
158   u_short th_sport; /* source port */
159   u_short th_dport; /* destination port */
160   tcp_seq th_seq; /* sequence number */
161   tcp_seq th_ack; /* acknowledgement number */
162   u_char th_offx2; /* data offset, rsvd */
163 #define TH_OFF(th)  (((th)->th_offx2 & 0xf0) >> 4)
164   u_char th_flags;
165 #define TH_FIN 0x01
166 #define TH_SYN 0x02
167 #define TH_RST 0x04
168 #define TH_PUSH 0x08
169 #define TH_ACK 0x10
170 #define TH_URG 0x20
171 #define TH_ECE 0x40
172 #define TH_CWR 0x80
173 #define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
174   u_short th_win; /* window */
175   u_short th_sum; /* checksum */
176   u_short th_urp; /* urgent pointer */
177 };
178
179 // Payload extraction /////////////////////////////////////////////////////////////////////////////////////
180 u_char *getPayload(const u_char* packet, int packetSize, int &payloadSize,
181                    std::string &srcIp, std::string &dstIp, int &fragmentId, bool &dfFlag,
182                    bool &mfFlag, int &fragmentOffset) {
183   const struct sniff_ethernet *ethernet; /* The ethernet header */
184   const struct sniff_ip *ip; /* The IP header */
185   const struct sniff_tcp *tcp; /* The TCP header */
186   u_int size_ip;
187   u_int size_tcp;
188   ethernet = (struct sniff_ethernet*)(packet);
189   ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
190   size_ip = IP_HL(ip) * 4; // 4 bytes per 32 bits word
191
192   if(size_ip < 20) {
193     LOGDEBUG(
194       Logger::debug(anna::functions::asString("Invalid IP header length: %d bytes", size_ip), ANNA_FILE_LOCATION));
195     return NULL;
196   }
197
198   static char str[INET_ADDRSTRLEN];
199   inet_ntop(AF_INET, &(ip->ip_src), str, INET_ADDRSTRLEN);
200   srcIp = str;
201   inet_ntop(AF_INET, &(ip->ip_dst), str, INET_ADDRSTRLEN);
202   dstIp = str;
203   LOGDEBUG(
204     Logger::debug(anna::functions::asString("ip_id: %d | ip_off: %d", ip->ip_id, ip->ip_off), ANNA_FILE_LOCATION));
205   fragmentId = ip->ip_id;
206   dfFlag = IP_DF_VAL(ip);
207   mfFlag = IP_MF_VAL(ip);
208   fragmentOffset = IP_OFF(ip);
209   tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
210   size_tcp = TH_OFF(tcp) * 4;
211
212   if(size_tcp < 20) {
213     LOGDEBUG(
214       Logger::debug(anna::functions::asString("Invalid TCP header length: %d bytes", size_tcp), ANNA_FILE_LOCATION));
215     return NULL;
216   }
217
218   int payloadOffset = SIZE_ETHERNET + size_ip + size_tcp;
219   LOGDEBUG(
220     Logger::debug(anna::functions::asString("PayloadOffset=%d", payloadOffset), ANNA_FILE_LOCATION));
221   payloadSize = packetSize - payloadOffset;
222   return ((u_char *)(packet + payloadOffset));
223 }
224
225 // Sniffing callback //////////////////////////////////////////////////////////////////////////////////////
226 void my_callback(u_char *useless, const struct pcap_pkthdr* pkthdr,
227                  const u_char* packet) {
228   static int count = 1;
229   static Payload auxPayload;
230   int packetSize = pkthdr->len;
231   int payloadSize;
232   std::string srcIp, dstIp;
233   int fragmentId, fragmentOffset;
234   bool dfFlag, mfFlag;
235   const u_char* payload = getPayload(packet, packetSize, payloadSize, srcIp,
236                                      dstIp, fragmentId, dfFlag, mfFlag, fragmentOffset);
237
238   if(payload && (payloadSize > 0)) {
239     LOGDEBUG(
240       std::string msg; msg += anna::functions::asString("\nFrame %d:", count); msg += anna::functions::asHexString(anna::DataBlock((const char *)packet, pkthdr->len)); time_t time = pkthdr->ts.tv_sec; msg += "\n"; msg += anna::functions::asString("\ntimestamp %d.%d", pkthdr->ts.tv_sec, pkthdr->ts.tv_usec); msg += anna::functions::asString("\ndate %s", ctime(&time)); msg += anna::functions::asString("\ncaplen %d", pkthdr->caplen); msg += anna::functions::asString("\npacketSize %d", packetSize); msg += anna::functions::asString("\npayloadSize %d", payloadSize); msg += "\nPayload:"; msg += anna::functions::asHexString(anna::DataBlock((const char *)payload, payloadSize)); msg += "\n"; msg += anna::functions::asString("\nsourceIP %s", srcIp.c_str()); msg += anna::functions::asString("\ndestinationIP %s", dstIp.c_str()); msg += "\n"; msg += anna::functions::asString("\nfragmentId %d:", fragmentId); msg += anna::functions::asString("\nDF %s:", (dfFlag ? "1" : "0")); msg += anna::functions::asString("\nMF %s:", (mfFlag ? "1" : "0")); msg += anna::functions::asString("\nfragmentOffset %d:", fragmentOffset);
241       Logger::debug(msg, ANNA_FILE_LOCATION););
242     auxPayload.setDiameterLength(
243       (payload[1] << 16) + (payload[2] << 8) + payload[3]);
244     auxPayload.setSourceIP(srcIp);
245     auxPayload.setDestinationIP(dstIp);
246     auxPayload.setTimestamp(pkthdr->ts.tv_sec);
247     auxPayload.setTimestampU(pkthdr->ts.tv_usec);
248     bool completed = auxPayload.appendData((const char *) payload, payloadSize);
249
250     if(completed) {
251       G_payloads[count] = auxPayload;
252       auxPayload.reset();
253     }
254   }
255
256   count++;
257 }
258
259 bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) throw() {
260   // Get hex string
261   static char buffer[8192];
262   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
263
264   if(infile.is_open()) {
265     infile >> buffer;
266     std::string hexString(buffer, strlen(buffer));
267     // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
268     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
269     LOGDEBUG(
270       std::string msg = "Hex string (remove colons if exists): ";
271       msg += hexString;
272       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
273     );
274     anna::functions::fromHexString(hexString, db);
275     // Close file
276     infile.close();
277     return true;
278   }
279
280   return false;
281 }
282
283
284 void _exit(const std::string &message, int resultCode = 1) {
285   if(resultCode)
286     std::cerr << message << std::endl << std::endl;
287   else
288     std::cout << message << std::endl << std::endl;
289
290   exit(resultCode);
291 }
292
293
294 //-------------------------------------------------------------------
295 int main(int argc, char **argv) {
296   std::string exec = argv[0];
297   std::string execBN = exec.substr(exec.find_last_of("/") + 1);
298   std::string filetrace = execBN + ".trace";
299   std::cout << std::endl;
300
301   //check command line arguments
302   if(argc < 2) {
303     std::string msg = "Usage: "; msg += exec;
304     msg += " <pcap file> [--write-hex: to write hex files] [--debug: activates debug level traces (warning by default)]\n\n";
305     _exit(msg);
306   }
307
308   // Command-line parameters:
309   std::string inputFile = argv[1];
310   bool isHex = (inputFile.substr(inputFile.find_last_of(".") + 1) == "hex");
311   std::string outputFile = inputFile; // extension will be added later
312   std::string optionals;
313   int indx = 2;
314   while(indx < argc) { optionals += " "; optionals += argv[indx]; indx++; }
315
316   bool debug = (optionals.find("--debug") != std::string::npos);
317   bool writeHex = (optionals.find("--write-hex") != std::string::npos);
318   Logger::setLevel(debug ? Logger::Debug:Logger::Warning);
319   Logger::initialize(execBN.c_str(), new TraceWriter(filetrace.c_str(), 2048000));
320
321   anna::DataBlock db_aux(true);
322
323   // SNIFFING //////////////////////////////////////////////////////////////////////////////////////////////7
324   //temporary packet buffers
325   struct pcap_pkthdr header; // The header that pcap gives us
326   const u_char *packet;      // The actual packet
327   //------------------
328   //open the pcap file
329   pcap_t *handle;
330   char errbuf[PCAP_ERRBUF_SIZE];        //not sure what to do with this, oh well
331   handle = pcap_open_offline(inputFile.c_str(), errbuf); //call pcap library function
332
333   if(handle == NULL) _exit(errbuf, 2);
334
335   // TODO: add filtering. At the moment, pcap must be previously filtered for diameter protocol ('tcp port 3868' or any other filter allowed)
336   /*
337   // Filtering:
338   std::string filter = ?????;
339   struct bpf_program _fp;
340   struct bpf_program *fp = &_fp;
341   bpf_u_int32 netmask = 4294967295; // FFFFFFFF
342
343   if (pcap_compile(handle, fp, (char*)(filter.c_str()), 1, netmask) == -1) {
344         std::cerr << "Couldn't compile the filter " << filter << std::endl;
345     return(2); 
346   }
347
348   if (pcap_setfilter(handle, fp) == -1) {
349     std::cerr << "Couldn't set the filter " << filter << std::endl; 
350     return(2); 
351   }
352   */
353
354   //begin processing the packets in this particular file
355   int packets = -1;
356
357   try {
358     while(packets != 0)
359       packets = pcap_dispatch(handle, -1, (pcap_handler) my_callback, NULL);
360   } catch(RuntimeException &ex) {
361     _exit(ex.asString());
362   }
363
364   pcap_close(handle);  //close the pcap file
365
366   // Print payloads //////////////////////////////////////////////////////////////////////////////////////////////
367   // Open output file:
368   outputFile += ".report";
369   std::ofstream out(outputFile.c_str(), std::ifstream::out);
370
371   for(payloads_it it = G_payloads.begin(); it != G_payloads.end(); it++) {
372     LOGDEBUG(
373       Logger::debug(anna::functions::asString("Dumping frame %d", it->first), ANNA_FILE_LOCATION));
374     time_t ts = (it->second).getTimestamp();
375     int tsu = (it->second).getTimestampU();
376     std::string ts_str = ctime(&ts);
377     ts_str.erase(ts_str.find("\n"));
378     out << "Frame:           " << anna::functions::asString(it->first) << std::endl;
379     out << "Date:            " << ts_str << std::endl;
380     out << "Timestamp:       " << anna::functions::asString((int)ts) << "."
381         << anna::functions::asString((int)tsu) << std::endl;
382     out << "Origin IP:       " << (it->second).getSourceIP() << std::endl;
383     out << "Destination IP:  " << (it->second).getDestinationIP() << std::endl;
384     out << "Destination IP:  " << (it->second).getDestinationIP() << std::endl;
385     out << "Hex String:      " << (it->second).getDataAsHex() << std::endl;
386
387     // Create hex file:
388     if (writeHex) {
389       std::string hexFile =  anna::functions::asString(it->first) + ".hex";
390       std::ofstream hex(hexFile.c_str(), std::ifstream::out);
391       hex << (it->second).getDataAsHex();
392       hex.close();
393     }
394
395     out << std::endl;
396   }
397
398   // Close output file:
399   out.close();
400   std::string msg = "Open '"; msg += filetrace; msg += "' in order to see process traces.\n";
401   msg += "Open '"; msg += outputFile; msg += "' to see decoding results.\n";
402   if (writeHex) msg += "Open '<frame number>.hex' to see specific frame data.";
403   _exit(msg, 0);
404 }
405