• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

savon-noir / python-libnmap / 13000574708

27 Jan 2025 11:47PM UTC coverage: 72.735% (+0.03%) from 72.708%
13000574708

push

github

savon-noir
fix: simplified the extraports structs

13 of 13 new or added lines in 3 files covered. (100.0%)

40 existing lines in 2 files now uncovered.

1750 of 2406 relevant lines covered (72.73%)

2.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.82
/libnmap/objects/host.py
1
# -*- coding: utf-8 -*-
2

3
from libnmap.diff import NmapDiff
3✔
4
from libnmap.objects.os import NmapOSFingerprint
3✔
5

6

7
class NmapHost(object):
3✔
8
    """
9
    NmapHost is a class representing a host object of NmapReport
10
    """
11

12
    def __init__(
3✔
13
        self,
14
        starttime="",
15
        endtime="",
16
        address=None,
17
        status=None,
18
        hostnames=None,
19
        services=None,
20
        extras=None,
21
    ):
22
        """
23
        NmapHost constructor
24
        :param starttime: unix timestamp of when the scan against
25
        that host started
26
        :type starttime: string
27
        :param endtime: unix timestamp of when the scan against
28
        that host ended
29
        :type endtime: string
30
        :param address: dict ie :{'addr': '127.0.0.1', 'addrtype': 'ipv4'}
31
        :param status: dict ie:{'reason': 'localhost-response',
32
                                'state': 'up'}
33
        :return: NmapHost:
34
        """
35
        self._starttime = starttime
3✔
36
        self._endtime = endtime
3✔
37
        self._hostnames = hostnames if hostnames is not None else []
3✔
38
        self._status = status if status is not None else {}
3✔
39
        self._services = services if services is not None else []
3✔
40
        self._extras = extras if extras is not None else {}
3✔
41
        self._extraports = self._extras.get("extraports", None)
3✔
42
        self._osfingerprinted = False
3✔
43
        self.os = None
3✔
44
        if "os" in self._extras:
3✔
45
            self.os = NmapOSFingerprint(self._extras["os"])
3✔
46
            self._osfingerprinted = True
3✔
47
        else:
48
            self.os = NmapOSFingerprint({})
3✔
49

50
        self._ipv4_addr = None
3✔
51
        self._ipv6_addr = None
3✔
52
        self._mac_addr = None
3✔
53
        self._vendor = None
3✔
54
        for addr in address:
3✔
55
            if addr["addrtype"] == "ipv4":
3✔
56
                self._ipv4_addr = addr["addr"]
3✔
57
            elif addr["addrtype"] == "ipv6":
3✔
UNCOV
58
                self._ipv6_addr = addr["addr"]
×
59
            elif addr["addrtype"] == "mac":
3✔
60
                self._mac_addr = addr["addr"]
3✔
61
            if "vendor" in addr:
3✔
62
                self._vendor = addr["vendor"]
3✔
63

64
        self._main_address = self._ipv4_addr or self._ipv6_addr or ""
3✔
65
        self._address = address
3✔
66

67
    def __eq__(self, other):
3✔
68
        """
69
        Compare eq NmapHost based on :
70

71
            - hostnames
72
            - address
73
            - if an associated services has changed
74

75
        :return: boolean
76
        """
77
        rval = False
3✔
78
        if self.__class__ == other.__class__ and self.id == other.id:
3✔
79
            rval = self.changed(other) == 0
3✔
80
        return rval
3✔
81

82
    def __ne__(self, other):
3✔
83
        """
84
        Compare ne NmapHost based on:
85

86
            - hostnames
87
            - address
88
            - if an associated services has changed
89

90
        :return: boolean
91
        """
92
        rval = True
3✔
93
        if self.__class__ == other.__class__ and self.id == other.id:
3✔
94
            rval = self.changed(other) > 0
3✔
95
        return rval
3✔
96

97
    def __repr__(self):
3✔
98
        """
99
        String representing the object
100
        :return: string
101
        """
