1 
2 /***************************************************************************
3  * NpingTarget.cc -- The NpingTarget class encapsulates much of the        *
4  * information Nping has about a host. Things like next hop address or the *
5  * network interface that should be used to send probes to the target, are *
6  * stored in this class as they are determined.                            *
7  *                                                                         *
8  ***********************IMPORTANT NMAP LICENSE TERMS************************
9  *                                                                         *
10  * The Nmap Security Scanner is (C) 1996-2020 Insecure.Com LLC ("The Nmap  *
11  * Project"). Nmap is also a registered trademark of the Nmap Project.     *
12  *                                                                         *
13  * This program is distributed under the terms of the Nmap Public Source   *
14  * License (NPSL). The exact license text applying to a particular Nmap    *
15  * release or source code control revision is contained in the LICENSE     *
16  * file distributed with that version of Nmap or source code control       *
17  * revision. More Nmap copyright/legal information is available from       *
18  * https://nmap.org/book/man-legal.html, and further information on the    *
19  * NPSL license itself can be found at https://nmap.org/npsl. This header  *
20  * summarizes some key points from the Nmap license, but is no substitute  *
21  * for the actual license text.                                            *
22  *                                                                         *
23  * Nmap is generally free for end users to download and use themselves,    *
24  * including commercial use. It is available from https://nmap.org.        *
25  *                                                                         *
26  * The Nmap license generally prohibits companies from using and           *
27  * redistributing Nmap in commercial products, but we sell a special Nmap  *
28  * OEM Edition with a more permissive license and special features for     *
29  * this purpose. See https://nmap.org/oem                                  *
30  *                                                                         *
31  * If you have received a written Nmap license agreement or contract       *
32  * stating terms other than these (such as an Nmap OEM license), you may   *
33  * choose to use and redistribute Nmap under those terms instead.          *
34  *                                                                         *
35  * The official Nmap Windows builds include the Npcap software             *
36  * (https://npcap.org) for packet capture and transmission. It is under    *
37  * separate license terms which forbid redistribution without special      *
38  * permission. So the official Nmap Windows builds may not be              *
39  * redistributed without special permission (such as an Nmap OEM           *
40  * license).                                                               *
41  *                                                                         *
42  * Source is provided to this software because we believe users have a     *
43  * right to know exactly what a program is going to do before they run it. *
44  * This also allows you to audit the software for security holes.          *
45  *                                                                         *
46  * Source code also allows you to port Nmap to new platforms, fix bugs,    *
47  * and add new features.  You are highly encouraged to submit your         *
48  * changes as a Github PR or by email to the dev@nmap.org mailing list     *
49  * for possible incorporation into the main distribution. Unless you       *
50  * specify otherwise, it is understood that you are offering us very       *
51  * broad rights to use your submissions as described in the Nmap Public    *
52  * Source License Contributor Agreement. This is important because we      *
53  * fund the project by selling licenses with various terms, and also       *
54  * because the inability to relicense code has caused devastating          *
55  * problems for other Free Software projects (such as KDE and NASM).       *
56  *                                                                         *
57  * The free version of Nmap is distributed in the hope that it will be     *
58  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of  *
59  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties,        *
60  * indemnification and commercial support are all available through the    *
61  * Npcap OEM program--see https://nmap.org/oem.                            *
62  *                                                                         *
63  ***************************************************************************/
64 
65 
66 #ifdef WIN32
67 #include "nping_winconfig.h"
68 #endif
69 
70 #ifndef FQDN_LEN
71 #define FQDN_LEN 254
72 #endif
73 
74 #include "NpingTarget.h"
75 #include <dnet.h>
76 #include "nbase.h"
77 #include "nping.h"
78 #include "output.h"
79 #include "common.h"
80 #include "stats.h"
81 #include "common_modified.h"
82 
83 
84 
85 /** Constructor */
NpingTarget()86 NpingTarget::NpingTarget() {
87   this->Initialize();
88 } /* End of NpingTarget constructor */
89 
90 
91 /** Initializes object attributes  */
Initialize()92 void NpingTarget::Initialize() {
93   memset(this->devname, 0, sizeof(this->devname));
94   memset(this->devfullname, 0, sizeof(this->devfullname));
95   dev_type=devt_other;
96   directly_connected = -1;
97   distance = -1;
98   nameIPBuf = NULL;
99   hostname = NULL;
100   namedhost=-1;
101   targetname = NULL;
102   addressfamily=-1;
103   memset(&targetsock, 0, sizeof(targetsock));
104   memset(&sourcesock, 0, sizeof(sourcesock));
105   memset(&spoofedsrcsock, 0, sizeof(spoofedsrcsock));
106   memset(&nexthopsock, 0, sizeof(nexthopsock));
107   targetsocklen = 0;
108   sourcesocklen = 0;
109   spoofedsrcsocklen=0;
110   nexthopsocklen = 0;
111   spoofedsrc_set=false;
112   memset(this->targetipstring, 0, INET6_ADDRSTRLEN);
113   targetipstring_set=false;
114   memset(&MACaddress, 0, sizeof(MACaddress));
115   memset(&SrcMACaddress, 0, sizeof(SrcMACaddress));
116   memset(&NextHopMACaddress, 0, sizeof(NextHopMACaddress));
117   MACaddress_set = false;
118   SrcMACaddress_set = false;
119   NextHopMACaddress_set = false;
120   icmp_id = get_random_u16();
121   icmp_seq = 1;
122   memset(sentprobes, 0, sizeof(pktstat_t)* MAX_SENTPROBEINFO_ENTRIES);
123   current_stat=0;
124   total_stats=0;
125   sent_total=0;
126   recv_total=0;
127   max_rtt=0;
128   max_rtt_set=false;
129   min_rtt=0;
130   min_rtt_set=false;
131   avg_rtt=0;
132   avg_rtt_set=false;
133 } /* End of Initialize() */
134 
135 
136 /** Recycles the object by freeing internal objects and reinitializing
137   * to default state */
Recycle()138 void NpingTarget::Recycle() {
139   this->FreeInternal();
140   this->Initialize();
141 } /* End of Recycle() */
142 
143 
144 /** Destructor */
~NpingTarget()145 NpingTarget::~NpingTarget() {
146   this->FreeInternal();
147 } /* End of NpingTarget destructor */
148 
149 
150 /** Frees memory allocated inside this object */
FreeInternal()151 void NpingTarget::FreeInternal() {
152   /* Free the DNS name if we resolved one */
153   if (hostname){
154     free(hostname);
155     hostname=NULL;
156   }
157   /* Free user supplied host name if we got one */
158   if (targetname){
159     free(targetname);
160     targetname=NULL;
161   }
162   /* Free IP-Name info string */
163   if (nameIPBuf) {
164     free(nameIPBuf);
165     nameIPBuf = NULL;
166   }
167 } /* End of FreeInternal() */
168 
169 
170 /** Fills a sockaddr_storage with the AF_INET or AF_INET6 address
171      information of the target.  This is a preferred way to get the
172      address since it is portable for IPv6 hosts.  Returns 0 for
173      success. ss_len must be provided.  It is not examined, but is set
174      to the size of the sockaddr copied in. */
getTargetSockAddr(struct sockaddr_storage * ss,size_t * ss_len)175 int NpingTarget::getTargetSockAddr(struct sockaddr_storage *ss, size_t *ss_len) {
176   assert(ss);
177   assert(ss_len);
178   if (targetsocklen <= 0)
179     return 1;
180   assert(targetsocklen <= sizeof(*ss));
181   memcpy(ss, &targetsock, targetsocklen);
182   *ss_len = targetsocklen;
183   return 0;
184 
185 } /* End of getTargetSockAddr() */
186 
187 
188 /** Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted
189      to sockaddr_storage */
setTargetSockAddr(struct sockaddr_storage * ss,size_t ss_len)190 int NpingTarget::setTargetSockAddr(struct sockaddr_storage *ss, size_t ss_len) {
191   assert(ss_len > 0 && ss_len <= sizeof(*ss));
192   struct sockaddr_in *tmp=(struct sockaddr_in *)ss;
193   this->addressfamily=tmp->sin_family;
194   memcpy(&targetsock, ss, ss_len);
195   targetsocklen = ss_len;
196   generateIPString();
197   return OP_SUCCESS;
198 } /* End of setTargetSockAddr() */
199 
200 
201 /** Returns IPv4 host address or {0} if unavailable. */
getIPv4Address()202 struct in_addr NpingTarget::getIPv4Address() {
203   const struct in_addr *addy = getIPv4Address_aux();
204   struct in_addr in;
205   if (addy) return *addy;
206   in.s_addr = 0;
207   return in;
208 } /* End of getIPv4Address() */
209 
210 
211 /** Aux function for getIPv4Address() */
getIPv4Address_aux()212 const struct in_addr *NpingTarget::getIPv4Address_aux(){
213   struct sockaddr_in *sin = (struct sockaddr_in *) &targetsock;
214   if (sin->sin_family == AF_INET) {
215     return &(sin->sin_addr);
216   }
217   return NULL;
218 } /* End of getIPv4Address_aux() */
219 
220 
getIPv6Address_u8()221 u8 *NpingTarget::getIPv6Address_u8(){
222   const struct in6_addr *in = getIPv6Address_aux();
223   if( in==NULL )
224     return NULL;
225   else
226     return (u8*)in->s6_addr;
227 } /* End of getIPv6Address_u8() */
228 
229 
230 /** Returns IPv6 host address or {0} if unavailable. */
getIPv6Address()231 struct in6_addr NpingTarget::getIPv6Address() {
232   const struct in6_addr *addy = getIPv6Address_aux();
233   struct in6_addr in;
234   if (addy) return *addy;
235   memset(&in, 0, sizeof(struct in6_addr));
236   return in;
237 } /* End of getIPv6Address() */
238 
239 
240 /** Aux function for getIPv6Address() */
getIPv6Address_aux()241 const struct in6_addr *NpingTarget::getIPv6Address_aux(){
242   struct sockaddr_in6 *sin = (struct sockaddr_in6 *) &targetsock;
243   if (sin->sin6_family == AF_INET6) {
244     return &(sin->sin6_addr);
245   }
246   return NULL;
247 } /* End of getIPv6Address_aux() */
248 
249 
250 /** Get source address used to reach the target.  */
getSourceSockAddr(struct sockaddr_storage * ss,size_t * ss_len)251 int NpingTarget::getSourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len) {
252   if (sourcesocklen <= 0)
253     return 1;
254   assert(sourcesocklen <= sizeof(*ss));
255   if (ss)
256     memcpy(ss, &sourcesock, sourcesocklen);
257   if (ss_len)
258     *ss_len = sourcesocklen;
259   return 0;
260 } /* End of getSourceSockAddr() */
261 
262 
263 /** Set source address used to reach the target.
264   * Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted
265   * to sockaddr_storage */
setSourceSockAddr(struct sockaddr_storage * ss,size_t ss_len)266 int NpingTarget::setSourceSockAddr(struct sockaddr_storage *ss, size_t ss_len) {
267   assert(ss_len > 0 && ss_len <= sizeof(*ss));
268   memcpy(&sourcesock, ss, ss_len);
269   sourcesocklen = ss_len;
270   return OP_SUCCESS;
271 } /* End of setSourceSockAddr() */
272 
273 
274 /** Set source address used to reach the target.
275   * Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted
276   * to sockaddr_storage */
setSpoofedSourceSockAddr(struct sockaddr_storage * ss,size_t ss_len)277 int NpingTarget::setSpoofedSourceSockAddr(struct sockaddr_storage *ss, size_t ss_len) {
278   assert(ss_len > 0 && ss_len <= sizeof(*ss));
279   memcpy(&spoofedsrcsock, ss, ss_len);
280   spoofedsrcsocklen = ss_len;
281   this->spoofedsrc_set=true;
282   return OP_SUCCESS;
283 } /* End of setSpoofedSourceSockAddr() */
284 
285 
286 /** Get source address used to reach the target.  */
getSpoofedSourceSockAddr(struct sockaddr_storage * ss,size_t * ss_len)287 int NpingTarget::getSpoofedSourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len) {
288   if (spoofedsrcsocklen <= 0)
289     return 1;
290   assert(spoofedsrcsocklen <= sizeof(*ss));
291   if (ss)
292     memcpy(ss, &spoofedsrcsock, spoofedsrcsocklen);
293   if (ss_len)
294     *ss_len = spoofedsrcsocklen;
295   return 0;
296 } /* End of getSpoofedSourceSockAddr() */
297 
298 
spoofingSourceAddress()299 bool NpingTarget::spoofingSourceAddress(){
300   return this->spoofedsrc_set;
301 } /* End of spoofingSourceAddress()*/
302 
303 
304 /** Returns IPv4 host address or {0} if unavailable. */
getIPv4SourceAddress()305 struct in_addr NpingTarget::getIPv4SourceAddress() {
306   const struct in_addr *addy = getIPv4SourceAddress_aux();
307   struct in_addr in;
308   if (addy) return *addy;
309   in.s_addr = 0;
310   return in;
311 } /* End of getIPv4SourceAddress() */
312 
313 
314 /** Returns IPv4 host address or NULL if unavailable.*/
getIPv4SourceAddress_aux()315 const struct in_addr *NpingTarget::getIPv4SourceAddress_aux() {
316   struct sockaddr_in *sin = (struct sockaddr_in *) &sourcesock;
317   if (sin->sin_family == AF_INET) {
318     return &(sin->sin_addr);
319   }
320   return NULL;
321 } /* End of getIPv4SourceAddress_aux() */
322 
323 
324 
325 
326 /** Returns IPv4 host address or {0} if unavailable. */
getIPv4SpoofedSourceAddress()327 struct in_addr NpingTarget::getIPv4SpoofedSourceAddress() {
328   const struct in_addr *addy = getIPv4SpoofedSourceAddress_aux();
329   struct in_addr in;
330   if (addy) return *addy;
331   in.s_addr = 0;
332   return in;
333 } /* End of getIPv4SourceAddress() */
334 
335 
336 /** Returns IPv4 host address or NULL if unavailable.*/
getIPv4SpoofedSourceAddress_aux()337 const struct in_addr *NpingTarget::getIPv4SpoofedSourceAddress_aux() {
338   struct sockaddr_in *sin = (struct sockaddr_in *) &spoofedsrcsock;
339   if (sin->sin_family == AF_INET) {
340     return &(sin->sin_addr);
341   }
342   return NULL;
343 } /* End of getIPv4SpoofedSourceAddress_aux() */
344 
345 
346 /** Returns IPv6 host address or {0} if unavailable. */
getIPv6SourceAddress()347 struct in6_addr NpingTarget::getIPv6SourceAddress() {
348   const struct in6_addr *addy = getIPv6SourceAddress_aux();
349   struct in6_addr in;
350   if (addy) return *addy;
351   memset(&in, 0, sizeof(struct in6_addr));
352   return in;
353 } /* End of getIPv6SourceAddress() */
354 
355 
356 /** Returns IPv6 host address or NULL if unavailable.*/
getIPv6SourceAddress_aux()357 const struct in6_addr *NpingTarget::getIPv6SourceAddress_aux() {
358   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &sourcesock;
359   if (sin6->sin6_family == AF_INET) {
360     return &(sin6->sin6_addr);
361   }
362   return NULL;
363 } /* End of getIPv6SourceAddress_aux() */
364 
365 
getIPv6SourceAddress_u8()366 u8 *NpingTarget::getIPv6SourceAddress_u8(){
367   const struct in6_addr *in = getIPv6SourceAddress_aux();
368   if( in==NULL )
369     return NULL;
370   else
371     return (u8*)in->s6_addr;
372 } /* End of getIPv6Address_u8() */
373 
374 
375 /** If the host is directly connected on a network, set and retrieve
376   * that information here.  directlyConnected() will abort if it hasn't
377   * been set yet.  */
setDirectlyConnected(bool connected)378 void NpingTarget::setDirectlyConnected(bool connected) {
379   directly_connected = (connected) ? 1 : 0;
380 } /* End of setDirectlyConnected() */
381 
382 
isDirectlyConnectedOrUnset()383 int NpingTarget::isDirectlyConnectedOrUnset(){
384   return directly_connected;
385 } /* End of isDirectlyConnectedOrUnset() */
386 
387 
isDirectlyConnected()388 bool NpingTarget::isDirectlyConnected() {
389   assert(directly_connected == 0 || directly_connected == 1);
390   return directly_connected;
391 } /* End of isDirectlyConnected() */
392 
393 
394 /** Returns the next hop for sending packets to this host.  Returns true if
395   * next_hop was filled in.  It might be false, for example, if
396   * next_hop has never been set */
getNextHop(struct sockaddr_storage * next_hop,size_t * next_hop_len)397 bool NpingTarget::getNextHop(struct sockaddr_storage *next_hop, size_t *next_hop_len) {
398   if (nexthopsocklen <= 0)
399     return false;
400   assert(nexthopsocklen <= sizeof(*next_hop));
401   if (next_hop)
402     memcpy(next_hop, &nexthopsock, nexthopsocklen);
403   if (next_hop_len)
404     *next_hop_len = nexthopsocklen;
405   return true;
406 } /* End of getNextHop() */
407 
408 
409 /** Sets the next hop for sending packets to this host. Note that it is OK to
410   *  pass in a sockaddr_in or sockaddr_in6 casted to sockaddr_storage */
setNextHop(struct sockaddr_storage * next_hop,size_t next_hop_len)411 void NpingTarget::setNextHop(struct sockaddr_storage *next_hop, size_t next_hop_len) {
412   assert(next_hop_len > 0 && next_hop_len <= sizeof(nexthopsock));
413   memcpy(&nexthopsock, next_hop, next_hop_len);
414   nexthopsocklen = next_hop_len;
415 } /* End of setNextHop() */
416 
417 
418 /** Sets next hop MAC address
419  *  @warning addy must contain at least 6 bytes. */
setNextHopMACAddress(const u8 * addy)420 int NpingTarget::setNextHopMACAddress(const u8 *addy) {
421   if (addy==NULL)
422     return OP_FAILURE;
423   memcpy(NextHopMACaddress, addy, 6);
424   NextHopMACaddress_set = 1;
425   return OP_SUCCESS;
426 } /* End of setNextHopMACAddress() */
427 
428 
429 /** Returns a pointer to a 6 byte buffer that contains next hop MAC address */
getNextHopMACAddress()430 const u8 *NpingTarget::getNextHopMACAddress() {
431   return (NextHopMACaddress_set)? NextHopMACaddress : NULL;
432 } /* End of getNextHopMACAddress() */
433 
434 
435 /** Sets target MAC address.
436   * Returns OP_SUCCESS if MAC address set successfully and OP_FAILURE in case
437   * of error. */
setMACAddress(const u8 * addy)438 int NpingTarget::setMACAddress(const u8 *addy) {
439   if (addy==NULL)
440     return OP_FAILURE;
441   memcpy(MACaddress, addy, 6);
442   MACaddress_set = 1;
443   return OP_SUCCESS;
444 } /* End of setMACAddress() */
445 
446 
447 /** Returns the 6-byte long MAC address, or NULL if none has been set */
getMACAddress()448 const u8 *NpingTarget::getMACAddress(){
449   return (MACaddress_set)? MACaddress : NULL;
450 } /* End of getMACAddress() */
451 
452 
453 /** Sets the MAC address that should be used when sending raw ethernet frames
454  *  from this host to the target.
455   * Returns OP_SUCCESS if MAC address set successfully and OP_FAILURE in case
456   * of error. */
setSrcMACAddress(const u8 * addy)457 int NpingTarget::setSrcMACAddress(const u8 *addy) {
458   if (addy==NULL)
459     return OP_FAILURE;
460   memcpy(SrcMACaddress, addy, 6);
461   SrcMACaddress_set = 1;
462   return OP_SUCCESS;
463 } /* End of setSrcMACAddress() */
464 
465 
466 /** Returns the 6-byte long Source MAC address, or NULL if none has been set */
getSrcMACAddress()467 const u8 *NpingTarget::getSrcMACAddress() {
468   return (SrcMACaddress_set)? SrcMACaddress : NULL;
469 } /* End of getSrcMACAddress() */
470 
471 
472 /** Set the device names so that they can be returned by deviceName()
473     and deviceFullName().  The normal name may not include alias
474     qualifier, while the full name may include it (e.g. "eth1:1").  If
475     these are non-null, they will overwrite the stored version */
setDeviceNames(const char * name,const char * fullname)476 void NpingTarget::setDeviceNames(const char *name, const char *fullname) {
477   if (name)
478       Strncpy(devname, name, sizeof(devname));
479   if (fullname)
480       Strncpy(devfullname, fullname, sizeof(devfullname));
481 } /* End of setDeviceNames() */
482 
483 
484 /** Returns device normal name (e.g. eth0) */
getDeviceName()485 const char * NpingTarget::getDeviceName() {
486   return (devname[0] != '\0')? devname : NULL;
487 } /* End of getDeviceName() */
488 
489 
490 /** Returns device full name (e.g. eth0:1) */
getDeviceFullName()491 const char * NpingTarget::getDeviceFullName() {
492   return (devfullname[0] != '\0')? devfullname : NULL;
493 } /* End of getDeviceFullName() */
494 
495 
setDeviceType(devtype type)496 int NpingTarget::setDeviceType(devtype type){
497   this->dev_type = type;
498   return OP_SUCCESS;
499 } /* End of setDeviceType() */
500 
501 
getDeviceType()502 devtype NpingTarget::getDeviceType(){
503   return this->dev_type;
504 } /* End of getDeviceType() */
505 
506 
507 /** Set target resolved host name. You can set to NULL to erase a name or if
508   * it failed to resolve, or just don't call this if it fails to resolve */
setResolvedHostName(char * name)509 void NpingTarget::setResolvedHostName(char *name) {
510   char *p;
511   if (hostname) {
512     free(hostname);
513     hostname = NULL;
514   }
515   if (name) {
516     p = hostname = strdup(name);
517     while (*p) {
518       // I think only a-z A-Z 0-9 . and - are allowed, but I'll be a little more
519       // generous.
520       if (!isalnum(*p) && !strchr(".-+=:_~*", *p)) {
521     nping_warning(QT_2, "Illegal character(s) in hostname -- replacing with '*'\n");
522     *p = '*';
523       }
524       p++;
525     }
526   }
527 } /* End of setResolvedHostName() */
528 
529 
530 /** Give the name from the last setHostName() call, which should be
531     the name obtained from reverse-resolution (PTR query) of the IP (v4
532     or v6).  If the name has not been set, or was set to NULL, an empty
533     string ("") is returned to make printing easier. */
getResolvedHostName()534 const char *NpingTarget::getResolvedHostName(){
535   return hostname? hostname : "";
536 } /* End of getResolvedHostName() */
537 
538 
539 /** Set user supplied host name. You can set to NULL to erase a name. */
setSuppliedHostName(char * name)540 int NpingTarget::setSuppliedHostName(char *name) {
541   if(name==NULL)
542     return OP_FAILURE;
543   if (targetname) {
544     free(targetname);
545     targetname = NULL;
546   }
547   targetname = strdup(name);
548   return OP_SUCCESS;
549 } /* End of setSuppliedHostName() */
550 
551 
552 /** Give the name from the last setTargetName() call, which is the
553     name of the target given on the command line if it's a named
554     host. */
getSuppliedHostName()555 const char *NpingTarget::getSuppliedHostName(){
556   return targetname;
557 } /* End of getSuppliedHostName() */
558 
559 
setNamedHost(bool val)560 int NpingTarget::setNamedHost(bool val){
561   this->namedhost= (val)? 1 : 0;
562   return OP_SUCCESS;
563 } /* End of setNamedHost() */
564 
565 
isNamedHost()566 bool NpingTarget::isNamedHost(){
567   assert(this->namedhost==1 || this->namedhost==0 );
568   return (this->namedhost==1);
569 } /* End of isNamedHost() */
570 
571 
572 /**  Creates a "presentation" formatted string out of the IPv4/IPv6 address.
573     Called when the IP changes */
generateIPString()574 void NpingTarget::generateIPString() {
575   const char *ret=NULL;
576   struct sockaddr_in *sin = (struct sockaddr_in *) &targetsock;
577   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &targetsock;
578   if (sin->sin_family == AF_INET){
579     ret=inet_ntop(AF_INET, (char *) &sin->sin_addr, targetipstring, sizeof(targetipstring));
580   }else if(sin->sin_family == AF_INET6){
581     ret=inet_ntop(AF_INET6, (char *) &sin6->sin6_addr, targetipstring, sizeof(targetipstring));
582   }else{
583     nping_fatal(QT_3, "NpingTarget::GenerateIPString(): Unsupported address family");
584   }
585   if( ret==NULL )
586     nping_fatal(QT_3, "NpingTarget::GenerateIPString(): Unsupported address family");
587  targetipstring_set=true;
588 } /* End of generateIPString() */
589 
590 
591 /**  Creates a "presentation" formatted string out of the IPv4/IPv6 address.
592     Called when the IP changes */
getSourceIPStr()593 const char *NpingTarget::getSourceIPStr() {
594   static char buffer[256];
595   const char *ret=NULL;
596   struct sockaddr_in *sin = (struct sockaddr_in *) &sourcesock;
597   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &sourcesock;
598 
599   if (sin->sin_family == AF_INET){
600     ret=inet_ntop(AF_INET, (char *) &sin->sin_addr, buffer, sizeof(buffer));
601   }else if(sin->sin_family == AF_INET6){
602     ret=inet_ntop(AF_INET6, (char *) &sin6->sin6_addr, buffer, sizeof(buffer));
603   }else{
604     nping_fatal(QT_3, "NpingTarget::getSourceIPString(): Unsupported address family");
605   }
606   if(ret==NULL)
607     return NULL;
608   else
609     return buffer;
610 } /* End of getSourceIPStr() */
611 
612 
613 
614 
615 /**  Creates a "presentation" formatted string out of the IPv4/IPv6 address.
616     Called when the IP changes */
getSpoofedSourceIPStr()617 const char *NpingTarget::getSpoofedSourceIPStr() {
618   static char buffer[256];
619   const char *ret=NULL;
620   struct sockaddr_in *sin = (struct sockaddr_in *) &spoofedsrcsock;
621   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &spoofedsrcsock;
622 
623   if (sin->sin_family == AF_INET){
624     ret=inet_ntop(AF_INET, (char *) &sin->sin_addr, buffer, sizeof(buffer));
625   }else if(sin->sin_family == AF_INET6){
626     ret=inet_ntop(AF_INET6, (char *) &sin6->sin6_addr, buffer, sizeof(buffer));
627   }else{
628     nping_fatal(QT_3, "NpingTarget::getSourceIPString(): Unsupported address family");
629   }
630   if(ret==NULL)
631     return NULL;
632   else
633     return buffer;
634 } /* End of getSpoofedSourceIPStr() */
635 
636 
getNextHopIPStr()637 const char *NpingTarget::getNextHopIPStr(){
638   static char buffer[256];
639   const char *ret=NULL;
640   struct sockaddr_in *sin = (struct sockaddr_in *) &nexthopsock;
641   struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &nexthopsock;
642   if (sin->sin_family == AF_INET){
643     ret=inet_ntop(AF_INET, (char *) &sin->sin_addr, buffer, sizeof(buffer));
644   }else if(sin->sin_family == AF_INET6){
645     ret=inet_ntop(AF_INET6, (char *) &sin6->sin6_addr, buffer, sizeof(buffer));
646   }else{
647     nping_fatal(QT_3, "NpingTarget::getNextHopIPStr(): Unsupported address family");
648   }
649   if(ret==NULL)
650     return NULL;
651   else
652     return buffer;
653 } /* End of getNextHopIPStr() */
654 
655 
getMACStr(u8 * mac)656 const char *NpingTarget::getMACStr(u8 *mac){
657   static char buffer[256];
658   assert(mac!=NULL);
659   sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", (u8)mac[0],(u8)mac[1],
660           (u8)mac[2], (u8)mac[4],(u8)mac[4],(u8)mac[5]);
661   return buffer;
662 }
663 
getTargetMACStr()664 const char *NpingTarget::getTargetMACStr(){
665     return getMACStr(this->MACaddress);
666 }
667 
668 
getSourceMACStr()669 const char *NpingTarget::getSourceMACStr(){
670     return getMACStr(this->SrcMACaddress);
671 }
672 
673 
getNextHopMACStr()674 const char *NpingTarget::getNextHopMACStr(){
675     return getMACStr(this->NextHopMACaddress);
676 }
677 
678 
679 /** Returns a "presentation" formatted string for the targetIPv4/IPv6 address. */
getTargetIPstr()680 const char *NpingTarget::getTargetIPstr(){
681   if( targetipstring_set == false )
682     this->generateIPString();
683   return targetipstring;
684 } /* End of getTargetIPstr() */
685 
686 
687 /** Generates a printable string consisting of the host's IP address and
688   * hostname (if available).  Eg "www.insecure.org (64.71.184.53)" or
689   * "fe80::202:e3ff:fe14:1102".  The name is written into the buffer provided,
690   * which is also returned.  Results that do not fit in buflen will be
691   * truncated.
692   */
getNameAndIP(char * buf,size_t buflen)693 const char *NpingTarget::getNameAndIP(char *buf, size_t buflen) {
694   assert(buf);
695   assert(buflen > 8);
696   if (hostname) {
697     Snprintf(buf, buflen, "%s (%s)", hostname, targetipstring);
698   }else if (targetname){
699     Snprintf(buf, buflen, "%s (%s)", targetname, targetipstring);
700   }else Strncpy(buf, targetipstring, buflen);
701   return buf;
702 } /* End of getNameAndIP() */
703 
704 /** This next version returns a static buffer -- so no concurrency */
getNameAndIP()705 const char *NpingTarget::getNameAndIP() {
706   if(!nameIPBuf)
707     nameIPBuf = (char *)safe_malloc(FQDN_LEN + INET6_ADDRSTRLEN + 4);
708   return getNameAndIP(nameIPBuf, FQDN_LEN + INET6_ADDRSTRLEN + 4);
709 } /* End of getNameAndIP() */
710 
711 
712 /* This method returns a number suitable to be used as a ICMP sequence field.
713  * The first time this function is called, 1 is returned. The internal icmp_seq
714  * attribute is incremented in every call so subsequent calls will return
715  * n+1 where n is the value returned by last call. */
obtainICMPSequence()716 u16 NpingTarget::obtainICMPSequence() {
717   return this->icmp_seq++;
718 } /* End of obtainICMPSequence() */
719 
720 
getICMPIdentifier()721 u16 NpingTarget::getICMPIdentifier(){
722   return this->icmp_id;
723 } /* End of getICMPIdentifier()*/
724 
725 
726 /* This function ensures that the next hop MAC address for a target is
727    filled in.  This address is the target's own MAC if it is directly
728    connected, and the next hop mac otherwise.  Returns true if the
729    address is set when the function ends, false if not.  This function
730    firt checks if it is already set, if not it tries the arp cache,
731    and if that fails it sends an ARP request itself.  This should be
732    called after an ARP scan if many directly connected machines are
733    involved. setDirectlyConnected() (whether true or false) should
734    have already been called on target before this.  The target device
735    and src mac address should also already be set.  */
determineNextHopMACAddress()736 bool NpingTarget::determineNextHopMACAddress() {
737   struct sockaddr_storage targetss, srcss;
738   size_t sslen;
739   arp_t *a;
740   u8 mac[6];
741   struct arp_entry ae;
742 
743   if (this->getDeviceType() != devt_ethernet)
744     return false; /* Duh. */
745 
746   /* First check if we already have it, duh. */
747   if ( this->getNextHopMACAddress() )
748     return true;
749 
750   nping_print(DBG_2,"Determining target %s MAC address or next hop MAC address...", this->getTargetIPstr() );
751   /* For connected machines, it is the same as the target addy */
752   if (this->isDirectlyConnected() && this->getMACAddress() ) {
753     this->setNextHopMACAddress(this->getMACAddress());
754     return true;
755   }
756 
757   if (this->isDirectlyConnected()) {
758     this->getTargetSockAddr(&targetss, &sslen);
759   } else {
760     if (!this->getNextHop(&targetss, &sslen))
761       fatal("%s: Failed to determine nextHop to target", __func__);
762   }
763 
764   /* First, let us check the ARP cache ... */
765   if (mac_cache_get(&targetss, mac)) {
766     this->setNextHopMACAddress(mac);
767     return true;
768   }
769 
770   /* Maybe the system ARP cache will be more helpful */
771   nping_print(DBG_3,"    > Checking system's ARP cache...");
772   a = arp_open();
773   addr_ston((sockaddr *)&targetss, &ae.arp_pa);
774   if (arp_get(a, &ae) == 0) {
775     mac_cache_set(&targetss, ae.arp_ha.addr_eth.data);
776     this->setNextHopMACAddress(ae.arp_ha.addr_eth.data);
777     arp_close(a);
778     nping_print(DBG_3,"    > Success: Entry found [%s]", this->getNextHopMACStr() );
779     return true;
780   }
781   arp_close(a);
782   nping_print(DBG_3,"    > No relevant entries found in system's ARP cache.");
783 
784 
785   /* OK, the last choice is to send our own damn ARP request (and
786      retransmissions if necessary) to determine the MAC */
787   /* We first try sending the ARP with our spoofed IP address on it */
788   if( this->spoofingSourceAddress() ){
789     nping_print(DBG_3,"    > Sending ARP request using spoofed IP %s...", this->getSpoofedSourceIPStr() );
790       this->getSpoofedSourceSockAddr(&srcss, NULL);
791       if (doArp(this->getDeviceName(), this->getSrcMACAddress(), &srcss, &targetss, mac, NULL)) {
792         mac_cache_set(&targetss, mac);
793         this->setNextHopMACAddress(mac);
794         nping_print(DBG_4,"    > Success: 1 ARP response received [%s]", this->getNextHopMACStr() );
795         return true;
796       }
797   }
798   nping_print(DBG_3,"    > No ARP responses received." );
799 
800   /* If our spoofed IP address didn't work, try our real IP */
801   nping_print(DBG_4,"    > Sending ARP request using our real IP %s...", this->getSourceIPStr() );
802   this->getSourceSockAddr(&srcss, NULL);
803   if (doArp(this->getDeviceName(), this->getSrcMACAddress(), &srcss, &targetss, mac, NULL)) {
804     mac_cache_set(&targetss, mac);
805     this->setNextHopMACAddress(mac);
806     nping_print(DBG_3,"    > Success: 1 ARP response received [%s]", this->getNextHopMACStr() );
807     return true;
808   }
809   nping_print(DBG_3,"    > No ARP responses received" );
810 
811   /* I'm afraid that we couldn't find it!  Maybe it doesn't exist?*/
812   return false;
813 }
814 
815 
816 /* Sets Target MAC if is directly connected to us. In that case, Next Hop MAC
817  * address is copied into the target mac */
determineTargetMACAddress()818 bool NpingTarget::determineTargetMACAddress() {
819   if( this->isDirectlyConnected() ){
820      if(this->NextHopMACaddress_set){
821         memcpy(MACaddress, NextHopMACaddress, 6);
822         return true;
823     }
824   }
825   return false;
826 } /* End of determineTargetMACAddress() */
827 
828 
829 /* Prints target details. Used for testing. */
printTargetDetails()830 void NpingTarget::printTargetDetails(){
831   devtype aux = this->getDeviceType();
832   const char *type=NULL;
833 
834   switch(aux){
835     case devt_ethernet: type="Ethernet"; break;
836     case devt_loopback: type="Loopback"; break;
837     case devt_p2p:      type="P2P";      break;
838     default:    type="Unknown";  break;
839   }
840 
841     printf("+-----------------TARGET-----------------+\n");
842     printf("Device Name:            %s\n", this->getDeviceName() );
843     printf("Device FullName:        %s\n", this->getDeviceFullName());
844     printf("Device Type:            %s\n", type);
845     printf("Directly connected?:    %s\n", this->isDirectlyConnected()? "yes" : "no");
846     printf("Address family:         %s\n", this->addressfamily==AF_INET? "AF_INET" : "AF_INET6/OTHER");
847     printf("Resolved Hostname:      %s\n", this->getResolvedHostName());
848     printf("Supplied Hostname:      %s\n", this->getSuppliedHostName());
849     printf("Target Address:         %s\n", this->getTargetIPstr());
850     printf("Source Address:         %s\n", this->getSourceIPStr());
851     if(this->spoofedsrc_set)
852         printf("Spoofed Address:        %s\n", this->getSpoofedSourceIPStr() );
853     printf("Next Hop Address:       %s\n", this->getNextHopIPStr());
854     printf("Target MAC Address:     %s\n", this->getTargetMACStr());
855     printf("Source MAC Address:     %s\n", this->getSourceMACStr());
856     printf("Next Hop MAC Address:   %s\n", this->getNextHopMACStr());
857    return;
858 } /* End of printTargetDetails() */
859 
860 
861 
862 
863 
864 /* Update info about the last TCP probe sent */
setProbeSentTCP(u16 sport,u16 dport)865 int NpingTarget::setProbeSentTCP(u16 sport, u16 dport){
866   this->sent_total++;
867  /* Check if we already have an entry for the supplied dst port */
868  for(int i=0; i<MAX_SENTPROBEINFO_ENTRIES && i<total_stats; i++){
869     if( this->sentprobes[i].tcp_port==dport ){
870         gettimeofday(&this->sentprobes[i].sent, NULL); /* overwrite previous value? TODO: think about this */
871         return OP_SUCCESS;
872     }
873   }
874   /* If we get here means that we don't have the dst port on our small
875    * stats "cache", so we have to overwrite an existing port with this one */
876   gettimeofday(&this->sentprobes[current_stat].sent, NULL);
877   this->sentprobes[current_stat].tcp_port=dport;
878   current_stat=(current_stat+1)%MAX_SENTPROBEINFO_ENTRIES;
879   if( total_stats< MAX_SENTPROBEINFO_ENTRIES)
880     total_stats++;
881   return OP_SUCCESS;
882 } /* End of setProbeSentTCP() */
883 
884 
885 /* Update info about the last TCP probe received */
setProbeRecvTCP(u16 sport,u16 dport)886 int NpingTarget::setProbeRecvTCP(u16 sport, u16 dport){
887   int i=0;
888   unsigned long int diff=0;
889   this->recv_total++;
890 /* Let's see if we have the supplied source port in our stats "cache". */
891  for(i=0; i<MAX_SENTPROBEINFO_ENTRIES; i++){
892     if( this->sentprobes[i].tcp_port == sport ){
893         gettimeofday(&this->sentprobes[i].recv, NULL);
894           /* Update stats info */
895           diff= TIMEVAL_SUBTRACT(this->sentprobes[i].recv, this->sentprobes[i].sent);
896           this->updateRTTs(diff);
897 
898         return OP_SUCCESS;
899     }
900   }
901   /* If we get here means that, for some reason, we don't have a tx time for
902    * the received packet so there is no point on updating anything since we
903    * cannot compute the rtt without the initial time. */
904   return OP_FAILURE;
905 } /* End of setProbeRecvTCP() */
906 
907 
908 /* For the moment we are treating TCP and UDP the same way. However, this
909  * function is provided just in case we want to differentiate in the future. */
setProbeRecvUDP(u16 sport,u16 dport)910 int NpingTarget::setProbeRecvUDP(u16 sport, u16 dport){
911     return this->setProbeRecvTCP(sport, dport);
912 } /* End of setProbeRecvUDP() */
913 
914 
915 /* For the moment we are treating TCP and UDP the same way. However, this
916  * function is provided just in case we want to differentiate in the future. */
setProbeSentUDP(u16 sport,u16 dport)917 int NpingTarget::setProbeSentUDP(u16 sport, u16 dport){
918     return this->setProbeSentTCP(sport, dport);
919 } /* End of setProbeSentUDP() */
920 
921 
922 /* Update info about the last ICMP probe sent */
setProbeSentICMP(u16 id,u16 seq)923 int NpingTarget::setProbeSentICMP(u16 id, u16 seq){
924   this->sent_total++;
925  /* Check if we already have an entry for the supplied id and seq numbers */
926  for(int i=0; i<MAX_SENTPROBEINFO_ENTRIES && i<total_stats; i++){
927     if( this->sentprobes[i].icmp_id==id && this->sentprobes[i].icmp_seq==seq){
928         gettimeofday(&this->sentprobes[i].sent, NULL); /* overwrite previous value? TODO: think about this */
929         return OP_SUCCESS;
930     }
931   }
932   /* If we get here means that we don't have the id/seq on our small
933    * stats "cache", so we have to overwrite an existing entry with this one */
934   gettimeofday(&this->sentprobes[current_stat].sent, NULL);
935   this->sentprobes[current_stat].icmp_id=id;
936   this->sentprobes[current_stat].icmp_seq=seq;
937   current_stat=(current_stat+1)%MAX_SENTPROBEINFO_ENTRIES;
938   if( total_stats< MAX_SENTPROBEINFO_ENTRIES)
939     total_stats++;
940   return OP_SUCCESS;
941 } /* End of setProbeSentARP() */
942 
943 
944 
945 
946 /* Update info about the last ICMP probe received */
setProbeRecvICMP(u16 id,u16 seq)947 int NpingTarget::setProbeRecvICMP(u16 id, u16 seq){
948   int i= this->current_stat-1;
949   unsigned long int diff=0;
950 
951   if( i<0 && total_stats>=MAX_SENTPROBEINFO_ENTRIES)
952     i=MAX_SENTPROBEINFO_ENTRIES-1;
953 
954   gettimeofday(&this->sentprobes[i].recv, NULL);
955 
956   /* Update stats info */
957   recv_total++;
958   diff= TIMEVAL_SUBTRACT(this->sentprobes[i].recv, this->sentprobes[i].sent);
959   this->updateRTTs(diff);
960   return OP_FAILURE;
961 } /* End of setProbeRecvICMP() */
962 
963 
964 /* Update info about the last ARP probe sent */
setProbeSentARP()965 int NpingTarget::setProbeSentARP(){
966   this->sent_total++;
967   return OP_SUCCESS;
968 } /* End of setProbeSentARP() */
969 
970 
971 /* Update info about the last ICMP probe received */
setProbeRecvARP()972 int NpingTarget::setProbeRecvARP(){
973   //int i= this->current_stat-1;
974   //unsigned long int diff=0;
975   return OP_FAILURE;
976 } /* End of setProbeRecvICMP() */
977 
978 
979 /* Assumes recv_total has already been incremented */
updateRTTs(unsigned long int diff)980 int NpingTarget::updateRTTs(unsigned long int diff){
981   if( diff > max_rtt || max_rtt==0 ){
982     max_rtt=diff;
983     max_rtt_set=true;
984   }
985   if( diff < min_rtt || min_rtt==0){
986     min_rtt=diff;
987     min_rtt_set=true;
988   }
989 
990   /* Update average round trip time */
991   if(!avg_rtt_set || recv_total<=1)
992     avg_rtt = diff;
993   else
994     avg_rtt = ((avg_rtt*(recv_total-1))+diff) / (recv_total);
995   avg_rtt_set=true;
996 
997   return OP_SUCCESS;
998 } /* End of updateRTTs() */
999 
1000 
printStats()1001 int NpingTarget::printStats(){
1002   nping_print(VB_0, "Statistics for host %s:", this->getNameAndIP());
1003   nping_print(VB_0|NO_NEWLINE," |  ");
1004   this->printCounts();
1005   nping_print(VB_0|NO_NEWLINE," |_ ");
1006   this->printRTTs();
1007   return OP_SUCCESS;
1008 } /* End of printStats() */
1009 
1010 
1011 /* Print packet counts */
printCounts()1012 void NpingTarget::printCounts(){
1013   unsigned long int lost = this->sent_total - this->recv_total;
1014 
1015   /* Sent Packets */
1016   nping_print(VB_0|NO_NEWLINE, "Probes Sent: %ld ", this->sent_total);
1017   /* Received Packets */
1018   nping_print(VB_0|NO_NEWLINE,"| Rcvd: %ld ", this->recv_total );
1019   /* Lost Packets */
1020   nping_print(VB_0|NO_NEWLINE,"| Lost: %ld ", lost );
1021 
1022   /* Only compute percentage if we actually sent packets, don't do divisions
1023    * by zero! (this could happen when user presses CTRL-C and we print the
1024    * stats */
1025   float percentlost=0.0;
1026   if( lost!=0 && this->sent_total!=0)
1027     percentlost=((double)lost)/((double)this->sent_total) * 100;
1028   nping_print(VB_0," (%.2lf%%)", percentlost);
1029 } /* End of printCounts() */
1030 
1031 
1032 /* Print round trip times */
printRTTs()1033 void NpingTarget::printRTTs(){
1034   if( max_rtt_set )
1035     nping_print(QT_1|NO_NEWLINE,"Max rtt: %.3lfms ", this->max_rtt/1000.0 );
1036   else
1037     nping_print(QT_1|NO_NEWLINE,"Max rtt: N/A ");
1038 
1039   if( min_rtt_set )
1040     nping_print(QT_1|NO_NEWLINE,"| Min rtt: %.3lfms ", this->min_rtt/1000.0 );
1041   else
1042     nping_print(QT_1|NO_NEWLINE,"| Min rtt: N/A " );
1043 
1044   if( avg_rtt_set)
1045     nping_print(QT_1,"| Avg rtt: %.3lfms", this->avg_rtt/1000.0 );
1046   else
1047     nping_print(QT_1,"| Avg rtt: N/A" );
1048 } /* End of printRTTs() */
1049