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