UNCOV
102
        return "{0}: [{1} ({2}) - {3}]".format(
×
103
            self.__class__.__name__,
104
            self.address,
105
            " ".join(self._hostnames),
106
            self.status,
107
        )
108

109
    def __hash__(self):
3✔
110
        """
111
        Hash is needed to be able to use our object in sets
112
        :return: hash
113
        """
114
        return (
3✔
115
            hash(self.status)
116
            ^ hash(self.address)
117
            ^ hash(self._mac_addr)
118
            ^ hash(frozenset(self._services))
119
            ^ hash(frozenset(" ".join(self._hostnames)))
120
        )
121

122
    def changed(self, other):
3✔
123
        """
124
        return the number of attribute who have changed
125
        :param other: NmapHost object to compare
126
        :return int
127
        """
128
        return len(self.diff(other).changed())
3✔
129

130
    @property
3✔
131
    def starttime(self):
3✔
132
        """
133
        Accessor for the unix timestamp of when the scan was started
134

135
        :return: string
136
        """
137
        return self._starttime
3✔
138

139
    @property
3✔
140
    def endtime(self):
3✔
141
        """
142
        Accessor for the unix timestamp of when the scan ended
143

144
        :return: string
145
        """
146
        return self._endtime
3✔
147

148
    @property
3✔
149
    def address(self):
3✔
150
        """
151
        Accessor for the IP address of the scanned host
152

153
        :return: IP address as a string
154
        """
155
        return self._main_address
3✔
156

157
    @address.setter
3✔
158
    def address(self, addrdict):
3✔
159
        """
160
        Setter for the address dictionnary.
161

162
        :param addrdict: valid dict is {'addr': '1.1.1.1',
163
                                        'addrtype': 'ipv4'}
164
        """
165
        if addrdict["addrtype"] == "ipv4":
3✔
166
            self._ipv4_addr = addrdict["addr"]
3✔
167
        elif addrdict["addrtype"] == "ipv6":
×
168
            self._ipv6_addr = addrdict["addr"]
×
169
        elif addrdict["addrtype"] == "mac":
×
UNCOV
170
            self._mac_addr = addrdict["addr"]
×
171
        if "vendor" in addrdict:
3✔
UNCOV
172
            self._vendor = addrdict["vendor"]
×
173

174
        self._main_address = self._ipv4_addr or self._ipv6_addr or ""
3✔
175
        self._address = addrdict
3✔
176

177
    @property
3✔
178
    def ipv4(self):
3✔
179
        """
180
        Accessor for the IPv4 address of the scanned host
181

182
        :return: IPv4 address as a string
183
        """
UNCOV
184
        return self._ipv4_addr or ""
×
185

186
    @property
3✔
187
    def mac(self):
3✔
188
        """
189
        Accessor for the MAC address of the scanned host
190

191
        :return: MAC address as a string
192
        """
UNCOV
193
        return self._mac_addr or ""
×
194

195
    @property
3✔
196
    def vendor(self):
3✔
197
        """
198
        Accessor for the vendor attribute of the scanned host
199

200
        :return: string (vendor) of empty string if no vendor defined
201
        """
UNCOV
202
        return self._vendor or ""
×
203

204
    @property
3✔
205
    def ipv6(self):
3✔
206
        """
207
        Accessor for the IPv6 address of the scanned host
208

209
        :return: IPv6 address as a string
210
        """
UNCOV
211
        return self._ipv6_addr or ""
×
212

213
    @property
3✔
214
    def status(self):
3✔
215
        """
216
        Accessor for the host's status (up, down, unknown...)
217

218
        :return: string
219
        """
220
        return self._status["state"]
3✔
221

222
    @status.setter
3✔
223
    def status(self, statusdict):
3✔
224
        """
225
        Setter for the status dictionnary.
226

227
        :param statusdict: valid dict is {"state": "open",
228
                                          "reason": "syn-ack",
229
                                          "reason_ttl": "0"}
230
                            'state' is the only mandatory key.
231
        """
UNCOV
232
        self._status = statusdict
×
233

234
    def is_up(self):
