diff options
Diffstat (limited to 'nping/ProbeMode.cc')
-rw-r--r-- | nping/ProbeMode.cc | 2342 |
1 files changed, 2342 insertions, 0 deletions
diff --git a/nping/ProbeMode.cc b/nping/ProbeMode.cc new file mode 100644 index 0000000..b8887a8 --- /dev/null +++ b/nping/ProbeMode.cc @@ -0,0 +1,2342 @@ + +/*************************************************************************** + * ProbeMode.cc -- Probe Mode is nping's default working mode. Basically, * + * it involves sending the packets that the user requested at regular * + * intervals and capturing responses from the wire. * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * + * The Nmap Security Scanner is (C) 1996-2023 Nmap Software LLC ("The Nmap + * Project"). Nmap is also a registered trademark of the Nmap Project. + * + * This program is distributed under the terms of the Nmap Public Source + * License (NPSL). The exact license text applying to a particular Nmap + * release or source code control revision is contained in the LICENSE + * file distributed with that version of Nmap or source code control + * revision. More Nmap copyright/legal information is available from + * https://nmap.org/book/man-legal.html, and further information on the + * NPSL license itself can be found at https://nmap.org/npsl/ . This + * header summarizes some key points from the Nmap license, but is no + * substitute for the actual license text. + * + * Nmap is generally free for end users to download and use themselves, + * including commercial use. It is available from https://nmap.org. + * + * The Nmap license generally prohibits companies from using and + * redistributing Nmap in commercial products, but we sell a special Nmap + * OEM Edition with a more permissive license and special features for + * this purpose. See https://nmap.org/oem/ + * + * If you have received a written Nmap license agreement or contract + * stating terms other than these (such as an Nmap OEM license), you may + * choose to use and redistribute Nmap under those terms instead. + * + * The official Nmap Windows builds include the Npcap software + * (https://npcap.com) for packet capture and transmission. It is under + * separate license terms which forbid redistribution without special + * permission. So the official Nmap Windows builds may not be redistributed + * without special permission (such as an Nmap OEM license). + * + * 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. + * + * Source code also allows you to port Nmap to new platforms, fix bugs, and add + * new features. You are highly encouraged to submit your changes as a Github PR + * or by email to the dev@nmap.org mailing list for possible incorporation into + * the main distribution. Unless you specify otherwise, it is understood that + * you are offering us very broad rights to use your submissions as described in + * the Nmap Public Source License Contributor Agreement. This is important + * because we fund the project by selling licenses with various terms, and also + * because the inability to relicense code has caused devastating problems for + * other Free Software projects (such as KDE and NASM). + * + * The free version of Nmap 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. Warranties, + * indemnification and commercial support are all available through the + * Npcap OEM program--see https://nmap.org/oem/ + * + ***************************************************************************/ + +#include "nping.h" +#include "ProbeMode.h" +#include <vector> +#include "nsock.h" +#include "output.h" +#include "NpingOps.h" + +#ifdef WIN32 +/* Need DnetName2PcapName */ +#include "libnetutil/netutil.h" +#endif + +extern NpingOps o; + + +ProbeMode::ProbeMode() { + this->reset(); +} /* End of ProbeMode constructor */ + + +ProbeMode::~ProbeMode() { +} /* End of ProbeMode destructor */ + + +/** Sets every attribute to its default value- */ +void ProbeMode::reset() { + this->nsock_init=false; +} /* End of reset() */ + + +/** Sets up the internal nsock pool and the nsock trace level */ +int ProbeMode::init_nsock(){ + struct timeval now; + if( nsock_init==false ){ + /* Create a new nsock pool */ + if ((nsp = nsock_pool_new(NULL)) == NULL) + nping_fatal(QT_3, "Failed to create new pool. QUITTING.\n"); + nsock_pool_set_device(nsp, o.getDevice()); + + /* Allow broadcast addresses */ + nsock_pool_set_broadcast(nsp, 1); + + /* Set nsock trace level */ + gettimeofday(&now, NULL); + if( o.getDebugging() == DBG_5) + nsock_set_loglevel(NSOCK_LOG_INFO); + else if( o.getDebugging() > DBG_5 ) + nsock_set_loglevel(NSOCK_LOG_DBG_ALL); + /* Flag it as already initialized so we don't do it again */ + nsock_init=true; + } + return OP_SUCCESS; +} /* End of init() */ + + +/** Cleans up the internal nsock pool and any other internal data that + * needs to be taken care of before destroying the object. */ +int ProbeMode::cleanup(){ + nsock_pool_delete(this->nsp); + return OP_SUCCESS; +} /* End of cleanup() */ + + +/** Returns the internal nsock pool. + * @warning the caller must ensure that init_nsock() has been called before + * calling this method; otherwise, it will fatal() */ +nsock_pool ProbeMode::getNsockPool(){ + if( this->nsock_init==false) + nping_fatal(QT_3, "getNsockPool() called before init_nsock(). Please report a bug."); + return this->nsp; +} /* End of getNsockPool() */ + + +/** This function handles regular ping mode. Basically it handles both + * unprivileged modes (TCP_CONNECT and UDP_UNPRIV) and raw packet modes + * (TCP, UDP, ICMP, ARP). This function is where the loops that iterate + * over target hosts and target ports are located. It uses the nsock lib + * to schedule transmissions. The actual Tx and Rx is done inside the nsock + * event handlers, here we just schedule them, take care of the timers, + * set up pcap and the bpf filter, etc. */ +int ProbeMode::start(){ + int rc; + int p=0, pc=-1; /**< Indexes for ports count */ + u32 c=0; /**< Index for packet count */ + u32 zero=0; /**< Empty payload */ + u8 pktinfobuffer[512+1]; /**< Used in ippackethdrinfo() calls */ + u8 pkt[MAX_IP_PACKET_LEN]; /**< Holds packets returned by fillpacket */ + int pktLen=0; /**< Length of current packet */ + NpingTarget *target=NULL; /**< Current target */ + u16 *targetPorts=NULL; /**< Pointer to array of target ports */ + int numTargetPorts=0; /**< Total number of target ports */ + u16 currentPort=0; /**< Current target port */ + char *filterstring; /**< Stores BFP filter spec string */ + int rawipsd=-1; /**< Descriptor for raw IP socket */ + enum nsock_loopstatus loopret; /**< Stores nsock_loop returned status */ + nsock_iod pcap_nsi; /**< Stores Pcap IOD */ + u32 packetno=0; /**< Total packet count */ + bool first_time=true; /**< First time we run the loop? */ + char pcapdev[128]; /**< Device name passed to pcap_open_live */ + #define MX_PKT 1024 /**< Packet structs we keep simultaneously*/ + sendpkt_t pkts2send[MX_PKT]; /**< We have a race condition here but the + * problem is not trivial to solve because we cannot create a sendpkt_t + * struct for every probe we send. That could be alright in most cases but + * not when targeting large networks or when doing flooding. The problem here + * is that we may need access to specific sendpkt_t vars inside the nsock + * event handlers but as some operations are asynchronous, we may exhaust + * the current array of sendpkt_t structs and overwrite some positions + * that contain data that has not been read yet. Anyway, this bug should not + * happen when using Nping for normal purposes. As long as you don't choose + * to ping a 100 million hosts with an inter-probe delay of 1ms, you should be + * fine. For more info, write to luis.mgarc@gmail.com or post a message to the + * nmap-dev mailing list. */ + + + /* Some safe zero initializations */ + memset(pktinfobuffer, 0, 512+1); + memset(pkt, 0, MAX_IP_PACKET_LEN); + memset(&pcap_nsi, 0, sizeof(pcap_nsi)); + memset(pkts2send, 0, MX_PKT * sizeof(sendpkt_t)); + + /* Get array of target ports */ + targetPorts = o.getTargetPorts( &numTargetPorts ); + + /* Set up nsock */ + this->init_nsock(); + + switch( o.getMode() ){ + + /***************************************************************************/ + /** TCP CONNECT MODE **/ + /***************************************************************************/ + case TCP_CONNECT: + o.stats.startClocks(); + for( c=0; c < o.getPacketCount(); c++){ /* Do requested times */ + o.targets.rewind(); + for (p=0; p < numTargetPorts; p++){ /* Iterate through all destination ports */ + o.targets.rewind(); + while( (target=o.targets.getNextTarget()) != NULL ){ + + /* Store relevant info so we can pass it to the handler */ + pc=(pc+1)%MX_PKT; + pkts2send[pc].type=PKT_TYPE_TCP_CONNECT; + pkts2send[pc].target=target; + pkts2send[pc].dstport=targetPorts[p]; + + /* Schedule a TCP Connect attempt */ + if( first_time ){ + nsock_timer_create(nsp, tcpconnect_event_handler, 1, &pkts2send[pc]); + first_time=false; + loopret=nsock_loop(nsp, 2); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + }else{ + nsock_timer_create(nsp, tcpconnect_event_handler, o.getDelay()+1, &pkts2send[pc]); + loopret=nsock_loop(nsp, o.getDelay()+1); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + } + } + } + } + o.stats.stopTxClock(); + /* If there are some events pending, we'll wait for DEFAULT_WAIT_AFTER_PROBES ms, + * otherwise nsock_loop() will return immediately */ + loopret=nsock_loop(nsp, DEFAULT_WAIT_AFTER_PROBES); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + o.stats.stopRxClock(); + return OP_SUCCESS; + break; /* case TCP_CONNECT */ + + + /***************************************************************************/ + /** UDP UNPRIVILEGED MODE **/ + /***************************************************************************/ + case UDP_UNPRIV: + o.stats.startClocks(); + for( c=0; c < o.getPacketCount(); c++){ /* Do requested times */ + o.targets.rewind(); + for (p=0; p < numTargetPorts; p++){ /* Iterate through all destination ports */ + o.targets.rewind(); + while( (target=o.targets.getNextTarget()) != NULL ){ + + /* Store relevant info so we can pass it to the handler */ + pc=(pc+1)%MX_PKT; + pkts2send[pc].type=PKT_TYPE_UDP_NORMAL; + pkts2send[pc].target=target; + pkts2send[pc].dstport=targetPorts[p]; + + if(o.issetPayloadBuffer() ){ + pkts2send[pc].pkt=o.getPayloadBuffer(); + pkts2send[pc].pktLen=o.getPayloadLen(); + }else{ + /* We send 4 bytes of value 0 because nsock does not let us send empty UDP packets */ + pkts2send[pc].pkt=(u8*)&zero; + pkts2send[pc].pktLen=4; + /* TODO: At some point we want to support David's custom UDP payloads here*/ + } + + /* Schedule a UDP attempt */ + if( first_time ){ + nsock_timer_create(nsp, udpunpriv_event_handler, 1, &pkts2send[pc]); + first_time=false; + loopret=nsock_loop(nsp, 2); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + }else{ + nsock_timer_create(nsp, udpunpriv_event_handler, o.getDelay(), &pkts2send[pc]); + loopret=nsock_loop(nsp, o.getDelay()); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + } + } + } + } + o.stats.stopTxClock(); + /* If there are some events pending, we'll wait for DEFAULT_WAIT_AFTER_PROBES ms, + * otherwise nsock_loop() will return immediately */ + if(!o.disablePacketCapture()){ + loopret=nsock_loop(nsp, DEFAULT_WAIT_AFTER_PROBES); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + } + o.stats.stopRxClock(); + return OP_SUCCESS; + break; /* case UDP_UNPRIV */ + + + + /***************************************************************************/ + /** TCP/UDP/ICMP/ARP MODES **/ + /***************************************************************************/ + case TCP: + case UDP: + case ICMP: + case ARP: + + if( o.getMode()!=ARP && o.sendEth()==false ){ + /* Get socket descriptor. No need for it in ARP since we send at eth level */ + if ((rawipsd = obtainRawSocket()) < 0 ) + nping_fatal(QT_3,"Couldn't acquire raw socket. Are you root?"); + } + + /* Check if we have enough information to get the party started */ + if((o.getMode()==TCP || o.getMode()==UDP) && targetPorts==NULL) + nping_fatal(QT_3, "normalProbeMode(): NpingOps does not contain correct target ports\n"); + + /* Set up libpcap */ + if(!o.disablePacketCapture()){ + /* Create new IOD for pcap */ + if ((pcap_nsi = nsock_iod_new(nsp, NULL)) == NULL) + nping_fatal(QT_3, "Failed to create new nsock_iod. QUITTING.\n"); + + /* Open pcap */ + filterstring=getBPFFilterString(); + nping_print(DBG_2,"Opening pcap device %s", o.getDevice() ); + #ifdef WIN32 + /* Nping normally uses device names obtained through dnet for interfaces, + * but Pcap has its own naming system. So the conversion is done here */ + if (!DnetName2PcapName(o.getDevice(), pcapdev, sizeof(pcapdev))) { + /* Oh crap -- couldn't find the corresponding dev apparently. + * Let's just go with what we have then ... */ + Strncpy(pcapdev, o.getDevice(), sizeof(pcapdev)); + } + #else + Strncpy(pcapdev, o.getDevice(), sizeof(pcapdev)); + #endif + + rc = nsock_pcap_open(nsp, pcap_nsi, pcapdev, 8192, + (o.spoofSource()) ? 1 : 0, filterstring); + if (rc) + nping_fatal(QT_3, "Error opening capture device %s\n", o.getDevice()); + nping_print(DBG_2,"Pcap device %s open successfully", o.getDevice()); + } + + /* Ready? Go! */ + o.stats.startClocks(); + + switch ( o.getMode() ){ + + /* Modes in which we need to iterate over target ports */ + case TCP: + case UDP: + /* Do user requested times */ + for( c=0; c < o.getPacketCount(); c++){ + o.targets.rewind(); + o.setCurrentRound( o.issetTTL() ? ((c%(256-o.getTTL()))+o.getTTL()) : ((c%255)+1 ) ); /* Used in traceroute mode */ + /* Iterate through all destination ports */ + for (p=0; p < numTargetPorts; p++){ + o.targets.rewind(); + /* Iterate trough all target IP addresses */ + while( (target=o.targets.getNextTarget()) != NULL ){ + + currentPort=targetPorts[p]; + + if ( fillPacket( target, currentPort, pkt, MAX_IP_PACKET_LEN, &pktLen, rawipsd ) != OP_SUCCESS ){ + nping_fatal(QT_3, "normalProbeMode(): Error in packet creation"); + } + /* Safe checks */ + if (pktLen <=0) + nping_fatal(QT_3, "normalProbeMode(): Invalid packet returned by fillPacket() "); + + /* Store relevant info so we can pass it to the handler */ + pc=(pc+1)%MX_PKT; + pkts2send[pc].type = (o.getMode()==TCP) ? PKT_TYPE_TCP_RAW : PKT_TYPE_UDP_RAW; + pkts2send[pc].pkt = pkt; + pkts2send[pc].target=target; + pkts2send[pc].pktLen = pktLen; + pkts2send[pc].rawfd = rawipsd; + pkts2send[pc].seq = ++packetno; + pkts2send[pc].dstport=currentPort; + + /* Tell nsock we expect one reply. Actually we schedule 2 pcap events just in case + * we get more than one response. */ + if(!o.disablePacketCapture()){ + nsock_pcap_read_packet(nsp, pcap_nsi, nping_event_handler, o.getDelay(), NULL); + nsock_pcap_read_packet(nsp, pcap_nsi, nping_event_handler, o.getDelay(), NULL); + } + + /* Let nsock handle probe transmission and inter-probe delay */ + if( first_time ){ + nsock_timer_create(nsp, nping_event_handler, 1, &pkts2send[pc]); + first_time=false; + loopret=nsock_loop(nsp, 2); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + }else{ + nsock_timer_create(nsp, nping_event_handler, o.getDelay(), &pkts2send[pc]); + loopret=nsock_loop(nsp, o.getDelay()+1); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + } + } + } + } + break; /* Nested case UDP/TCP */ + + + /* Modes in which we DO NOT need to iterate over target ports */ + case ICMP: + case ARP: + /* Do user requested times */ + for( c=0; c < o.getPacketCount(); c++){ + o.targets.rewind(); + o.setCurrentRound( o.issetTTL() ? ((c%(256-o.getTTL()))+o.getTTL()) : ((c%255)+1 ) ); /* Used in traceroute mode */ + /* Iterate trough all target IP addresses */ + while( (target=o.targets.getNextTarget()) != NULL ){ + + if ( fillPacket( target, 0, pkt, MAX_IP_PACKET_LEN, &pktLen, rawipsd ) != OP_SUCCESS ) + nping_fatal(QT_3, "normalProbeMode(): Error in packet creation"); + if (pktLen <=0) + nping_fatal(QT_3, "normalProbeMode(): Error packet returned by createPacket() "); + + /* Store relevant info so we can pass it to the handler */ + pc=(pc+1)%MX_PKT; + pkts2send[pc].type = (o.getMode()==ICMP) ? PKT_TYPE_ICMP_RAW : PKT_TYPE_ARP_RAW; + pkts2send[pc].pkt = pkt; + pkts2send[pc].pktLen = pktLen; + pkts2send[pc].target=target; + pkts2send[pc].rawfd = rawipsd; + pkts2send[pc].seq = ++packetno; + + /* Tell nsock we expect one reply. Actually we schedule 2 pcap events just in case + * we get more than one response. */ + if(!o.disablePacketCapture()){ + nsock_pcap_read_packet(nsp, pcap_nsi, nping_event_handler, o.getDelay(), NULL); + nsock_pcap_read_packet(nsp, pcap_nsi, nping_event_handler, o.getDelay(), NULL); + } + + /* Let nsock handle probe transmission and inter-probe delay */ + if( first_time ){ + nsock_timer_create(nsp, nping_event_handler, 1, &pkts2send[pc]); + first_time=false; + loopret=nsock_loop(nsp, 2); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + }else{ + nsock_timer_create(nsp, nping_event_handler, o.getDelay(), &pkts2send[pc]); + loopret=nsock_loop(nsp, o.getDelay()+1); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + } + } + } + break; /* Nested case ICMP/ARP */ + + } /* End of nested switch */ + + o.stats.stopTxClock(); + if(!o.disablePacketCapture()){ + nsock_pcap_read_packet(nsp, pcap_nsi, nping_event_handler, DEFAULT_WAIT_AFTER_PROBES, NULL); + loopret=nsock_loop(nsp, DEFAULT_WAIT_AFTER_PROBES); + if (loopret == NSOCK_LOOP_ERROR) + nping_fatal(QT_3, "Unexpected nsock_loop error.\n"); + o.stats.stopRxClock(); + } + /* Close opened descriptors */ + if(rawipsd>=0) + close(rawipsd); + break; /* case TCP || case UDP || case ICMP || case ARP */ + + default: + nping_fatal(QT_3, "normalProbeMode(): Wrong mode. Please report this bug."); + break; + } /* End of main switch */ + return OP_SUCCESS; +} /* End of start() */ + + + + + +/** Creates buffer suitable to be passed to a sendto() call. The buffer + * represents a raw network packet. The specific protocols are obtained from + * the information stored in "NpingOps o" object. For example, if o.getMode() + * returns TCP and o.ipv4() is true, then an IPv4-TCP packet will be generated + * and stored in the supplied buffer. + * @param target should contain a valid target with an IP that matches + * NpingOps::af() returned value. + * @param port is the destination port number. It is only necessary in TCP and + * UDP modes. You can safely pass a dummy value in ICMP and ARP modes. + * @param buff should point to a buffer where the generated packet can be stored. + * @param bufflen should be the size of the supplied buffer. This function will + * never write more than "bufflen" bytes to the buffer. + * @param filledlen will be set to the amount of bytes actually written into + * the buffer. + * @param rawfd is the raw socket descriptor that will be used to send the + * packet. This is only necessary when sending IPv6 packets at raw TCP level + * because some IPv6 options like hop limit are tuned using calls to + * setsockopt() */ +int ProbeMode::fillPacket(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd){ + EthernetHeader e; /* Used when sending at raw Ethernet level. */ + u8 *pnt=buff; /* Aux pointer to keep track of user supplied "buff". */ + int pntlen=bufflen; /* Aux counter to store how many bytes we have left. */ + int final_len=0; + bool eth_included=false; + + if(target==NULL || buff==NULL || bufflen<=0 || filledlen==NULL) + return OP_FAILURE; + else + nping_print(DBG_4, "fillPacket(target=%p, port=%d, buff=%p, bufflen=%d, filledlen=%p rawfd=%d)", target, port, buff, bufflen, filledlen, rawfd); + +/* If o.sendEth() is true that means we need to send packets at raw Ethernet + * level (we are probably running on windows or user requested that explicitly. + * Ethernet frames that carry ARP packets have special requirements (e.g. some + * of them are sent to a FF:FF:FF:FF:FF:FF broadcast address). That's why we + * don't create Ethernet frames here when ARP is used. Function fillPacketARP() + * takes care of that already. */ + if(o.sendEth() && o.getMode()!=ARP){ + e.setNextElement( NULL ); + if( buff==NULL || filledlen==NULL) + nping_fatal(QT_3,"fillPacketARP(): NULL pointer supplied."); + /* Source MAC Address */ + if( o.issetSourceMAC() ) + e.setSrcMAC( o.getSourceMAC() ); + else{ + if( target->getSrcMACAddress() ) + e.setSrcMAC( (u8 *)target->getSrcMACAddress() ); + else + nping_fatal(QT_3, "fillPacket(): Cannot determine Source MAC address."); + } + + /* Destination MAC Address */ + if( o.issetDestMAC() ) + e.setDstMAC( o.getDestMAC() ); + else{ + if( target->getNextHopMACAddress() ) + e.setDstMAC( (u8 *)target->getNextHopMACAddress() ); + else + nping_fatal(QT_3, "fillPacket(): Cannot determine Next Hop MAC address."); + } + + /* Ethertype value */ + if( o.issetEtherType() ) + e.setEtherType( o.getEtherType() ); + else{ + if( o.getIPVersion() == IP_VERSION_4 ) + e.setEtherType(ETHTYPE_IPV4); + else if ( o.getIPVersion() == IP_VERSION_6 ) + e.setEtherType(ETHTYPE_IPV6); + else + nping_fatal(QT_3, "Bug in fillPacket() and NpingOps::ipversion"); + } + + /* Write the ethernet header to the beginning of the original buffer */ + e.dumpToBinaryBuffer(buff, 14); + + /* Move this pointer so the fillPacketXXXX() functions start from the + * right byte. */ + pnt+=14; + pntlen-=14; + eth_included=true; + } + + switch( o.getMode() ){ + case TCP: + fillPacketTCP(target, port, pnt, pntlen, &final_len, rawfd); + break; + case UDP: + fillPacketUDP(target, port, pnt, pntlen, &final_len, rawfd); + break; + case ICMP: + fillPacketICMP(target, pnt, pntlen, &final_len, rawfd); + break; + case ARP: /* ARP builds its own Ethernet header inside fillPacketARP() */ + fillPacketARP(target, pnt, pntlen, &final_len, rawfd); + break; + default: + nping_fatal(QT_3, "Bug in fillPacket() and NpingOps::getMode()"); + break; + } + + if( eth_included ) + final_len+=14; + *filledlen=final_len; + return OP_SUCCESS; +} /* End of createPacket() */ + + + +/** Fills an IPv4Header object with information obtained from the NpingOps + * class. + * @return OP_SUCCESS on success and fatal()s in case of failure. */ +int ProbeMode::createIPv4(IPv4Header *i, PacketElement *next_element, const char *next_proto, NpingTarget *target){ + if( i==NULL || next_proto==NULL || target==NULL) + nping_fatal(QT_3,"createIPv4(): NULL pointer supplied."); + + i->setNextElement( next_element ); /* Set datagram payload */ + i->setDestinationAddress( target->getIPv4Address() ); /* Destination IP */ + i->setSourceAddress( o.spoofSource() ? o.getIPv4SourceAddress() : target->getIPv4SourceAddress()); /* Source IP */ + i->setTOS( o.getTOS() ); /* Type of service */ + i->setIdentification( o.getIdentification() ); /* Identification */ + i->setNextProto(next_proto); + + /* Time to live */ + if(o.issetTraceroute()){ + i->setTTL( o.getCurrentRound() ); + }else{ + i->setTTL( o.getTTL() ); + } + + /* Flags */ + if( o.issetMF() && o.getMF() == true ) + i->setMF(); + if( o.issetDF() && o.getDF() == true ) + i->setDF(); + if( o.issetRF() && o.getRF() == true ) + i->setRF(); + + /* IP Options */ + if( o.issetIPOptions() == true ) + i->setOpts( o.getIPOptions() ); + + i->setTotalLength(); + + /* Checksum */ + if( o.getBadsumIP() == true ) + i->setSumRandom(); + else + i->setSum(); + + return OP_SUCCESS; +} /* End of createIPv4() */ + + + + + +/** Fills an IPv6Header object with information obtained from the NpingOps + * class. + * @return OP_SUCCESS on success and fatal()s in case of failure. */ +int ProbeMode::createIPv6(IPv6Header *i, PacketElement *next_element, const char *next_proto, NpingTarget *target){ + if( i==NULL || next_proto==NULL || target==NULL) + nping_fatal(QT_3,"createIPv6(): NULL pointer supplied."); + + /* Set datagram payload */ + i->setNextElement( next_element ); + + i->setVersion(); + i->setTrafficClass( o.getTrafficClass() ); + i->setFlowLabel( o.getFlowLabel() ); + i->setNextHeader(next_proto); + i->setPayloadLength(); + i->setDestinationAddress( target->getIPv6Address_u8() ); + + /* Hop Limit */ + if ( o.issetTraceroute() ){ + i->setHopLimit( o.getCurrentRound() ); + }else{ + i->setHopLimit( o.getHopLimit() ); + } + + /* Source IP */ + if( o.issetIPv6SourceAddress() ){ + i->setSourceAddress( o.getIPv6SourceAddress() ); + }else{ + if ( target->getIPv6SourceAddress_u8() != NULL ) + i->setSourceAddress( target->getIPv6SourceAddress_u8() ); + else + nping_fatal(QT_3, "createIPv6(): Cannot determine Source IPv6 Address"); + } + return OP_SUCCESS; +} /* End of createIPv6() */ + + +/** This function is a bit tricky. The thing is that some engineer had + * the brilliant idea to remove IP_HDRINCL support in IPv6. As a result, it's + * a big pain in the ass to create raw IPv6 headers because we can only do it + * if we are sending packets at raw Ethernet level. So if we want our own IPv6 + * header (for source IP spoofing, etc) we have to do things like determine + * source and dest MAC addresses (this is even more complicated in IPv6 than + * in IPv4 because we don't have ARP anymore, we have to use something new, the + * NDP, Neighbor Discovery Protocol.) + * So the thing is that, if the user does not want to play with the IPv6 header, + * why bother with all that link layer work? So what we do is create raw + * transport layer packets and then send them through a raw IPv6 socket. The + * socket will encapsulate our packets into a nice clean IPv6 header + * automatically so we don't have to worry about low level details anymore. + * + * So this function basically takes a raw IPv6 socket descriptor and then tries + * to set some basic parameters (like Hop Limit) using setsockopt() calls. + * It always returns OP_SUCCESS. However, if errors are found, they are printed + * (QT_2 level) using nping_warning(); + * */ +int ProbeMode::doIPv6ThroughSocket(int rawfd){ + + /* Hop Limit */ + int hoplimit=0; + if( o.issetHopLimit() ) + hoplimit= o.getHopLimit(); + else if ( o.issetTraceroute() ){ + hoplimit= (o.getCurrentRound()<255)? o.getCurrentRound() : (o.getCurrentRound()%255)+1; + }else{ + hoplimit=DEFAULT_IPv6_TTL; + } + if( setsockopt(rawfd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&hoplimit, sizeof(hoplimit)) != 0 ) + nping_warning(QT_2, "doIPv6ThroughSocket(): setsockopt() for Unicast Hop Limit on IPv6 socket failed"); + if( setsockopt(rawfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hoplimit, sizeof(hoplimit)) != 0 ) + nping_warning(QT_2, "doIPv6ThroughSocket(): setsockopt() for Multicast Hop Limit on IPv6 socket failed"); + +#ifdef IPV6_CHECKSUM /* This is not available in when compiling with MinGW */ + /* Transport layer checksum */ + /* This is totally crazy. We have to tell the kernel EXPLICITLY that we + * want it to set the TCP/UDP checksum for us. Why the hell is this the + * default behavior if it's so incredibly difficult to get the IPv6 source + * address? + * Additionally, we have to be very careful not to set this option when + * dealing with ICMPv6 because in that case the kernel computes the + * checksum automatically and Nping can actually crash if we've set + * this option manually, can you believe it? */ + if( o.getMode()==TCP || o.getMode()==UDP){ + /* We don't request valid TCP checksums if the user requested bogus sums */ + if( o.getBadsum()==false ){ + int offset = 16; + if( setsockopt (rawfd, IPPROTO_IPV6, IPV6_CHECKSUM, (char *)&offset, sizeof(offset)) != 0 ) + nping_warning(QT_2, "doIPv6ThroughSocket(): failed to set IPV6_CHECKSUM option on IPv6 socket. "); + } + } +#endif + + /* Bind IPv6 socket to a specific network interface */ + if ( o.issetDevice() ) { + /* It seems that SO_BINDTODEVICE only work on Linux */ + #ifdef LINUX + if (setsockopt(rawfd, SOL_SOCKET, SO_BINDTODEVICE, o.getDevice(), strlen(o.getDevice())+1) == -1) { + nping_warning(QT_2, "Error binding IPv6 socket to device %s", o.getDevice() ); + } + #endif + } + + return OP_SUCCESS; + +} /* End of doIPv6ThroughSocket() */ + + + + + +/** This function handles TCP packet creation. However, the final packet that + * it produces also includes an IP header. + * There is one exception. When we are sending IPv6 packet at raw TCP level, + * the returned packet does not contain an IPv6 header but the supplied + * rawfd socket descriptor is ready to go because some options have been + * set on it by doIPv6ThroughSocket(). */ +int ProbeMode::fillPacketTCP(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd){ + + IPv4Header i; + IPv6Header i6; + TCPHeader t; + RawData p; + struct in_addr tip, sip; + + if( buff==NULL || filledlen==NULL || target==NULL) + nping_fatal(QT_3,"fillPacketTCP(): NULL pointer supplied."); + + /* Add Payload if necessary */ + if ( o.issetPayloadType() ){ + switch( o.getPayloadType() ){ + case PL_RAND: case PL_HEX: case PL_STRING: + p.store(o.getPayloadBuffer(), o.getPayloadLen()); + break; + + case PL_FILE: + break; + + default: + break; + } + t.setNextElement( &p ); + } + + /* Craft TCP Header */ + t.setSourcePort( o.getSourcePort() ); + t.setDestinationPort( port ); + t.setSeq( o.getTCPSequence() ); + t.setAck( o.getTCPAck() ); + t.setOffset(); + t.setWindow( o.getTCPWindow() ); + t.setUrgPointer(0); + t.setFlags(0); + + /* Flags */ + if( o.getFlagTCP(FLAG_CWR) == 1 ) t.setCWR(); + if( o.getFlagTCP(FLAG_ECN) == 1 ) t.setECN(); + if( o.getFlagTCP(FLAG_URG) == 1 ) t.setURG(); + if( o.getFlagTCP(FLAG_ACK) == 1 ) t.setACK(); + if( o.getFlagTCP(FLAG_PSH) == 1 ) t.setPSH(); + if( o.getFlagTCP(FLAG_RST) == 1 ) t.setRST(); + if( o.getFlagTCP(FLAG_SYN) == 1 ) t.setSYN(); + if( o.getFlagTCP(FLAG_FIN) == 1 ) t.setFIN(); + + + /* Now let's encapsulate the TCP packet into an IP packet */ + switch( o.getIPVersion() ){ + + case IP_VERSION_4: + + /* Fill the IPv4Header object with the info from NpingOps */ + createIPv4(&i, &t, "TCP", target); + + tip=target->getIPv4Address(); + i.getSourceAddress(&sip); + if( o.getBadsum() == true ){ + t.setSumRandom(tip, sip); + }else{ + t.setSum(); + } + /* Store result in user supplied buffer */ + *filledlen = i.dumpToBinaryBuffer(buff, bufflen); + + break; + + case IP_VERSION_6: + if( o.sendEth() ){ + /* Fill the IPv6Header object with the info from NpingOps */ + createIPv6(&i6, &t, "TCP", target); + + if( o.getBadsum() == true ) + t.setSumRandom(); + else{ + *filledlen = i6.dumpToBinaryBuffer(buff, bufflen); + ip6_checksum(buff, *filledlen); /* Provided by dnet */ + return OP_SUCCESS; + } + + /* Store result in user supplied buffer */ + *filledlen = i6.dumpToBinaryBuffer(buff, bufflen); + }else{ + doIPv6ThroughSocket(rawfd); + + /* Set some bogus checksum */ + if( o.getBadsum()==true ) + t.setSumRandom(); + /* Set checksum to zero and pray for the kernel to set it to + * the right value. Brothers and sisters: + * + * Our TCP/IP stack, Who is in the kernel, + * Holy is Your Name; + * Your kingdom come, + * Your will be done, + * on userland as it is in kernel space. + * Give us this day our TCP checksum, + * and forgive us for our raw sockets, + * as we forgive you for your kernel panics; + * and lead us not into /dev/null, + * but deliver our packet to the next hop. Amen. + * */ + else + t.setSum(0); + + /* Since we cannot include our own header like we do in IPv4, the + * buffer we return is the TCP one. */ + *filledlen = t.dumpToBinaryBuffer(buff, bufflen); + } + break; + + default: + nping_fatal(QT_3, "fillPacketTCP(): Wrong IP version in NpingOps\n"); + break; + + } + + return OP_SUCCESS; + +} /* End of fillPacketTCP() */ + + + + + + +/** This function handles UDP packet creation. However, the final packet that + * it produces also includes an IP header. + * There is one exception. When we are sending IPv6 packet at raw TCP level, + * the returned packet does not contain an IPv6 header but the supplied + * rawfd socket descriptor is ready to go because some options have been + * set on it by doIPv6ThroughSocket(). */ +int ProbeMode::fillPacketUDP(NpingTarget *target, u16 port, u8 *buff, int bufflen, int *filledlen, int rawfd){ + + IPv4Header i; + IPv6Header i6; + UDPHeader u; + RawData p; + struct in_addr tip, sip; + + if( buff==NULL || filledlen==NULL || target==NULL) + nping_fatal(QT_3,"fillPacketUDP(): NULL pointer supplied."); + + + /* Add Payload if necessary */ + if ( o.issetPayloadType() ){ + switch( o.getPayloadType() ){ + case PL_RAND: case PL_HEX: case PL_STRING: + p.store(o.getPayloadBuffer(), o.getPayloadLen()); + break; + + case PL_FILE: + break; + + default: + break; + } + u.setNextElement( &p ); + } + + /* Craft UDP Header */ + u.setSourcePort( o.getSourcePort() ); + u.setDestinationPort( port ); + u.setTotalLength(); + + /* Now let's encapsulate the TCP packet into an IP packet */ + switch( o.getIPVersion() ){ + + case IP_VERSION_4: + + /* Fill the IPv4Header object with the info from NpingOps */ + createIPv4(&i, &u, "UDP", target); + + /* Set checksum */ + tip=target->getIPv4Address(); + i.getSourceAddress(&sip); + if( o.getBadsum() == true ){ + u.setSumRandom(tip, sip); + }else{ + u.setSum(); + } + /* Store result in user supplied buffer */ + *filledlen = i.dumpToBinaryBuffer(buff, bufflen); + + break; + + case IP_VERSION_6: + + if( o.sendEth() ){ + /* Fill the IPv6Header object with the info from NpingOps */ + createIPv6(&i6, &u, "UDP", target); + + if( o.getBadsum() == true ){ + u.setSumRandom(); + /* Store result in user supplied buffer */ + *filledlen = i6.dumpToBinaryBuffer(buff, bufflen); + } + else{ + *filledlen = i6.dumpToBinaryBuffer(buff, bufflen); + ip6_checksum(buff, *filledlen); /* Provided by dnet */ + return OP_SUCCESS; + } + }else{ + doIPv6ThroughSocket(rawfd); + + /* Set some bogus checksum */ + if( o.getBadsum()==true ) + u.setSumRandom(); + /* Set checksum to zero and assume the kernel is gonna set the + * right value. If it doesn't, it's not that important since + * UDP checksum is optional and can safely be set to zero */ + else + u.setSum(0); + + /* Since we cannot include our own header like we do in IPv4, the + * buffer we return is the UDP one. */ + *filledlen = u.dumpToBinaryBuffer(buff, bufflen); + } + break; + + default: + nping_fatal(QT_3, "fillPacketUDP(): Wrong IP version in NpingOps\n"); + break; + + } + + return OP_SUCCESS; + +} /* End of fillPacketUDP() */ + + + +/** This function handles ICMP packet creation. However, the final packet that + * it produces also includes an IP header. + * + * Currently this function only supports ICMPv4 packet creation. ICMPv6 will + * be added in the future.*/ +int ProbeMode::fillPacketICMP(NpingTarget *target, u8 *buff, int bufflen, int *filledlen, int rawfd){ + IPv4Header i; + IPv6Header i6; + ICMPv4Header c4; + ICMPv6Header c6; + RawData p; + + if( buff==NULL || filledlen==NULL || target==NULL) + nping_fatal(QT_3,"fillPacketICMP(): NULL pointer supplied."); + nping_print(DBG_4, "fillPacketICMP(target=%p, buff=%p, bufflen=%d, filledlen=%p)", target, buff, bufflen, filledlen); + + /* Add Payload if necessary */ + if ( o.issetPayloadType() ){ + switch( o.getPayloadType() ){ + case PL_RAND: case PL_HEX: case PL_STRING: + p.store(o.getPayloadBuffer(), o.getPayloadLen()); + break; + + case PL_FILE: + break; + + default: + break; + } + c4.setNextElement( &p ); + c6.setNextElement( &p ); + } + + if( o.ipv4() ){ + + c4.setType( o.getICMPType() ); + c4.setCode( o.getICMPCode() ); + + /* Lets go for type specific options */ + switch ( c4.getType() ){ + + case ICMP_REDIRECT: + c4.setGatewayAddress( o.getICMPRedirectAddress() ); + break; + + case ICMP_ECHO: + case ICMP_ECHOREPLY: + if( o.issetICMPIdentifier() ) + c4.setIdentifier( o.getICMPIdentifier() ); + else + c4.setIdentifier( target->getICMPIdentifier() ); + + if( o.issetICMPSequence() ) + c4.setSequence( o.getICMPSequence() ); + else + c4.setSequence( target->obtainICMPSequence() ); + break; + + case ICMP_ROUTERADVERT: + c4.setAddrEntrySize( 2 ); + c4.setLifetime( o.getICMPRouterAdvLifetime() ); + + if( o.issetICMPAdvertEntry() ) + for (int z=0; z<o.getICMPAdvertEntryCount(); z++){ + struct in_addr entryaddr; + u32 entrypref; + o.getICMPAdvertEntry(z, &entryaddr, &entrypref ); + c4.addRouterAdvEntry(entryaddr, entrypref); + } + break; + + case ICMP_PARAMPROB: + c4.setParameterPointer( o.getICMPParamProblemPointer() ); + break; + + case ICMP_TSTAMP: + case ICMP_TSTAMPREPLY: + if( o.issetICMPIdentifier() ) + c4.setIdentifier( o.getICMPIdentifier() ); + else + c4.setIdentifier( target->getICMPIdentifier() ); + + if( o.issetICMPSequence() ) + c4.setSequence( o.getICMPSequence() ); + else + c4.setSequence( target->obtainICMPSequence() ); + c4.setOriginateTimestamp( o.getICMPOriginateTimestamp() ); + c4.setReceiveTimestamp( o.getICMPReceiveTimestamp() ); + c4.setTransmitTimestamp( o.getICMPTransmitTimestamp() ); + break; + + case ICMP_INFO: + case ICMP_INFOREPLY: + case ICMP_MASK: + case ICMP_MASKREPLY: + case ICMP_TRACEROUTE: + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_ROUTERSOLICIT: + case ICMP_TIMXCEED: + break; + + default: + /* TODO: What do we do here if user specified a non standard type? */ + break; + + } + /* Compute checksum */ + c4.setSum(); /* TODO: Do we want to implement --badsum-icmp? */ + + /* Fill the IPv4Header object with the info from NpingOps */ + createIPv4(&i, &c4, "ICMP", target); + + /* Store result in user supplied buffer */ + *filledlen = i.dumpToBinaryBuffer(buff, bufflen); + + }else{ + + c6.setType( o.getICMPType() ); + c6.setCode( o.getICMPCode() ); + + switch( c6.getType() ){ + + case ICMPv6_ECHO: + case ICMPv6_ECHOREPLY: + c6.setIdentifier(o.issetICMPIdentifier() ? o.getICMPIdentifier() : target->getICMPIdentifier()); + c6.setSequence(o.issetICMPSequence() ? o.getICMPSequence() : target->obtainICMPSequence()); + break; + + case ICMPv6_UNREACH: + case ICMPv6_PKTTOOBIG: + case ICMPv6_TIMXCEED: + case ICMPv6_PARAMPROB: + + case ICMPv6_ROUTERSOLICIT: + case ICMPv6_ROUTERADVERT: + case ICMPv6_NGHBRSOLICIT: + case ICMPv6_NGHBRADVERT: + case ICMPv6_REDIRECT: + case ICMPv6_RTRRENUM: + default: + break; + } + + /* Fill the IPv4Header object with the info from NpingOps */ + createIPv6(&i6, &c6, "ICMPv6", target); + + /* Compute checksum */ + c6.setSum(); + + /* Store result in user supplied buffer */ + *filledlen = i6.dumpToBinaryBuffer(buff, bufflen); + + } + + return OP_SUCCESS; + +} /* End of fillPacketICMP() */ + + + + +/** This function handles ARP packet creation. However, the final packet that + * it produces also includes an Ethernet header. */ +int ProbeMode::fillPacketARP(NpingTarget *target, u8 *buff, int bufflen, int *filledlen, int rawfd){ + + EthernetHeader e; + ARPHeader a; + + u8 bcastmac[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + u8 nullmac[6]={0x00,0x00,0x00,0x00,0x00,0x00}; + + if(target==NULL || buff==NULL || filledlen==NULL) + nping_fatal(QT_3,"fillPacketARP(): NULL pointer supplied."); + + nping_print(DBG_4, "fillPacketARP(target=%p, buff=%p, bufflen=%d, filledlen=%p)", target, buff, bufflen, filledlen); + + /* Source MAC Address */ + if( o.issetSourceMAC() ) + e.setSrcMAC( o.getSourceMAC() ); + else if( target->getSrcMACAddress()!=NULL ) + e.setSrcMAC(target->getSrcMACAddress()); + else + e.setSrcMAC( nullmac ); /* Defaults to 00:00:00:00:00:00 */ + + /* Destination MAC Address */ + if( o.issetDestMAC() ) + e.setDstMAC( o.getDestMAC() ); + else + e.setDstMAC( bcastmac ); /* Defaults to FF:FF:FF:FF:FF:FF */ + + /* Ethertype value */ + if( o.issetEtherType() ) + e.setEtherType( o.getEtherType() ); + else + e.setEtherType(ETHTYPE_ARP); + + /* Link Ethernet header to ARP packet. */ + e.setNextElement(&a); + + /* Hardware type */ + if( o.issetARPHardwareType() ) + a.setHardwareType( o.getARPHardwareType() ); + else + a.setHardwareType(); + + /* Protocol type */ + if( o.issetARPProtocolType() ) + a.setProtocolType( o.getARPProtocolType() ); + else + a.setProtocolType(); + + /* Length of HW Address */ + if( o.issetARPHwAddrLen() ) + a.setHwAddrLen( o.getARPHwAddrLen() ); + else + a.setHwAddrLen(); /* Defaults to length of a MAC address */ + + /* Length of Protocol Address */ + if( o.issetARPProtoAddrLen() ) + a.setProtoAddrLen( o.getARPProtoAddrLen() ); + else + a.setProtoAddrLen(); /* Defaults to length of IPv4 */ + + /* ARP Operation code. */ + a.setOpCode( o.getARPOpCode() ); + + /* Sender HW Address */ + if( o.issetARPSenderHwAddr() ) + a.setSenderMAC( o.getARPSenderHwAddr() ); + else + a.setSenderMAC( e.getSrcMAC() ); /* Get Ethernet's source MAC */ + + /* Sender Protocol Address */ + if( o.issetARPSenderProtoAddr() ) + a.setSenderIP( o.getARPSenderProtoAddr() ); + else if ( o.issetIPv4SourceAddress() ) + a.setSenderIP( o.getIPv4SourceAddress() ); + else + a.setSenderIP( target->getIPv4SourceAddress() ); + + /* Target HW Address */ + if( o.issetARPTargetProtoAddr() ) + a.setTargetIP( o.getARPTargetProtoAddr() ); + else + a.setTargetIP( target->getIPv4Address() ); + + /* Target Protocol Address */ + if( o.issetARPTargetHwAddr() ) + a.setTargetMAC( o.getARPTargetHwAddr() ); + else + a.setTargetMAC( nullmac ); /* Get Ethernet's target MAC */ + + /* Store result in user supplied buffer */ + *filledlen = e.dumpToBinaryBuffer(buff, bufflen); + + return OP_SUCCESS; +} + + + +/** This function creates a BPF filter specification, suitable to be passed to + * pcap_compile() or nsock_pcap_open(). It reads info from "NpingOps o" and + * creates the right BPF filter for the current operation mode. However, if + * user has supplied a custom BPF filter through option --bpf-filter, the + * same string stored in o.getBPFFilterSpec() is returned (so the caller + * should not even bother to check o.issetBPFFilterSpec() because that check + * is done here already. + * @warning Returned pointer is a statically allocated buffer that subsequent + * calls will overwrite. */ +char *ProbeMode::getBPFFilterString(){ + + char ipstring[128]; + static char filterstring[1024]; + char *buffer=filterstring; + u8 icmp_send_type=0; + u8 icmp_recv_type=0; + u16 arp_send_type=0; + u16 arp_recv_type=0; + bool skip_icmp_matching=false; + bool skip_arp_matching=false; + bool src_equals_target=false; + NpingTarget *t=NULL; + struct sockaddr_storage srcss; + struct sockaddr_in *s4=(struct sockaddr_in *)&srcss; + struct sockaddr_in6 *s6=(struct sockaddr_in6 *)&srcss; + struct sockaddr_storage dstss; + struct sockaddr_in *d4=(struct sockaddr_in *)&dstss; + struct sockaddr_in6 *d6=(struct sockaddr_in6 *)&dstss; + size_t dstlen, srclen; + memset(&srcss, 0, sizeof(struct sockaddr_storage)); + memset(&dstss, 0, sizeof(struct sockaddr_storage)); + memset(buffer, 0, 1024); + memset(ipstring, 0, 128); + + /* If user supplied a BPF from the cmd line, use it */ + if( o.issetBPFFilterSpec() ){ + buffer=o.getBPFFilterSpec(); + /* We copy it to our internal static buffer just in case... */ + if(buffer!=NULL) + strncpy(filterstring, buffer, sizeof(filterstring)-1); + else + strncpy(filterstring, "", 2); + nping_print(DBG_1, "BPF-filter: %s", filterstring); + return filterstring; + } + + /* For the server in Echo mode we need a special filter */ + if( o.getRole()==ROLE_SERVER ){ + /* Capture all IP packets but the ones that belong to the side-channel */ + sprintf(filterstring, "ip and ( not (tcp and (dst port %d or src port %d) ) )", o.getEchoPort(), o.getEchoPort() ); + nping_print(DBG_1, "BPF-filter: %s", filterstring); + return filterstring; + } + + /* Obtain source IP address */ + if( o.spoofSource() ) + memcpy( &srcss, o.getSourceSockAddr(), sizeof(struct sockaddr_storage) ); + else if( (t=o.targets.getNextTarget())!= NULL ){ + t->getSourceSockAddr(&srcss, &srclen); + }else{ + /* This should never happen but if for some reason we cannot obtain + * the target, set localhost address. */ + if ( o.ipv6() ){ + s4->sin_family=AF_INET; + inet_pton(AF_INET, "::1", &s6->sin6_addr); + }else{ + s4->sin_family=AF_INET; + inet_pton(AF_INET, "127.0.0.1", &s4->sin_addr); + } + nping_print(DBG_2, "Couldn't determine source address. Using address %s in BFP filter", IPtoa(&srcss) ); + } + o.targets.rewind(); + + /* Obtain the first target address */ + if(t!=NULL) + t->getTargetSockAddr(&dstss, &dstlen); + + /* Determine if source address and target address are the same. This is a + * special case that occurs when nping-ing localhost */ + if(s6->sin6_family==AF_INET6){ + if(memcmp(&s6->sin6_addr, &d6->sin6_addr, sizeof(d6->sin6_addr))==0) + src_equals_target=true; + }else if( s4->sin_family == AF_INET ){ + if(s4->sin_addr.s_addr == d4->sin_addr.s_addr) + src_equals_target=true; + } + + /* Convert src address to an ascii string so it can be copied to the BPF string */ + if(s6->sin6_family==AF_INET6){ + inet_ntop(AF_INET6, &s6->sin6_addr, ipstring, sizeof(ipstring)); + }else if( s4->sin_family == AF_INET ) { + inet_ntop(AF_INET, &s4->sin_addr, ipstring, sizeof(ipstring)); + }else{ + nping_warning(QT_2, "Warning: Wrong address family (%d) in getBPFFilterString(). Please report a bug", srcss.ss_family); + sprintf(ipstring,"127.0.0.1"); + } + + /* Tell the filter that we only want incoming packets, destined to our source IP */ + if(o.getMode()!=ARP){ + if(src_equals_target) + Snprintf(buffer, 1024, "(src host %s and dst host %s) and (", ipstring, ipstring); + else + Snprintf(buffer, 1024, "(not src host %s and dst host %s) and (", ipstring, ipstring); + buffer=filterstring+strlen(filterstring); + } + + /* Time for protocol specific constraints */ + switch( o.getMode() ){ + case TCP: /* Restrict to packets targeting our TCP source port */ + Snprintf(buffer, 1024-strlen(filterstring), "(tcp and dst port %d) ", o.getSourcePort()); + break; + + case UDP: /* Restrict to packets targeting our UDP source port */ + Snprintf(buffer, 1024-strlen(filterstring), "(udp and dst port %d) ", o.getSourcePort()); + break; + + case ICMP: /* Restrict to packets that are replies to our ICMP packets */ + icmp_send_type= o.issetICMPType() ? o.getICMPType() : DEFAULT_ICMP_TYPE; + switch( icmp_send_type ){ + case ICMP_TSTAMP: + icmp_recv_type=ICMP_TSTAMPREPLY; + break; + case ICMP_TSTAMPREPLY: /* If we are sending replies we probably want to see */ + icmp_recv_type=ICMP_TSTAMP; /* the requests that are being put into the network */ + break; + case ICMP_INFO: + icmp_recv_type=ICMP_INFOREPLY; + break; + case ICMP_INFOREPLY: + icmp_recv_type=ICMP_INFO; + break; + case ICMP_MASK: + icmp_recv_type=ICMP_MASKREPLY; + break; + case ICMP_MASKREPLY: + icmp_recv_type=ICMP_MASK; + break; + case ICMP_ECHO: + icmp_recv_type=ICMP_ECHOREPLY; + break; + case ICMP_ECHOREPLY: + icmp_recv_type=ICMP_ECHO; + break; + + /* These don't generate any response so we behave different */ + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_ROUTERADVERT: + case ICMP_ROUTERSOLICIT: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: + case ICMP_TRACEROUTE: + default: + skip_icmp_matching=true; + break; + } + /* We have an specific ICMP type to look for */ + if(!skip_icmp_matching){ + Snprintf(buffer, 1024-strlen(filterstring), "(icmp and icmp[icmptype] = %d) ", icmp_recv_type); + }else{ + /* If we are sending messages that don't generate responses, receive anything but the type we send. + * This conflicts in some cases with the conditions added at the end of this functions where we + * allow ICMP error messages to be received. However, this is not a problem since we are already + * filtering out our own outgoing packets and the packets that are not for us. */ + Snprintf(buffer, 1024-strlen(filterstring), "(icmp and icmp[icmptype] != %d) ", icmp_send_type); + } + break; + + case ARP: + arp_send_type= o.issetARPOpCode() ? o.getARPOpCode() : DEFAULT_ARP_OP; + switch(arp_send_type){ + case OP_ARP_REQUEST: + arp_recv_type=OP_ARP_REPLY; + break; + case OP_ARP_REPLY: /* If we are sending replies we probably want to see */ + arp_recv_type=OP_ARP_REQUEST; /* the requests that are being put into the network */ + break; + case OP_RARP_REQUEST: + arp_recv_type=OP_RARP_REPLY; + break; + case OP_RARP_REPLY: + arp_recv_type=OP_RARP_REQUEST; + break; + case OP_DRARP_REQUEST: + arp_recv_type=OP_DRARP_REPLY; + break; + case OP_DRARP_REPLY: + arp_recv_type=OP_DRARP_REQUEST; + break; + case OP_DRARP_ERROR: + arp_recv_type=OP_DRARP_REQUEST; + break; + case OP_INARP_REQUEST: + arp_recv_type=OP_INARP_REPLY; + break; + case OP_INARP_REPLY: + arp_recv_type=OP_INARP_REQUEST; + break; + default: + skip_arp_matching=true; + break; + } + if(!skip_arp_matching){ + /* If we are doing DRARP we also want to receive DRARP errors */ + if(arp_send_type==OP_DRARP_REQUEST || arp_send_type==OP_DRARP_REPLY) + Snprintf(buffer, 1024-strlen(filterstring), "arp and arp[6]==0x00 and (arp[7]==0x%02X or arp[7]==0x%02X)", (u8)arp_recv_type, (u8)OP_DRARP_ERROR); + else + Snprintf(buffer, 1024-strlen(filterstring), "arp and arp[6]==0x00 and arp[7]==0x%02X", (u8)arp_recv_type); + }else{ + /* If we are sending things like ATMARP's ARP_NAK, we just skip the type we send and receive all others */ + Snprintf(buffer, 1024-strlen(filterstring), "arp and arp[6]==0x00 and arp[7]!=0x%02X", (u8)arp_send_type); + } + break; + } + + /* We also want to get all ICMP error messages */ + if(o.getMode()!=ARP){ + buffer=filterstring+strlen(filterstring); + Snprintf(buffer, 1024-strlen(filterstring), "or (icmp and (icmp[icmptype] = %d or icmp[icmptype] = %d or icmp[icmptype] = %d or icmp[icmptype] = %d or icmp[icmptype] = %d)) )" , + ICMP_UNREACH, ICMP_SOURCEQUENCH, ICMP_REDIRECT, ICMP_TIMXCEED, ICMP_PARAMPROB); + } + nping_print(DBG_1, "BPF-filter: %s", filterstring); + return filterstring; +} /* End of getBPFFilterString() */ + + + +/** Helper to check whether a received ICMP-in-IPv4 packet is related to a probe + * we might have sent. Returns non-NULL target pointer if found. Otherwise + * returns NULL. */ +static NpingTarget *is_response_icmp(const unsigned char *packet, unsigned int packetlen) { + const void *data; + unsigned int datalen; + struct abstract_ip_hdr packethdr; + NpingTarget *trg; + + /* Parse the outermost IP header (for its source address). */ + datalen = packetlen; + data = ip_get_data(packet, &datalen, &packethdr); + if (data == NULL) + return NULL; + + trg = o.targets.findTarget(&packethdr.src); + if (trg != NULL) { + if (packethdr.proto == IPPROTO_ICMP) { + struct icmp_hdr *icmp; + struct icmp_msg_echo *echo; + + if (datalen < 4) + return NULL; + icmp = (struct icmp_hdr *) data; + /* In case of echo reply, make sure the ICMP ID is the same as we + are sending. */ + if (icmp->icmp_type == ICMP_ECHOREPLY) { + u16 expected_id; + + if (o.issetICMPIdentifier()) + expected_id = o.getICMPIdentifier(); + else + expected_id = trg->getICMPIdentifier(); + + if (datalen < 8) + return NULL; + echo = (struct icmp_msg_echo *) ((char *) icmp + 4); + if (ntohs(echo->icmp_id) != expected_id) + return NULL; + } + } + return trg; + } + + /* If that didn't work, check if this is ICMP with an encapsulated IP + header. */ + if (packethdr.proto == IPPROTO_ICMP) { + struct ip *ip; + unsigned int iplen; + struct sockaddr_storage ss; + struct sockaddr_in *sin = (struct sockaddr_in *) &ss; + + if (datalen < 8) + return NULL; + ip = (struct ip *) ((char *) data + 8); + iplen = datalen - 8; + /* Make sure there is enough header to have a dest address. */ + if (iplen < 20) + return NULL; + if (ip->ip_v != 4) + return NULL; + sin->sin_family = AF_INET; + sin->sin_addr = ip->ip_dst; + trg = o.targets.findTarget(&ss); + + return trg; + } + + return NULL; +} + + +/** This function handles nsock events related to raw packet modes + * TCP, UDP, ICMP and ARP (TCP_CONNEC and UDP_UNPRIV are handled by their + * own even handlers). + * Basically the handler receives nsock events and takes the appropriate + * action based on event type. This is basically what it does for each event: + * + * TIMERS: start() schedules probe transmissions through timers. + * That's how we manage to send probes at a given rate. When the alarm goes + * off, nsock generates a timer event so, in this function, we take the + * supplied "mydata" pointer, convert it to a sendpkt_t pointer and, from + * the info stored there, we send the packet though a raw socket. + * + * PCAP READS: start() also schedules pcap read operations so, + * whenever pcap has capture a packet, nsock generates a pcap read event so + * we just read the capture data, update the statistics and print the packet + * to stdout. + * */ +void ProbeMode::probe_nping_event_handler(nsock_pool nsp, nsock_event nse, void *mydata) { + + nsock_iod nsi = nse_iod(nse); + enum nse_status status = nse_status(nse); + enum nse_type type = nse_type(nse); + sendpkt_t *mypacket = (sendpkt_t *)mydata; + u8 pktinfobuffer[512+1]; + char *hex=NULL; + char final_output[65535]; + nsock_event_id ev_id; + struct timeval *t = (struct timeval *)nsock_gettimeofday(); + const unsigned char *packet=NULL; + const unsigned char *link=NULL; + size_t linklen=0; + size_t packetlen=0; + u16 *ethtype=NULL; + u8 buffer[512+1]; + size_t link_offset=0; + static struct timeval pcaptime; + static struct timeval prevtime; + NpingTarget *trg=NULL; + u16 *prt=NULL; + u8 proto=0; + bool ip=false; + memset(final_output, 0, sizeof(final_output)); + + nping_print(DBG_4, "nping_event_handler(): Received callback of type %s with status %s", + nse_type2str(type), nse_status2str(status)); + + if (status == NSE_STATUS_SUCCESS ) { + + switch(type) { + + /* This is actually for our raw packet probe transmissions */ + case NSE_TYPE_TIMER: + if( mypacket!=NULL){ + + /* Send the packet */ + send_packet(mypacket->target, mypacket->rawfd, mypacket->pkt, mypacket->pktLen); + o.setLastPacketSentTime(*t); + + /* Print packet contents */ + if( o.sendEth() ) + link_offset=14; + + if( mypacket->type==PKT_TYPE_ARP_RAW ) + getPacketStrInfo("ARP",mypacket->pkt+14, mypacket->pktLen-14, pktinfobuffer, 512); + else if ( o.ipv6UsingSocket() ){ + size_t sslen; + struct sockaddr_storage ss_src; + struct sockaddr_storage ss_dst; + mypacket->target->getSourceSockAddr(&ss_src, &sslen); + mypacket->target->getTargetSockAddr(&ss_dst, &sslen); + getPacketStrInfo("IPv6_NO_HEADER", mypacket->pkt, mypacket->pktLen, pktinfobuffer, 512, &ss_src, &ss_dst ); + } + else + getPacketStrInfo("IP", mypacket->pkt+link_offset, mypacket->pktLen-link_offset, pktinfobuffer, 512); + + o.stats.addSentPacket(mypacket->pktLen); + if( o.getMode()==TCP || o.getMode()==UDP){ + mypacket->target->setProbeSentTCP(0, mypacket->dstport); + }else if (o.getMode()==ICMP){ + mypacket->target->setProbeSentICMP(0,0); + } + if( o.showSentPackets() ){ + nping_print(VB_0,"SENT (%.4fs) %s", o.stats.elapsedRuntime(t), pktinfobuffer ); + if( o.getVerbosity() >= VB_3 ) + luis_hdump((char*)mypacket->pkt, mypacket->pktLen); + } + } + break; + + + + case NSE_TYPE_PCAP_READ: + + /* Read a packet */ + nse_readpcap(nse, &link, &linklen, &packet, &packetlen, NULL, &pcaptime); + + /* If we are on a Ethernet network, extract the next packet protocol + * from the Ethernet frame. */ + if( nsock_iod_linktype(nsi) == DLT_EN10MB ){ + ethtype=(u16*)(link+12); + *ethtype=ntohs(*ethtype); + switch(*ethtype){ + case ETHTYPE_IPV4: + case ETHTYPE_IPV6: + ip=true; + break; + case ETHTYPE_ARP: + case ETHTYPE_RARP: + ip=false; + break; + default: + nping_warning(QT_1, "RCVD (%.4fs) Unsupported protocol (Ethernet type %02X)", o.stats.elapsedRuntime(t), *ethtype); + print_hexdump(VB_3, packet, packetlen); + return; + break; + } + /* If link layer is not Ethernet, check if the first bits of the + * packets are 4 (IPv4) or 6 (IPv6). This is not exact science but + * it should be OK for the moment since we should never get non + * IP packets (the BPF filter prevents that) */ + }else{ + IPv4Header iphdr; + if( iphdr.storeRecvData(packet, packetlen)!=OP_SUCCESS ) + nping_warning(QT_1, "RCVD (%.4fs) Bogus packet received.", o.stats.elapsedRuntime(t)); + if( iphdr.getVersion()==4 || iphdr.getVersion()==6){ + ip=true; + }else{ + nping_warning(QT_1, "RCVD (%.4fs) Unsupported protocol.", o.stats.elapsedRuntime(t)); + print_hexdump(VB_3, packet, packetlen); + return; + } + } + + /* Packet is IP */ + if(ip){ + getPacketStrInfo("IP",(const u8*)packet, packetlen, buffer, 512); + proto = getProtoFromIPPacket((u8*)packet, packetlen); + if (proto == IPPROTO_UDP || proto == IPPROTO_TCP){ + /* for UDP/TCP we print out and update the global total straight away + since we know that pcap only found packets from connections that we + opened */ + snprintf(final_output, sizeof(final_output), "RCVD (%.4fs) %s\n", o.stats.elapsedRuntime(t), buffer); + if( o.getVerbosity() >= VB_3 ){ + hex=hexdump(packet, packetlen); + strncat(final_output, hex, sizeof(final_output)-1); + free(hex); + } + prevtime=pcaptime; + + /* Statistics */ + o.stats.addRecvPacket(packetlen); + + /* Then we check for a target and a port and do the individual statistics */ + trg=o.targets.findTarget( getSrcSockAddrFromIPPacket((u8*)packet, packetlen) ); + + if(trg != NULL){ + prt=getSrcPortFromIPPacket((u8*)packet, packetlen); + if( prt!=NULL ) + trg->setProbeRecvTCP(*prt, 0); + } + }else if (proto==IPPROTO_ICMP || proto==IPPROTO_ICMPV6){ + /* we look for a target based on first src addr and second the dest addr of + the packet header which is returned in the ICMP packet */ + trg = is_response_icmp(packet, packetlen); + + /* In the case of ICMP we only do any printing and statistics if we + found a target - otherwise it could be a packet that is nothing + to do with us */ + if(trg!=NULL){ + snprintf(final_output, sizeof(final_output), "RCVD (%.4fs) %s\n", o.stats.elapsedRuntime(t), buffer); + if( o.getVerbosity() >= VB_3 ){ + hex=hexdump(packet, packetlen); + strncat(final_output, hex, sizeof(final_output)-1); + free(hex); + } + prevtime=pcaptime; + o.stats.addRecvPacket(packetlen); + trg->setProbeRecvICMP(0, 0); + } + } + + /* Packet is ARP */ + }else{ + getPacketStrInfo("ARP",(const u8*)packet, packetlen, buffer, 512); + nping_print(VB_0, "RCVD (%.4fs) %s", o.stats.elapsedRuntime(t), buffer ); + o.stats.addRecvPacket(packetlen); + print_hexdump(VB_3 | NO_NEWLINE, packet, packetlen); + /* TODO: find target and call setProbeRecvARP() */ + } + + if( o.getRole() == ROLE_CLIENT ){ + int delay=(int)MIN(o.getDelay()*0.33, 333); + ev_id=nsock_timer_create(nsp, probe_delayed_output_handler, delay, NULL); + o.setDelayedRcvd(final_output, ev_id); + } + else + nping_print(VB_0|NO_NEWLINE, "%s", final_output); + break; + + /* In theory we should never get these kind of events in this handler + * because no code schedules them */ + case NSE_TYPE_CONNECT: + case NSE_TYPE_CONNECT_SSL: + case NSE_TYPE_READ: + case NSE_TYPE_WRITE: + nping_fatal(QT_3, "Bug in nping_event_handler(). Received %s event.", nse_type2str(type)); + break; + + default: + nping_fatal(QT_3, "nping_event_handler(): Bogus event type."); + break; + + } /* switch(type) */ + + + } else if (status == NSE_STATUS_EOF) { + nping_print(DBG_4, "nping_event_handler(): Unexpected behaviour: Got EOF. Please report this bug.\n"); + } else if (status == NSE_STATUS_ERROR) { + nping_warning(QT_2, "nping_event_handler(): %s failed: %s", nse_type2str(type), strerror(socket_errno())); + } else if (status == NSE_STATUS_TIMEOUT) { + nping_print(DBG_4,"nping_event_handler(): %s timeout: %s\n", nse_type2str(type), strerror(socket_errno())); + } else if (status == NSE_STATUS_CANCELLED) { + nping_warning(QT_2, "nping_event_handler(): %s canceled: %s", nse_type2str(type), strerror(socket_errno())); + } else if (status == NSE_STATUS_KILL) { + nping_warning(QT_2, "nping_event_handler(): %s killed: %s", nse_type2str(type), strerror(socket_errno())); + } else{ + nping_warning(QT_2, "nping_event_handler(): Unknown status code %d\n", status); + } + return; + +} /* End of nping_event_handler() */ + + +/** Prints the supplied string when the nsock timer event goes off. This is used + * by the echo client to delay output of received packets for a bit, so we + * receive the echoed packet and print it (CAPT) before the RCVD one. */ +void ProbeMode::probe_delayed_output_handler(nsock_pool nsp, nsock_event nse, void *mydata){ + char *str=NULL; + if((str=o.getDelayedRcvd(NULL))!=NULL){ + printf("%s", str); + free(str); + } + return; +} /* End of probe_delayed_output_handler() */ + + +/* DEFAULT_MAX__DESCRIPTORS. is a hardcoded value for the maximum number of + * opened descriptors in the current system. Nping tries to determine that + * limit at run time, but sometimes it can't and the limit defaults to + * DEFAULT_MAX_DESCRIPTORS. */ +#ifndef MACOSX + #define DEFAULT_MAX_DESCRIPTORS 1024 +#else + #define DEFAULT_MAX_DESCRIPTORS 256 +#endif + +/* When requesting a large number of descriptors from the system (TCP-connect + * mode and UDP unprivileged mode), this is the number of descriptors that need + * to be reserved for things like stdin, stdout, echo mode sockets, data files, + * etc. */ +#define RESERVED_DESCRIPTORS 8 + +/* Default timeout for UDP socket nsock_read() operations */ +#define DEFAULT_UDP_READ_TIMEOUT_MS 1000 + +/** This function handles nsock events related to TCP_CONNECT mode + * Basically the handler receives nsock events and takes the appropriate + * action based on event type. This is basically what it does for each event: + * + * TIMERS: normalProbeMode() schedules TCP connections through timers, that's + * how we manage to start the TCP handshakes at a given rate. When the alarm + * goes off, nsock generates a timer event so, in this function, we take the + * supplied "mydata" pointer, convert it to a sendpkt_t pointer and, from + * the info stored there, we schedule a nsock_connect_tcp() event. This means + * that nsock will initiate a TCP handshake and return. Whenever the handshake + * is completed, nsock will generate a CONNECT event to indicate it so we + * know the other peer was alive and willing to TCP-handshake with us. + * + * CONNECTS: These events are scheduled by the code that handles timer events. + * As described above, nsock generates a connect event when handshakes have + * completed. When we get a connect event we just tell the user the handshake + * was successful and update the stats. + * */ +/* This is the callback function for the nsock events produced in TCP-Connect + * mode. */ +void ProbeMode::probe_tcpconnect_event_handler(nsock_pool nsp, nsock_event nse, void *mydata) { + + nsock_iod nsi; /**< Current nsock IO descriptor. */ + enum nse_status status; /**< Current nsock event status. */ + enum nse_type type; /**< Current nsock event type. */ + sendpkt_t *mypacket=NULL; /**< Info about the current probe. */ + struct timeval *t=NULL; /**< Current time obtained through nsock. */ + struct sockaddr_storage to; /**< Stores destination address for Tx. */ + struct sockaddr_in *to4=NULL; /**< |_ Sockaddr for IPv4. */ + struct sockaddr_in6 *to6=NULL; /**< |_ Sockaddr for IPv6. */ + struct sockaddr_storage peer; /**< Stores source address for Rx. */ + struct sockaddr_in *peer4=NULL; /**< |_ Sockaddr for IPv4. */ + struct sockaddr_in6 *peer6=NULL; /**< |_ Sockaddr for IPv6. */ + int family=0; /**< Hill hold Rx address family. */ + char ipstring[128]; /**< To print IP Addresses. */ + u16 peerport=0; /**< To hold peer's port number. */ + size_t sslen=0; /**< To store length of sockaddr structs. */ + static nsock_iod *fds=NULL; /**< IODs for multiple parallel connections*/ + static int max_iods=0; /**< Number of IODS in "fds" */ + static u32 packetno=0; /**< Packets sent from this handler. */ + NpingTarget *trg=NULL; /**< Target we look up in NpingTargets:: */ + + /* Initializations */ + nsi = nse_iod(nse); + status = nse_status(nse); + type = nse_type(nse); + mypacket = (sendpkt_t *)mydata; + t = (struct timeval *)nsock_gettimeofday(); + to6=(struct sockaddr_in6 *)&to; + to4=(struct sockaddr_in *)&to; + peer4=(struct sockaddr_in *)&peer; + peer6=(struct sockaddr_in6 *)&peer; + memset(&to, 0, sizeof(struct sockaddr_storage)); + memset(&peer, 0, sizeof(struct sockaddr_storage)); + + /* Try to determine the max number of opened descriptors. If the limit is + * less than than we need, try to increase it. */ + if(fds==NULL){ + max_iods=get_max_open_descriptors()-RESERVED_DESCRIPTORS; + if( o.getTotalProbes() > max_iods ){ + max_iods=set_max_open_descriptors( o.getTotalProbes() )-RESERVED_DESCRIPTORS; + } + /* If we couldn't determine the limit, just use a predefined value */ + if(max_iods<=0) + max_iods=DEFAULT_MAX_DESCRIPTORS-RESERVED_DESCRIPTORS; + /* Allocate space for nsock_iods */ + if( (fds=(nsock_iod *)calloc(max_iods, sizeof(nsock_iod)))==NULL ){ + /* If we can't allocate for that many descriptors, reduce our requirements */ + max_iods=DEFAULT_MAX_DESCRIPTORS-RESERVED_DESCRIPTORS; + if( (fds=(nsock_iod *)calloc(max_iods, sizeof(nsock_iod)))==NULL ){ + nping_fatal(QT_3, "ProbeMode::probe_tcpconnect_event_handler(): Not enough memory"); + } + } + nping_print(DBG_7, "%d descriptors needed, %d available", o.getTotalProbes(), max_iods); + } + + nping_print(DBG_4, "tcpconnect_event_handler(): Received callback of type %s with status %s", nse_type2str(type), nse_status2str(status)); + + if (status == NSE_STATUS_SUCCESS ) { + + switch(type) { + + /* TCP Handshake was completed successfully */ + case NSE_TYPE_CONNECT: + if( mypacket==NULL ) + nping_fatal(QT_3, "tcpconnect_event_handler(): NULL value supplied."); + /* Determine which target are we dealing with */ + nsock_iod_get_communication_info(nsi, NULL, &family, NULL, + (struct sockaddr*)&peer, + sizeof(struct sockaddr_storage) ); + if(family==AF_INET6){ + inet_ntop(AF_INET6, &peer6->sin6_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer6->sin6_port); + }else{ + inet_ntop(AF_INET, &peer4->sin_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer4->sin_port); + } + + /* We cannot trust "mydata" pointer because it's contents may have + * been overwritten by the time we get the CONNECT event, so we have + * to look up the target by its IP address. */ + trg=o.targets.findTarget( &peer ); + if(trg!=NULL){ + if ( trg->getSuppliedHostName() ) + nping_print(VB_0,"RCVD (%.4fs) Handshake with %s:%d (%s:%d) completed", + o.stats.elapsedRuntime(t), trg->getSuppliedHostName(), peerport, ipstring, peerport ); + else + nping_print(VB_0,"RCVD (%.4fs) Handshake with %s:%d completed", o.stats.elapsedRuntime(t), ipstring, peerport ); + trg->setProbeRecvTCP( peerport , 0); + }else{ + nping_print(VB_0,"RCVD (%.4fs) Handshake with %s:%d completed", o.stats.elapsedRuntime(t), ipstring, peerport ); + } + o.stats.addRecvPacket(40); /* Estimation Dst>We 1 TCP SYN|ACK */ + break; + + + /* We need to start an scheduled TCP Handshake. Theoretically in this + * case we can trust the supplied "mydata" structure because all our timers + * have the same exact time and Nsock should do FIFO with list of timer + * events. Additionally, even if that failed, more than MAX_PKT timer + * events would have to overlap to "corrupt" "mydata". Even if that's + * the case, it is only a problem when dealing with multiple targets host + * and/or multiple target ports. */ + case NSE_TYPE_TIMER: + if( mypacket==NULL ) + nping_fatal(QT_3, "tcpconnect_event_handler():2: NULL value supplied."); + + /* Fill the appropriate sockaddr for the connect() call */ + if( o.getIPVersion() == IP_VERSION_6 ){ + to6->sin6_addr=mypacket->target->getIPv6Address(); + to6->sin6_family = AF_INET6; + to6->sin6_port = htons( mypacket->dstport ); + sslen=sizeof(struct sockaddr_in6); + }else{ + to4->sin_addr=mypacket->target->getIPv4Address(); + to4->sin_family = AF_INET; + to4->sin_port = htons( mypacket->dstport ); + sslen=sizeof(struct sockaddr_in); + } + + /* We need to keep many IODs open in parallel but we don't allocate + * millions, just as many as the OS let us (max number of open files). + * If we run out of them, we just start overwriting the oldest one. + * If we don't have a response by that time we probably aren't gonna + * get any, so it shouldn't be a big problem. */ + if( packetno>(u32)max_iods ){ + nsock_iod_delete(fds[packetno%max_iods], NSOCK_PENDING_SILENT); + } + /* Create new IOD for connects */ + if ((fds[packetno%max_iods] = nsock_iod_new(nsp, NULL)) == NULL) + nping_fatal(QT_3, "tcpconnect_event_handler(): Failed to create new nsock_iod.\n"); + + /* Set socket source address. This allows setting things like custom source port */ + struct sockaddr_storage ss; + nsock_iod_set_localaddr(fds[packetno%max_iods], o.getSourceSockAddr(&ss), sizeof(sockaddr_storage)); + /*Set socket options for REUSEADDR*/ + //setsockopt(nsock_iod_get_sd(fds[packetno%max_iods]),SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)); + + nsock_connect_tcp(nsp, fds[packetno%max_iods], tcpconnect_event_handler, 100000, mypacket, (struct sockaddr *)&to, sslen, mypacket->dstport); + if( o.showSentPackets() ){ + if ( mypacket->target->getSuppliedHostName() ) + nping_print(VB_0,"SENT (%.4fs) Starting TCP Handshake > %s:%d (%s:%d)", o.stats.elapsedRuntime(NULL), mypacket->target->getSuppliedHostName(), mypacket->dstport ,mypacket->target->getTargetIPstr(), mypacket->dstport); + else + nping_print(VB_0,"SENT (%.4fs) Starting TCP Handshake > %s:%d", o.stats.elapsedRuntime(NULL), mypacket->target->getTargetIPstr(), mypacket->dstport); + } + packetno++; + o.stats.addSentPacket(80); /* Estimation Src>Dst 1 TCP SYN && TCP ACK */ + mypacket->target->setProbeSentTCP(0, mypacket->dstport); + break; + + case NSE_TYPE_WRITE: + case NSE_TYPE_READ: + case NSE_TYPE_PCAP_READ: + case NSE_TYPE_CONNECT_SSL: + nping_warning(QT_2,"tcpconnect_event_handler(): Unexpected behaviour, %s event received . Please report this bug.", nse_type2str(type)); + break; + + default: + nping_fatal(QT_3, "tcpconnect_event_handler(): Bogus event type (%d). Please report this bug.", type); + break; + + } /* switch(type) */ + + + } else if (status == NSE_STATUS_EOF) { + nping_print(DBG_4, "tcpconnect_event_handler(): Unexpected behaviour: Got EOF. Please report this bug.\n"); + } else if (status == NSE_STATUS_ERROR) { + /** In my tests with Nping and Wireshark, I've seen that we get NSE_STATUS_ERROR + * whenever we start a TCP handshake but our peer sends a TCP RST packet back + * denying the connection. So in this case, we inform the user (as opposed + * to saying nothing, that's what we do when we don't get responses, e.g: + * when trying to connect to filtered ports). This is not 100% accurate + * because there may be other reasons why ge get NSE_STATUS_ERROR so that's + * why we say "Possible TCP RST received". */ + if ( type == NSE_TYPE_CONNECT ){ + nsock_iod_get_communication_info(nsi, NULL, &family, NULL, (struct sockaddr*)&peer, sizeof(struct sockaddr_storage) ); + if(family==AF_INET6){ + inet_ntop(AF_INET6, &peer6->sin6_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer6->sin6_port); + }else{ + inet_ntop(AF_INET, &peer4->sin_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer4->sin_port); + } + nping_print(VB_0,"RCVD (%.4fs) Possible TCP RST received from %s:%d --> %s", o.stats.elapsedRuntime(t),ipstring, peerport, strerror(nse_errorcode(nse)) ); + } + else + nping_warning(QT_2,"ERR: (%.4fs) %s to %s:%d failed: %s", o.stats.elapsedRuntime(t), nse_type2str(type), ipstring, peerport, strerror(socket_errno())); + } else if (status == NSE_STATUS_TIMEOUT) { + nping_print(DBG_4, "tcpconnect_event_handler(): %s timeout: %s\n", nse_type2str(type), strerror(socket_errno())); + } else if (status == NSE_STATUS_CANCELLED) { + nping_print(DBG_4, "tcpconnect_event_handler(): %s canceled: %s", nse_type2str(type), strerror(socket_errno())); + } else if (status == NSE_STATUS_KILL) { + nping_print(DBG_4, "tcpconnect_event_handler(): %s killed: %s", nse_type2str(type), strerror(socket_errno())); + } else{ + nping_warning(QT_2, "tcpconnect_event_handler(): Unknown status code %d. Please report this bug.", status); + } + return; + +} /* End of tcpconnect_event_handler() */ + + + + + +/** This function handles nsock events related to UDP_UNPRIV mode. + * Basically the handler receives nsock events and takes the appropriate + * action based on event type. This is basically what it does for each event: + * + * TIMERS: normalProbeMode() schedules UDP packet transmissions through timers, + * that's how we manage to send the packets at a given rate. When the alarm + * goes off, nsock generates a timer event so, in this function, we take the + * supplied "mydata" pointer, convert it to a sendpkt_t pointer and, from + * the info stored there, we schedule a nsock_connect_udp() event. This means + * that nsock will perform the necessary system calls to obtain a UDP socket + * suitable to transmit information to our target host. We also schedule + * a write operation, since the connect_udp() doesn't do anything useful + * really and what we want to do is to actually send a TCP packet. + * + * + * CONNECTS: These events generated by nsock for consistency with the + * behavior in TCP connects. They are pretty useless. They merely indicate + * that nsock successfully obtained a UDP socket ready to allow sending + * packets to the appropriate target. We basically don't do anything when + * that event is received, just print a message if we are un debugging mode. + * + * WRITES: When we get event WRITE it means that nsock actually managed to + * get our data sent to the target. In this case, we inform the user that + * the packet has been sent, and we schedule a READ operation, to see + * if our peer actually returns any data. + * + * READS: When we get this event it means that the other end actually sent + * some data back to us. What we do is read that data, tell the user that + * we received some bytes and update statistics. + * + * */ +void ProbeMode::probe_udpunpriv_event_handler(nsock_pool nsp, nsock_event nse, void *mydata) { + + nsock_iod nsi; /**< Current nsock IO descriptor. */ + enum nse_status status; /**< Current nsock event status. */ + enum nse_type type; /**< Current nsock event type. */ + sendpkt_t *mypacket=NULL; /**< Info about the current probe. */ + struct timeval *t=NULL; /**< Current time obtained through nsock. */ + struct sockaddr_storage to; /**< Stores destination address for Tx. */ + struct sockaddr_in *to4=NULL; /**< |_ Sockaddr for IPv4. */ + struct sockaddr_in6 *to6=NULL; /**< |_ Sockaddr for IPv6. */ + struct sockaddr_storage peer; /**< Stores source address for Rx. */ + struct sockaddr_in *peer4=NULL; /**< |_ Sockaddr for IPv4. */ + struct sockaddr_in6 *peer6=NULL; /**< |_ Sockaddr for IPv6. */ + int family=0; /**< Hill hold Rx address family. */ + char ipstring[128]; /**< To print IP Addresses. */ + u16 peerport=0; /**< To hold peer's port number. */ + size_t sslen=0; /**< To store length of sockaddr structs. */ + static nsock_iod *fds=NULL; /**< IODs for multiple parallel connections*/ + static int max_iods=0; /**< Number of IODS in "fds" */ + static u32 packetno=0; /**< Packets sent from this handler. */ + int readbytes=0; /**< Bytes read in total. */ + char *readbuff=NULL; /**< Hill hold read data. */ + static size_t sentbytes=0; /**< Payload bytes sent in each UDP packet */ + NpingTarget *trg=NULL; /**< Target we look up in NpingTargets:: */ + + /* Initializations */ + nsi = nse_iod(nse); + status = nse_status(nse); + type = nse_type(nse); + mypacket = (sendpkt_t *)mydata; + t = (struct timeval *)nsock_gettimeofday(); + to6=(struct sockaddr_in6 *)&to; + to4=(struct sockaddr_in *)&to; + peer4=(struct sockaddr_in *)&peer; + peer6=(struct sockaddr_in6 *)&peer; + memset(&to, 0, sizeof(struct sockaddr_storage)); + memset(&peer, 0, sizeof(struct sockaddr_storage)); + + /* Try to determine the max number of opened descriptors. If the limit is + * less than than we need, try to increase it. */ + if(fds==NULL){ + max_iods=get_max_open_descriptors()-RESERVED_DESCRIPTORS; + if( o.getTotalProbes() > max_iods ){ + max_iods=set_max_open_descriptors( o.getTotalProbes() )-RESERVED_DESCRIPTORS; + } + /* If we couldn't determine the limit, just use a predefined value */ + if(max_iods<=0) + max_iods=DEFAULT_MAX_DESCRIPTORS-RESERVED_DESCRIPTORS; + /* Allocate space for nsock_iods */ + if( (fds=(nsock_iod *)calloc(max_iods, sizeof(nsock_iod)))==NULL ){ + /* If we can't allocate for that many descriptors, reduce our requirements */ + max_iods=DEFAULT_MAX_DESCRIPTORS-RESERVED_DESCRIPTORS; + if( (fds=(nsock_iod *)calloc(max_iods, sizeof(nsock_iod)))==NULL ){ + nping_fatal(QT_3, "ProbeMode:probe_udpunpriv_event_handler(): Not enough memory"); + } + } + nping_print(DBG_7, "%d descriptors needed, %d available", o.getTotalProbes(), max_iods); + } + + nping_print(DBG_4, "udpunpriv_event_handler(): Received callback of type %s with status %s", nse_type2str(type), nse_status2str(status)); + + if (status == NSE_STATUS_SUCCESS ) { + + switch(type) { + + + /* This is a bit stupid but, for consistency, Nsock creates an event of + * type NSE_TYPE_CONNECT after a call to nsock_connect_udp() is made. + * Basically this just means that nsock successfully obtained a UDP socket + * ready to allow sending packets to the appropriate target. */ + case NSE_TYPE_CONNECT: + nping_print(DBG_3,"Nsock UDP \"connection\" completed successfully."); + break; + + + + /* We need to start an scheduled UDP packet transmission. */ + case NSE_TYPE_TIMER: + if( mypacket==NULL ) + nping_fatal(QT_3, "udpunpriv_event_handler():: NULL value supplied."); + + /* Fill the appropriate sockaddr for the connect() call */ + if( o.getIPVersion() == IP_VERSION_6 ){ + to6->sin6_addr=mypacket->target->getIPv6Address(); + to6->sin6_family = AF_INET6; + to6->sin6_port = htons( mypacket->dstport ); + sslen=sizeof(struct sockaddr_in6); + }else{ + to4->sin_addr=mypacket->target->getIPv4Address(); + to4->sin_family = AF_INET; + to4->sin_port = htons( mypacket->dstport ); + sslen=sizeof(struct sockaddr_in); + } + + /* We need to keep many IODs open in parallel but we don't allocate + * millions, just as many as the OS let us (max number of open files). + * If we run out of them, we just start overwriting the oldest one. + * If we don't have a response by that time we probably aren't gonna + * get any, so it shouldn't be a big problem. */ + if( packetno>(u32)max_iods ){ + nsock_iod_delete(fds[packetno%max_iods], NSOCK_PENDING_SILENT); + } + /* Create new IOD for connects */ + if ((fds[packetno%max_iods] = nsock_iod_new(nsp, NULL)) == NULL) + nping_fatal(QT_3, "Failed to create new nsock_iod. QUITTING.\n"); + + /* Set socket source address. This allows setting things like custom source port */ + struct sockaddr_storage ss; + nsock_iod_set_localaddr(fds[packetno%max_iods], o.getSourceSockAddr(&ss), sizeof(sockaddr_storage)); + + + /* I dunno if it's safe to schedule an nsock_write before we + * receive a NSE_TYPE_CONNECT event. The call to nsock_connect_udp() + * calls inheritable_socket() before returning which should mean + * an actual socket() call has been made before we nsock_write(). + * However, if the way nsock behaves changes in the future it may + * break this so we may need to place nsock_write() in + * "case NSE_TYPE_CONNECT:". We could do it right now but it may + * be a bit complicated due to the "packetno" index. + */ + nsock_connect_udp(nsp, fds[packetno%max_iods], udpunpriv_event_handler, mypacket, (struct sockaddr *)&to, sslen, mypacket->dstport); + nsock_write(nsp, fds[packetno%max_iods], udpunpriv_event_handler,100000, mypacket, (const char*)mypacket->pkt, mypacket->pktLen); + sentbytes=mypacket->pktLen; + packetno++; + break; + + + + + /* We get this event as a result of the nsock_write() call performed by + * the code in charge of dealing with the timer event. When we get this + * even it means that nsock successfully wrote data to the UDP socket so + * here we basically just print that we did send some data and we schedule + * a read operation. + */ + case NSE_TYPE_WRITE: + /* Determine which target are we dealing with */ + nsock_iod_get_communication_info(nsi, NULL, &family, NULL, (struct sockaddr*)&peer, sizeof(struct sockaddr_storage) ); + if(family==AF_INET6){ + inet_ntop(AF_INET6, &peer6->sin6_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer6->sin6_port); + }else{ + inet_ntop(AF_INET, &peer4->sin_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer4->sin_port); + } + + /* We cannot trust "mydata" pointer because it's contents may have + * been overwritten by the time we get the WRITE event, (this is + * unlikely but it may happen when sending probes at a very high rate). + * As a consequence, we have to look up the target by its IP address. */ + trg=o.targets.findTarget( &peer ); + if(trg!=NULL){ + if ( trg->getSuppliedHostName() ) + nping_print(VB_0,"SENT (%.4fs) UDP packet with %lu bytes to %s:%d (%s:%d)", o.stats.elapsedRuntime(NULL), (unsigned long int)sentbytes, trg->getSuppliedHostName(), peerport, ipstring, peerport ); + else + nping_print(VB_0,"SENT (%.4fs) UDP packet with %lu bytes to %s:%d", o.stats.elapsedRuntime(NULL), (unsigned long int)sentbytes, ipstring, peerport ); + trg->setProbeSentUDP( 0, peerport); + }else{ + nping_print(VB_0,"SENT (%.4fs) UDP packet with %lu bytes to %s:%d", o.stats.elapsedRuntime(t), (unsigned long int)sentbytes, ipstring, peerport ); + } + o.stats.addSentPacket(sentbytes); /* Here we don't count the headers, just payload bytes */ + + /* If user did not disable packet capture, schedule a read operation */ + if( !o.disablePacketCapture() ) + nsock_read(nsp, nsi, udpunpriv_event_handler, DEFAULT_UDP_READ_TIMEOUT_MS, mypacket); + break; + + + + /* We get this event when we've written some data to a UDP socket and + * the other end has sent some data back. In this case we read the data and + * inform the user of how many bytes we got. + */ + case NSE_TYPE_READ: + /* Do an actual read() of the recv data */ + readbuff=nse_readbuf(nse, &readbytes); + if(readbuff==NULL){ + nping_fatal(QT_3, "Error: nse_readbuff failed to read in the from the probe"); + } + /* Determine which target are we dealing with */ + nsock_iod_get_communication_info(nsi, NULL, &family, NULL, (struct sockaddr*)&peer, sizeof(struct sockaddr_storage) ); + if(family==AF_INET6){ + inet_ntop(AF_INET6, &peer6->sin6_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer6->sin6_port); + }else{ + inet_ntop(AF_INET, &peer4->sin_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer4->sin_port); + } + + /* Lookup our peer's NpingTarget entry */ + trg=o.targets.findTarget( &peer ); + if(trg!=NULL){ + if ( trg->getSuppliedHostName() ) + nping_print(VB_0,"RCVD (%.4fs) UDP packet with %d bytes from %s:%d (%s:%d)", o.stats.elapsedRuntime(NULL), readbytes, trg->getSuppliedHostName(), peerport, ipstring, peerport ); + else + nping_print(VB_0,"RCVD (%.4fs) UDP packet with %d bytes from %s:%d", o.stats.elapsedRuntime(NULL), readbytes, ipstring, peerport ); + trg->setProbeRecvUDP(peerport, 0); + }else{ + nping_print(VB_0,"RCVD (%.4fs) UDP packet with %d bytes from %s:%d", o.stats.elapsedRuntime(t), readbytes, ipstring, peerport ); + } + o.stats.addRecvPacket(readbytes); + break; + + + case NSE_TYPE_PCAP_READ: + case NSE_TYPE_CONNECT_SSL: + nping_warning(QT_2,"udpunpriv_event_handler(): Unexpected behavior, %s event received . Please report this bug.", nse_type2str(type)); + break; + + default: + nping_fatal(QT_3, "udpunpriv_event_handler(): Bogus event type (%d). Please report this bug.", type); + break; + + } /* switch(type) */ + + + } else if (status == NSE_STATUS_EOF) { + nping_print(DBG_4, "udpunpriv_event_handler(): Unexpected behaviour: Got EOF. Please report this bug.\n"); + } else if (status == NSE_STATUS_ERROR) { + nsock_iod_get_communication_info(nsi, NULL, &family, NULL, (struct sockaddr*)&peer, sizeof(struct sockaddr_storage) ); + if(family==AF_INET6){ + inet_ntop(AF_INET6, &peer6->sin6_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer6->sin6_port); + }else{ + inet_ntop(AF_INET, &peer4->sin_addr, ipstring, sizeof(ipstring)); + peerport=ntohs(peer4->sin_port); + } + nping_warning(QT_2,"ERR: (%.4fs) %s to %s:%d failed: %s", o.stats.elapsedRuntime(t), nse_type2str(type), ipstring, peerport, strerror(socket_errno())); + } else if (status == NSE_STATUS_TIMEOUT) { + nping_print(DBG_4, "udpunpriv_event_handler(): %s timeout: %s\n", nse_type2str(type), strerror(socket_errno())); + } else if (status == NSE_STATUS_CANCELLED) { + nping_print(DBG_4, "udpunpriv_event_handler(): %s canceled: %s", nse_type2str(type), strerror(socket_errno())); + } else if (status == NSE_STATUS_KILL) { + nping_print(DBG_4, "udpunpriv_event_handler(): %s killed: %s", nse_type2str(type), strerror(socket_errno())); + } + else{ + nping_warning(QT_2, "udpunpriv_event_handler(): Unknown status code %d. Please report this bug.", status); + } + return; + +} /* End of udpunpriv_event_handler() */ + + + +/* This handler is a wrapper for the ProbeMode::probe_nping_event_handler() + * method. We need this because C++ does not allow to use class methods as + * callback functions for things like signal() or the Nsock lib. */ +void nping_event_handler(nsock_pool nsp, nsock_event nse, void *arg){ + nping_print(DBG_4, "%s()", __func__); + ProbeMode::probe_nping_event_handler(nsp, nse, arg); + return; +} /* End of nping_event_handler() */ + + +/* This handler is a wrapper for the ProbeMode::probe_tcpconnect_event_handler() + * method. We need this because C++ does not allow to use class methods as + * callback functions for things like signal() or the Nsock lib. */ +void tcpconnect_event_handler(nsock_pool nsp, nsock_event nse, void *arg){ + nping_print(DBG_4, "%s()", __func__); + ProbeMode::probe_tcpconnect_event_handler(nsp, nse, arg); + return; +} /* End of tcpconnect_event_handler() */ + + +/* This handler is a wrapper for the ProbeMode::probe_udpunpriv_event_handler() + * method. We need this because C++ does not allow to use class methods as + * callback functions for things like signal() or the Nsock lib. */ +void udpunpriv_event_handler(nsock_pool nsp, nsock_event nse, void *arg){ + nping_print(DBG_4, "%s()", __func__); + ProbeMode::probe_udpunpriv_event_handler(nsp, nse, arg); + return; +} /* End of udpunpriv_event_handler() */ + + + +/* This handler is a wrapper for the ProbeMode::probe_delayed_output_handler() + * method. We need this because C++ does not allow to use class methods as + * callback functions for things like signal() or the Nsock lib. */ +void delayed_output_handler(nsock_pool nsp, nsock_event nse, void *arg){ + nping_print(DBG_4, "%s()", __func__); + ProbeMode::probe_delayed_output_handler(nsp, nse, arg); + return; +} /* End of udpunpriv_event_handler() */ |