/*************************************************************************** * PacketParser.cc -- The PacketParser Class offers methods to parse * * received network packets. Its main purpose is to facilitate the * * conversion of raw sequences of bytes into chains of objects of the * * PacketElement family. * * * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * * The Nmap Security Scanner is (C) 1996-2010 Insecure.Com LLC. Nmap is * * also a registered trademark of Insecure.Com LLC. This program is free * * software; you may redistribute and/or modify it under the terms of the * * GNU General Public License as published by the Free Software * * Foundation; Version 2 with the clarifications and exceptions described * * below. This guarantees your right to use, modify, and redistribute * * this software under certain conditions. If you wish to embed Nmap * * technology into proprietary software, we sell alternative licenses * * (contact sales@insecure.com). Dozens of software vendors already * * license Nmap technology such as host discovery, port scanning, OS * * detection, and version detection. * * * * Note that the GPL places important restrictions on "derived works", yet * * it does not provide a detailed definition of that term. To avoid * * misunderstandings, we consider an application to constitute a * * "derivative work" for the purpose of this license if it does any of the * * following: * * o Integrates source code from Nmap * * o Reads or includes Nmap copyrighted data files, such as * * nmap-os-db or nmap-service-probes. * * o Executes Nmap and parses the results (as opposed to typical shell or * * execution-menu apps, which simply display raw Nmap output and so are * * not derivative works.) * * o Integrates/includes/aggregates Nmap into a proprietary executable * * installer, such as those produced by InstallShield. * * o Links to a library or executes a program that does any of the above * * * * The term "Nmap" should be taken to also include any portions or derived * * works of Nmap. This list is not exclusive, but is meant to clarify our * * interpretation of derived works with some common examples. Our * * interpretation applies only to Nmap--we don't speak for other people's * * GPL works. * * * * If you have any questions about the GPL licensing restrictions on using * * Nmap in non-GPL works, we would be happy to help. As mentioned above, * * we also offer alternative license to integrate Nmap into proprietary * * applications and appliances. These contracts have been sold to dozens * * of software vendors, and generally include a perpetual license as well * * as providing for priority support and updates as well as helping to * * fund the continued development of Nmap technology. Please email * * sales@insecure.com for further information. * * * * As a special exception to the GPL terms, Insecure.Com LLC grants * * permission to link the code of this program with any version of the * * OpenSSL library which is distributed under a license identical to that * * listed in the included docs/licenses/OpenSSL.txt file, and distribute * * linked combinations including the two. You must obey the GNU GPL in all * * respects for all of the code used other than OpenSSL. If you modify * * this file, you may extend this exception to your version of the file, * * but you are not obligated to do so. * * * * If you received these files with a written license agreement or * * contract stating terms other than the terms above, then that * * alternative license agreement takes precedence over these comments. * * * * Source is provided to this software because we believe users have a * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes (none * * have been found so far). * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to send your changes * * to nmap-dev@insecure.org for possible incorporation into the main * * distribution. By sending these changes to Fyodor or one of the * * Insecure.Org development mailing lists, it is assumed that you are * * offering the Nmap Project (Insecure.Com LLC) the unlimited, * * non-exclusive right to reuse, modify, and relicense the code. Nmap * * will always be available Open Source, but this is important because the * * inability to relicense code has caused devastating problems for other * * Free Software projects (such as KDE and NASM). We also occasionally * * relicense the code to third parties as discussed above. If you wish to * * specify special license conditions of your contributions, just say so * * when you send them. * * * * This program 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 v2.0 for more details at * * http://www.gnu.org/licenses/gpl-2.0.html , or in the COPYING file * * included with Nmap. * * * ***************************************************************************/ #include "PacketParser.h" #include "nping.h" #include "output.h" PacketParser::PacketParser() { this->reset(); } /* End of PacketParser constructor */ PacketParser::~PacketParser() { } /* End of PacketParser destructor */ /** Sets every attribute to its default value- */ void PacketParser::reset() { } /* End of PacketParser destructor */ const char *PacketParser::header_type2string(int val){ header_type_string_t header_types[]={ {HEADER_TYPE_IPv6_HOPOPT, "IPv6 Hop-by-Hop"}, {HEADER_TYPE_ICMPv4,"ICMPv4"}, {HEADER_TYPE_IGMP,"IGMP"}, {HEADER_TYPE_IPv4,"IPv4"}, {HEADER_TYPE_TCP,"TCP"}, {HEADER_TYPE_EGP,"EGP"}, {HEADER_TYPE_UDP,"UDP"}, {HEADER_TYPE_IPv6,"IPv6"}, {HEADER_TYPE_IPv6_ROUTE,"IPv6-Route"}, {HEADER_TYPE_IPv6_FRAG,"IPv6-Frag"}, {HEADER_TYPE_GRE,"GRE"}, {HEADER_TYPE_ESP,"ESP"}, {HEADER_TYPE_AH,"AH"}, {HEADER_TYPE_ICMPv6,"ICMPv6"}, {HEADER_TYPE_IPv6_NONXT,"IPv6-NoNxt"}, {HEADER_TYPE_IPv6_OPTS,"IPv6-Opts"}, {HEADER_TYPE_EIGRP,"EIGRP"}, {HEADER_TYPE_ETHERNET,"Ethernet"}, {HEADER_TYPE_L2TP,"L2TP"}, {HEADER_TYPE_SCTP,"SCTP"}, {HEADER_TYPE_IPv6_MOBILE,"Mobility Header"}, {HEADER_TYPE_MPLS_IN_IP,"MPLS-in-IP"}, {HEADER_TYPE_ARP,"ARP"}, {HEADER_TYPE_RAW_DATA,"Raw Data"}, {0,NULL} }; int i=0; for(i=0; header_types[i].str!=NULL; i++ ){ if((int)header_types[i].type==val) return header_types[i].str; } return NULL; } /* End of header_type2string() */ #define MAX_HEADERS_IN_PACKET 32 pkt_type_t *PacketParser::parse_packet(const u8 *pkt, size_t pktlen, bool eth_included){ outPrint(DBG_4, "%s(%p, %lu)", __func__, pkt, (long unsigned)pktlen); static pkt_type_t this_packet[MAX_HEADERS_IN_PACKET+1]; /* Packet structure array */ u8 current_header=0; /* Current array position of "this_packet" */ const u8 *curr_pkt=pkt; /* Pointer to current part of the packet */ size_t curr_pktlen=pktlen; /* Remaining packet length */ int ethlen=0, arplen=0; /* Aux length variables: link layer */ int iplen=0,ip6len=0; /* Aux length variables: network layer */ int tcplen=0,udplen=0,icmplen=0; /* Aux length variables: transport layer */ int next_layer=0; /* Next header type to process */ int expected=0; /* Next protocol expected */ bool finished=false; /* Loop breaking flag */ bool unknown_hdr=false; /* Indicates unknown header found */ IPv4Header ip4; IPv6Header ip6; TCPHeader tcp; UDPHeader udp; ICMPv4Header icmp4; ICMPv6Header icmp6; EthernetHeader eth; ARPHeader arp; memset(this_packet, 0, sizeof(this_packet)); /* Decide which layer we have to start from */ if( eth_included ){ next_layer=LINK_LAYER; expected=HEADER_TYPE_ETHERNET; }else{ next_layer=NETWORK_LAYER; } /* Header processing loop */ while(!finished && curr_pktlen>0 && current_header0){ next_layer=APPLICATION_LAYER; expected=HEADER_TYPE_RAW_DATA; }else{ finished=true; } }else{ assert(finished==true); } /* IPv4 and IPv6 headers **************************************************/ }else if(next_layer==NETWORK_LAYER){ outPrint(DBG_4, "Next Layer=Network"); /* Determine IP version */ if (ip4.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ unknown_hdr=true; break; } /* IP version 4 ---------------------------------*/ if(ip4.getVersion()==4){ if( (iplen=ip4.validate())==OP_FAILURE){ unknown_hdr=true; break; } /* Determine next header type */ switch(ip4.getNextProto()){ case HEADER_TYPE_ICMPv4: next_layer=TRANSPORT_LAYER; expected=HEADER_TYPE_ICMPv4; break; case HEADER_TYPE_IPv4: /* IP in IP */ next_layer=NETWORK_LAYER; expected=HEADER_TYPE_IPv4; break; case HEADER_TYPE_TCP: next_layer=TRANSPORT_LAYER; expected=HEADER_TYPE_TCP; break; case HEADER_TYPE_UDP: next_layer=TRANSPORT_LAYER; expected=HEADER_TYPE_UDP; break; case HEADER_TYPE_IPv6: /* IPv6 in IPv4 */ next_layer=NETWORK_LAYER; expected=HEADER_TYPE_IPv6; break; default: next_layer=APPLICATION_LAYER; expected=HEADER_TYPE_RAW_DATA; break; } this_packet[current_header].length=iplen; this_packet[current_header++].type=HEADER_TYPE_IPv4; ip4.reset(); curr_pkt+=iplen; curr_pktlen-=iplen; /* IP version 6 ---------------------------------*/ }else if(ip4.getVersion()==6){ ip4.reset(); if (ip6.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ unknown_hdr=true; break; } if( (ip6len=ip6.validate())==OP_FAILURE ){ unknown_hdr=true; break; } switch( ip6.getNextHeader() ){ case HEADER_TYPE_ICMPv6: next_layer=TRANSPORT_LAYER; expected=HEADER_TYPE_ICMPv6; break; case HEADER_TYPE_IPv4: /* IPv4 in IPv6 */ next_layer=NETWORK_LAYER; expected=HEADER_TYPE_IPv4; break; case HEADER_TYPE_TCP: next_layer=TRANSPORT_LAYER; expected=HEADER_TYPE_TCP; break; case HEADER_TYPE_UDP: next_layer=TRANSPORT_LAYER; expected=HEADER_TYPE_UDP; break; case HEADER_TYPE_IPv6: /* IPv6 in IPv6 */ next_layer=NETWORK_LAYER; expected=HEADER_TYPE_IPv6; break; /* TODO: ADD here IPv6 options, fragmentation, hop-by-hop, etc */ default: next_layer=APPLICATION_LAYER; expected=HEADER_TYPE_RAW_DATA; break; } this_packet[current_header].length=ip6len; this_packet[current_header++].type=HEADER_TYPE_IPv6; ip6.reset(); curr_pkt+=ip6len; curr_pktlen-=ip6len; /* Bogus IP version -----------------------------*/ }else{ /* Wrong IP version, treat as raw data. */ next_layer=APPLICATION_LAYER; expected=HEADER_TYPE_RAW_DATA; } /* TCP, UDP, ICMPv4 and ICMPv6 headers ************************************/ }else if(next_layer==TRANSPORT_LAYER){ outPrint(DBG_4, "Next Layer=Transport"); if(expected==HEADER_TYPE_TCP){ outPrint(DBG_4, "Expected Layer=TCP"); if(tcp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ unknown_hdr=true; break; } if( (tcplen=tcp.validate())==OP_FAILURE){ unknown_hdr=true; break; } switch( tcp.getDestinationPort() ){ //case 21: // expected=HEADER_TYPE_FTP; //break; //case 80: // expected=HEADER_TYPE_HTTP; //break; default: expected=HEADER_TYPE_RAW_DATA; break; } this_packet[current_header].length=tcplen; this_packet[current_header++].type=HEADER_TYPE_TCP; tcp.reset(); curr_pkt+=tcplen; curr_pktlen-=tcplen; next_layer=APPLICATION_LAYER; }else if(expected==HEADER_TYPE_UDP){ outPrint(DBG_4, "Expected Layer=UDP"); if(udp.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ unknown_hdr=true; break; } if( (udplen=udp.validate())==OP_FAILURE){ unknown_hdr=true; break; } switch( udp.getDestinationPort() ){ //case 53: // expected=HEADER_TYPE_DNS; //break; default: expected=HEADER_TYPE_RAW_DATA; break; } this_packet[current_header].length=udplen; this_packet[current_header++].type=HEADER_TYPE_UDP; udp.reset(); curr_pkt+=udplen; curr_pktlen-=udplen; next_layer=APPLICATION_LAYER; }else if(expected==HEADER_TYPE_ICMPv4){ outPrint(DBG_4, "Expected Layer=ICMPv4"); if(icmp4.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE ){ unknown_hdr=true; break; } if( (icmplen=icmp4.validate())==OP_FAILURE){ unknown_hdr=true; break; } switch( icmp4.getType() ){ /* Types that include an IPv4 packet as payload */ case ICMP_UNREACH: case ICMP_TIMXCEED: case ICMP_PARAMPROB: case ICMP_SOURCEQUENCH: case ICMP_REDIRECT: next_layer=NETWORK_LAYER; expected=HEADER_TYPE_IPv4; break; /* ICMP types that include misc payloads (or no payload) */ default: expected=HEADER_TYPE_RAW_DATA; next_layer=APPLICATION_LAYER; break; } this_packet[current_header].length=icmplen; this_packet[current_header++].type=HEADER_TYPE_ICMPv4; icmp4.reset(); curr_pkt+=icmplen; curr_pktlen-=icmplen; }else if(expected==HEADER_TYPE_ICMPv6){ outPrint(DBG_4, "Expected Layer=ICMPv6"); if(icmp6.storeRecvData(curr_pkt, curr_pktlen)==OP_FAILURE){ unknown_hdr=true; break; } if( (icmplen=icmp6.validate())==OP_FAILURE){ unknown_hdr=true; break; } switch( icmp6.getType() ){ /* Types that include an IPv6 packet as payload */ case ICMPv6_UNREACH: case ICMPv6_PKTTOOBIG: case ICMPv6_TIMXCEED: case ICMPv6_PARAMPROB: next_layer=NETWORK_LAYER; expected=HEADER_TYPE_IPv6; break; /* ICMPv6 types that include misc payloads (or no payload) */ default: expected=HEADER_TYPE_RAW_DATA; next_layer=APPLICATION_LAYER; break; } this_packet[current_header].length=icmplen; this_packet[current_header++].type=HEADER_TYPE_ICMPv6; icmp6.reset(); curr_pkt+=icmplen; curr_pktlen-=icmplen; }else{ /* Wrong application layer protocol, treat as raw data. */ next_layer=APPLICATION_LAYER; expected=HEADER_TYPE_RAW_DATA; } /* Miscellaneous payloads *************************************************/ }else{ // next_layer==APPLICATION_LAYER outPrint(DBG_4, "Next Layer=Application"); if(curr_pktlen>0){ //if(expected==HEADER_TYPE_DNS){ //}else if(expected==HEADER_TYPE_HTTP){ //}... ETC this_packet[current_header].length=curr_pktlen; this_packet[current_header++].type=HEADER_TYPE_RAW_DATA; curr_pktlen=0; } finished=true; } } /* End of header processing loop */ /* If we couldn't validate some header, treat that header and any remaining * data, as raw application data. */ if (unknown_hdr==true){ if(curr_pktlen>0){ outPrint(DBG_4, "Unknown layer found. Treating it as raw data."); this_packet[current_header].length=curr_pktlen; this_packet[current_header++].type=HEADER_TYPE_RAW_DATA; } } return this_packet; } /* End of parse_received_packet() */ /* TODO: remove */ int PacketParser::dummy_print_packet_type(const u8 *pkt, size_t pktlen, bool eth_included){ pkt_type_t *packetheaders=PacketParser::parse_packet(pkt, pktlen, eth_included); for(int i=0; packetheaders[i].length!=0; i++){ printf("%s:", header_type2string(packetheaders[i].type)); } printf("\n"); return OP_SUCCESS; } /* End of dummy_print_packet_type() */ int PacketParser::dummy_print_packet(const u8 *pkt, size_t pktlen, bool eth_included){ PacketElement *me=NULL, *aux=NULL; if( (me=split(pkt, pktlen, eth_included))==NULL ) return OP_FAILURE; else{ me->print(stdout, PRINT_DETAIL_HIGH); printf("\n"); } /* Free the structs */ while(me!=NULL){ aux=me->getNextElement(); delete me; me=aux; } return OP_SUCCESS; } /* End of dummy_print_packet() */ /** For a given packet, this method determines where the application layer data * begins. It returs a positive offset if any application data was found, zero * if the packet did not contain application data and a negative integer in * case of error. */ int PacketParser::payload_offset(const u8 *pkt, size_t pktlen, bool link_included){ PacketElement *me=NULL, *aux=NULL; size_t offset=pktlen; /* Initially, point to the end of the packet. */ /* Safe checks*/ if(pkt==NULL || pktlen<=0) return -1; dummy_print_packet_type(pkt, pktlen, link_included); /* Split the packet into separate protocol headers */ if( (me=split(pkt, pktlen, link_included))==NULL ) return -2; else{ aux=me; } /* Find if there is application data and where it begins */ while(me!=NULL){ /* When we find application data, we compute the offset by substacting the length of the application data from the packet's total length */ if(me->protocol_id()==HEADER_TYPE_RAW_DATA){ offset=pktlen-me->getLen(); break; me=me->getNextElement(); }else{ me=me->getNextElement(); } } /* Free the structs */ me=aux; while(me!=NULL){ aux=me->getNextElement(); delete me; me=aux; } /* Return 0 if we didn't find any application data */ if(offset==pktlen){ return 0; }else{ return offset; } } /* End of payload_offset() */ PacketElement *PacketParser::split(const u8 *pkt, size_t pktlen){ return split(pkt, pktlen, false); } /* End of split() */ PacketElement *PacketParser::split(const u8 *pkt, size_t pktlen, bool eth_included){ pkt_type_t *packetheaders=NULL; const u8 *curr_pkt=pkt; PacketElement *first=NULL; PacketElement *last=NULL; IPv4Header *ip4=NULL; IPv6Header *ip6=NULL; TCPHeader *tcp=NULL; UDPHeader *udp=NULL; ICMPv4Header *icmp4=NULL; ICMPv6Header *icmp6=NULL; EthernetHeader *eth=NULL; ARPHeader *arp=NULL; RawData *raw=NULL; /* Analyze the packet. This returns a list of header types and lengths */ if((packetheaders=PacketParser::parse_packet(pkt, pktlen, eth_included))==NULL) return NULL; /* Store each header in its own PacketHeader object type */ for(int i=0; packetheaders[i].length!=0; i++){ switch(packetheaders[i].type){ case HEADER_TYPE_ETHERNET: eth=new EthernetHeader(); eth->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=eth; }else{ last->setNextElement(eth); } last=eth; break; case HEADER_TYPE_ARP: arp=new ARPHeader(); arp->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=arp; }else{ last->setNextElement(arp); } last=arp; break; case HEADER_TYPE_IPv4: ip4=new IPv4Header(); ip4->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=ip4; }else{ last->setNextElement(ip4); } last=ip4; break; case HEADER_TYPE_IPv6: ip6=new IPv6Header(); ip6->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=ip6; }else{ last->setNextElement(ip6); } last=ip6; break; case HEADER_TYPE_TCP: tcp=new TCPHeader(); tcp->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=tcp; }else{ last->setNextElement(tcp); } last=tcp; break; case HEADER_TYPE_UDP: udp=new UDPHeader(); udp->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=udp; }else{ last->setNextElement(udp); } last=udp; break; case HEADER_TYPE_ICMPv4: icmp4=new ICMPv4Header(); icmp4->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=icmp4; }else{ last->setNextElement(icmp4); } last=icmp4; break; case HEADER_TYPE_ICMPv6: icmp6=new ICMPv6Header(); icmp6->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=icmp6; }else{ last->setNextElement(icmp6); } last=icmp6; break; case HEADER_TYPE_RAW_DATA: default: raw=new RawData(); raw->storeRecvData(curr_pkt, packetheaders[i].length); if(first==NULL){ first=raw; }else{ last->setNextElement(raw); } last=raw; break; } curr_pkt+=packetheaders[i].length; } return first; } /* End of split() */