3✔
235
        """
236
        method to determine if host is up or not
237

238
        :return: bool
239
        """
240
        rval = False
×
241
        if self.status == "up":
×
242
            rval = True
×
UNCOV
243
        return rval
×
244

245
    @property
3✔
246
    def hostnames(self):
3✔
247
        """
248
        Accessor returning the list of hostnames (array of strings).
249

250
        :return: array of string
251
        """
252
        return self._hostnames
3✔
253

254
    @property
3✔
255
    def services(self):
3✔
256
        """
257
        Accessor for the array of scanned services for that host.
258

259
        An array of NmapService objects is returned.
260

261
        :return: array of NmapService
262
        """
263
        return self._services
3✔
264

265
    def get_ports(self):
3✔
266
        """
267
        Retrieve a list of the port used by each service of the NmapHost
268

269
        :return: list: of tuples (port,'proto') ie:[(22,'tcp'),(25, 'tcp')]
270
        """
271
        return [(p.port, p.protocol) for p in self._services]
3✔
272

273
    def get_open_ports(self):
3✔
274
        """
275
        Same as get_ports() but only for open ports
276

277
        :return: list: of tuples (port,'proto') ie:[(22,'tcp'),(25, 'tcp')]
278
        """
279
        return [
3✔
280
            (p.port, p.protocol) for p in self._services if p.state == "open"
281
        ]
282

283
    def get_service(self, portno, protocol="tcp"):
3✔
284
        """
285
        :param portno: int the portnumber
286
        :param protocol='tcp': string ('tcp','udp')
287

288
        :return: NmapService or None
289
        """
290
        plist = [
3✔
291
            p
292
            for p in self._services
293
            if (p.port == portno and p.protocol == protocol)
294
        ]
295
        if len(plist) > 1:
3✔
UNCOV
296
            raise Exception("Duplicate services found in NmapHost object")
×
297
        return plist.pop() if len(plist) else None
3✔
298

299
    def get_service_byid(self, service_id):
3✔
300
        """
301
        Returns a NmapService by providing its id.
302

303
        The id of a nmap service is a python tupl made of (protocol, port)
304
        """
305
        rval = None
×
306
        for _tmpservice in self._services:
×
307
            if _tmpservice.id == service_id:
×
308
                rval = _tmpservice
×
UNCOV
309
        return rval
×
310

311
    def os_class_probabilities(self):
3✔
312
        """
313
        Returns an array of possible OS class detected during
314
        the OS fingerprinting.
315

316
        :return: Array of NmapOSClass objects
317
        """
318
        rval = []
3✔
319
        if self.os is not None:
3✔
320
            rval = self.os.osclasses
3✔
321
        return rval
3✔
322

323
    def os_match_probabilities(self):
3✔
324
        """
325
        Returns an array of possible OS match detected during
326
        the OS fingerprinting
327

328
        :return: array of NmapOSMatches objects
329
        """
330
        rval = []
×
331
        if self.os is not None:
×
332
            rval = self.os.osmatches
×
UNCOV
333
        return rval
×
334

335
    @property
3✔
336
    def os_fingerprinted(self):
3✔
337
        """
338
        Specify if the host has OS fingerprint data available
339

340
        :return: Boolean
341
        """
342
        return self._osfingerprinted
3✔
343

344
    @property
3✔
345
    def os_fingerprint(self):
3✔
346
        """
347
        Returns the fingerprint of the scanned system.
348

349
        :return: string
350
        """
351
        rval = ""
×
352
        if self.os is not None:
×
353
            rval = "\n".join(self.os.fingerprints)
×
UNCOV
354
        return rval
×
355

356
    def os_ports_used(self):
3✔
357
        """
358
        Returns an array of the ports used for OS fingerprinting
359

360
        :return: array of ports used: [{'portid': '22',
361
                                        'proto': 'tcp',
362
                                        'state': 'open'},]
363
        """
364
        rval = []
×
365
        try:
×
366
            rval = self._extras["os"]["ports_used"]
×
367
        except (KeyError, TypeError):
×
368
            pass
×
UNCOV
369
        return rval
×
370

371
    @property
3✔
372
    def tcpsequence(self):
3✔
373
        """
374
        Returns the difficulty to determine remotely predict
375
        the tcp sequencing.
376

377
        return: string
378
        """
379
        rval = ""
×
380
        try:
×
381
            rval = self._extras["tcpsequence"]["difficulty"]
×
382
        except (KeyError, TypeError):
×
383
            pass
×
UNCOV
384
        return rval
×
385

386
    @property
3✔
387
    def ipsequence(self):
3✔
388
        """
389
        Return the class of ip sequence of the remote hosts.
390

391
        :return: string
392
        """
393
        rval = ""
×
394
        try:
×
395
            rval = self._extras["ipidsequence"]["class"]
×
396
        except (KeyError, TypeError):
×
397
            pass
×
UNCOV
398
        return rval
×
399

400
    @property
3✔
401
    def uptime(self):
3✔
402
        """
403
        uptime of the remote host (if nmap was able to determine it)
404

405
        :return: string (in seconds)
406
        """
407
        rval = 0
×
408
        try:
×
409
            rval = int(self._extras["uptime"]["seconds"])
×
410
        except (KeyError, TypeError):
×
411
            pass
×
UNCOV
412
        return rval
×
413

414
    @property
3✔
415
    def lastboot(self):
3✔
416
        """
417
        Since when the host was booted.
418

419
        :return: string
420
        """
421
        rval = ""
×
422
        try:
×
423
            rval = self._extras["uptime"]["lastboot"]
×
424
        except (KeyError, TypeError):
×
425
            pass
×
UNCOV
426
        return rval
×
427

428
    @property
3✔
429
    def distance(self):
3✔
430
        """
431
        Number of hops to host
432

433
        :return: int
434
        """
435
        rval = 0
×
436
        try:
×
437
            rval = int(self._extras["distance"]["value"])
×
438
        except (KeyError, TypeError):
×
439
            pass
×
UNCOV
440
        return rval
×
441

442
    @property
3✔
443
    def scripts_results(self):
3✔
444
        """
445
        Scripts results specific to the scanned host
446

447
        :return: array of <script> dictionary
448
        """
449
        rval = {}
×
450
        try:
×
451
            rval = self._extras["hostscript"]
×
452
        except (KeyError, TypeError):
×
453
            pass
×
UNCOV
454
        return rval
×
455

456
    @property
3✔
457
    def id(self):
3✔
458
        """
459
        id of the host. Used for diff()ing NmapObjects
460

461
        :return: string
462
        """
463
        return self.address
3✔
464

465
    @property
3✔
466
    def extraports(self):
3✔
467
        """
468
        Returns a list of extraport dict with
469
        with struct { count: "123", state: "filtered, extrareasons: [{}] }
470

471
        :return: list of extraport dict
472
        """
473
        return self._extraports
3✔
474

475
    def get_dict(self):
3✔
476
        """
477
        Return a dict representation of the object.
478

479
        This is needed by NmapDiff to allow comparaison
480

481
        :return dict
482
        """
483
        d = dict(
3✔
484
            [
485
                ("{0}::{1}".format(s.__class__.__name__, str(s.id)), hash(s))
486
                for s in self.services
487
            ]
488
        )
489

490
        d.update(
3✔
491
            {
492
                "address": self.address,
493
                "status": self.status,
494
                "hostnames": " ".join(self._hostnames),
495
                "mac_addr": self._mac_addr,
496
            }
497
        )
498

499
        return d
3✔
500

501
    def diff(self, other):
3✔
502
        """
503
        Calls NmapDiff to check the difference between self and
504
        another NmapHost object.
505

506
        Will return a NmapDiff object.
507

508
        This objects return python set() of keys describing the elements
509
        which have changed, were added, removed or kept unchanged.
510

511
        :param other: NmapHost to diff with
512

513
        :return: NmapDiff object
514
        """
515
        return NmapDiff(self, other)
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc