Codebase list python-icmplib / 0aa7247
Import upstream version 2.0.0 Kali Janitor 3 years ago
20 changed file(s) with 1568 addition(s) and 1014 deletion(s). Raw diff Collapse all Expand all
00 # Changelog
11
22 All notable changes to this project will be documented in this file.
3
4 ## [v2.0.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v2.0.0) - 2020-11-15
5 icmplib 2.0 is here! :tada:
6
7 Here is an overview of the improvements:
8 - All the foundations of the library have been completely reworked to make it even faster and simplify future developments.
9 - You can now use the library without root privileges. Remember to disable the `privileged` parameter on functions and sockets.
10 - The `multiping` function has been rewritten to use only one thread instead of as many threads as hosts to reach. This function will be up to 10 times faster and up to 2 times more memory efficient.
11 - You can set a source IP address for sending your ICMP packets.
12 - The `traceroute` function now has a `first_hop` parameter to specify the initial time to live value.
13 - Two new exceptions have been added: `NameLookupError` and `SocketAddressError`
14 - Compatibility with Linux, macOS and Windows has been improved.
15 - Docstrings, examples and documentation have been updated.
16
17 And more!
18 - The `receive` method of sockets can receive all incoming packets.
19 - The new `BufferedSocket` class (experimental) can read and classify incoming ICMP packets into a buffer, in real time. Useful if you want to send several ICMP packets consecutively without waiting for a response between each sending.
20 - Sockets throw new exceptions during instantiation, sending and receiving.
21 - The `resolve` function now raises a `NameLookupError` if the requested name does not exist or cannot be resolved.
22 - Compatibility with existing programs is maintained.
323
424 ## [v1.2.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.2) - 2020-10-10
525 - Add support for hostnames and FQDN resolution to IPv6 addresses.
00 <div align="center">
11 <br>
2 <img src="media/icmplib-logo.png" height="125" width="100" alt="icmplib">
2 <img src="media/icmplib-logo-2.0.png" height="190" width="100" alt="icmplib">
33 <br>
44 <br>
55
6 <p><strong>Easily forge ICMP packets and make your own ping and traceroute.</strong></p>
7 <a href="https://pypi.org/project/icmplib">
8 <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics">
9 </a>
106 <br>
11 <br>
12
137 <div>
148 <a href="#features">Features</a>&nbsp;&nbsp;&nbsp;
159 <a href="#installation">Installation</a>&nbsp;&nbsp;&nbsp;
2115 <a href="#license">License</a>
2216 </div>
2317 <br>
24 <br>
18
19 <pre><strong>icmplib 2.0 is here! 🎉</strong>
20
21 To celebrate its first year and its integration into popular projects,
22 icmplib has been completely revised! Thanks to its advanced features,
23 including the ability to use the library without root privileges, QoS and
24 increased compatibility with all modern operating systems, it becomes the
25 most advanced library in its category. Many things are yet to come!
26
27 Star this project if you like it 😍</pre>
2528
2629 <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python.
27 Use the built-in functions or build your own, you have the choice!
28
29 <strong>Root privileges are required to use this library (raw sockets).</strong></pre>
30 Use the built-in functions or build your own, you have the choice!</pre>
3031 </div>
3132
3233 <br>
3536
3637 - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`.
3738 - :gem: **Modern:** This library uses the latest technologies offered by Python 3.6+ and is fully object-oriented.
38 - :rocket: **Fast:** Each class and function has been designed and optimized to deliver the best performance. Some functions are also multithreaded (like the `multiping` function). You can ping the world in seconds!
39 - :nut_and_bolt: **Powerful and evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets.
39 - :rocket: **Fast:** Each class and function has been designed and optimized to deliver the best performance. Some functions are also multithreaded like the `multiping` function. You can ping the world in seconds!
40 - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload and more!
41 - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets.
4042 - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance.
4143 - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it).
4244 - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows.
4648
4749 ## Installation
4850
49 Install, upgrade and uninstall icmplib with these commands:
51 The recommended way to install or upgrade icmplib is to use `pip3`:
5052
5153 ```shell
5254 $ pip3 install icmplib
5355 $ pip3 install --upgrade icmplib
54 $ pip3 uninstall icmplib
55 ```
56
57 icmplib requires Python 3.6 or later.
58
59 Import icmplib into your project (only import what you need):
56 ```
57
58 *icmplib requires Python 3.6 or later.*
59
60 To import icmplib into your project (only import what you need):
6061
6162 ```python
6263 # For simple use
63 from icmplib import ping, multiping, traceroute, Host, Hop
64 from icmplib import ping, multiping, traceroute, resolve, Host, Hop
6465
6566 # For advanced use (sockets)
6667 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply
6768
6869 # Exceptions
69 from icmplib import ICMPLibError, ICMPSocketError, SocketPermissionError
70 from icmplib import ICMPLibError, NameLookupError, ICMPSocketError
71 from icmplib import SocketAddressError, SocketPermissionError
7072 from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded
7173 from icmplib import ICMPError, DestinationUnreachable, TimeExceeded
7274 ```
7678 ## Built-in functions
7779
7880 ### Ping
79 Send *ICMP ECHO_REQUEST* packets to a network host.
80
81 #### Definition
82 ```python
83 ping(address, count=4, interval=1, timeout=2, id=PID, **kwargs)
81 Send ICMP Echo Request packets to a network host.
82
83 ```python
84 ping(address, count=4, interval=1, timeout=2, id=PID, source=None, privileged=True, **kwargs)
8485 ```
8586
8687 #### Parameters
8788 - `address`
8889
89 The IP address of the gateway or host to which the message should be sent.
90 The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address.
9091
9192 - Type: `str`
9293
113114
114115 - `id`
115116
116 The identifier of the request. Used to match the reply with the request.<br>
117 In practice, a unique identifier is used for every ping process.
117 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every ping process. On Linux, this identifier is ignored when the `privileged` parameter is disabled.
118118
119119 - Type: `int`
120120 - Default: `PID`
121121
122 - `**kwargs`
123
124 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
122 - `source`
123
124 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
125
126 - Type: `str`
127 - Default: `None`
128
129 - `privileged`
130
131 When this option is enabled, this library fully manages the exchanges and the structure of ICMP packets. Disable this option if you want to use this function without root privileges and let the kernel handle ICMP headers.
132
133 *Only available on Unix systems. Ignored on Windows.*
134
135 - Type: `bool`
136 - Default: `True`
137
138 - `payload`
139
140 The payload content in bytes. A random payload is used by default.
141
142 - Type: `bytes`
143 - Default: `None`
144
145 - `payload_size`
146
147 The payload size. Ignored when the `payload` parameter is set.
148
149 - Type: `int`
150 - Default: `56`
151
152 - `traffic_class`
153
154 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
155
156 *Only available on Unix systems. Ignored on Windows.*
157
158 - Type: `int`
159 - Default: `0`
125160
126161 #### Return value
127 - `Host` object
128
129 A `Host` object containing statistics about the desired destination:<br>
130 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`,<br>
131 `packets_received`, `packet_loss`, `is_alive`.
162 - A `Host` object containing statistics about the desired destination:<br>
163 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
132164
133165 #### Exceptions
166 - `NameLookupError`
167
168 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
169
134170 - `SocketPermissionError`
135171
136 If the permissions are insufficient to create a socket.
172 If the privileges are insufficient to create the socket.
173
174 - `SocketAddressError`
175
176 If the source address cannot be assigned to the socket.
177
178 - `ICMPSocketError`
179
180 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
137181
138182 #### Example
139183 ```python
140184 >>> host = ping('1.1.1.1', count=10, interval=0.2)
141185
142 >>> host.address # The IP address of the gateway or host
143 '1.1.1.1' # that responded to the request
186 >>> host.address # The IP address of the host that responded
187 '1.1.1.1' # to the request
144188
145189 >>> host.min_rtt # The minimum round-trip time
146190 12.2
167211 <br>
168212
169213 ### Multiping
170 Send *ICMP ECHO_REQUEST* packets to multiple network hosts.
171
172 #### Definition
173 ```python
174 multiping(addresses, count=2, interval=1, timeout=2, id=PID, max_threads=10, **kwargs)
214 Send ICMP Echo Request packets to several network hosts.
215
216 This function relies on a single thread to send multiple packets simultaneously. If you mix IPv4 and IPv6 addresses, up to two threads are used.
217
218 ```python
219 multiping(addresses, count=2, interval=0.01, timeout=2, id=PID, source=None, privileged=True, **kwargs)
175220 ```
176221
177222 #### Parameters
178223 - `addresses`
179224
180 The IP addresses of the gateways or hosts to which messages should be sent.
225 The IP addresses of the hosts to which messages should be sent. Hostnames and FQDNs are not allowed. You can easily retrieve their IP address by calling the built-in `resolve` function.
181226
182227 - Type: `list of str`
183228
193238 The interval in seconds between sending each packet.
194239
195240 - Type: `int` or `float`
196 - Default: `1`
241 - Default: `0.01`
197242
198243 - `timeout`
199244
200 The maximum waiting time for receiving a reply in seconds.
245 The maximum waiting time for receiving all responses in seconds.
201246
202247 - Type: `int` or `float`
203248 - Default: `2`
204249
205250 - `id`
206251
207 The identifier of the requests. This identifier will be incremented by one for each destination.
252 The identifier of ICMP requests. Used to match the responses with requests. This identifier will be incremented by one for each destination. On Linux, this identifier is ignored when the `privileged` parameter is disabled.
208253
209254 - Type: `int`
210255 - Default: `PID`
211256
212 - `max_threads`
213
214 The number of threads allowed to speed up processing.
215
216 - Type: `int`
217 - Default: `10`
218
219 - `**kwargs`
220
221 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
257 - `source`
258
259 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. This parameter should not be used if you are passing both IPv4 and IPv6 addresses to this function.
260
261 - Type: `str`
262 - Default: `None`
263
264 - `privileged`
265
266 When this option is enabled, this library fully manages the exchanges and the structure of ICMP packets. Disable this option if you want to use this function without root privileges and let the kernel handle ICMP headers.
267
268 *Only available on Unix systems. Ignored on Windows.*
269
270 - Type: `bool`
271 - Default: `True`
272
273 - `payload`
274
275 The payload content in bytes. A random payload is used by default.
276
277 - Type: `bytes`
278 - Default: `None`
279
280 - `payload_size`
281
282 The payload size. Ignored when the `payload` parameter is set.
283
284 - Type: `int`
285 - Default: `56`
286
287 - `traffic_class`
288
289 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
290
291 *Only available on Unix systems. Ignored on Windows.*
292
293 - Type: `int`
294 - Default: `0`
222295
223296 #### Return value
224 - `List of Host`
225
226 A `list of Host` objects containing statistics about the desired destinations:<br>
227 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`,<br>
228 `packets_received`, `packet_loss`, `is_alive`.<br>
297 - A `list of Host` objects containing statistics about the desired destinations:<br>
298 `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive`
299
229300 The list is sorted in the same order as the addresses passed in parameters.
230301
231302 #### Exceptions
232303 - `SocketPermissionError`
233304
234 If the permissions are insufficient to create a socket.
305 If the privileges are insufficient to create the socket.
306
307 - `SocketAddressError`
308
309 If the source address cannot be assigned to the socket.
310
311 - `ICMPSocketError`
312
313 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
235314
236315 #### Example
237316 ```python
256335 ### Traceroute
257336 Determine the route to a destination host.
258337
259 The Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking the route one's packets follow can be difficult. This function utilizes the IP protocol time to live field and attempts to elicit an *ICMP TIME_EXCEEDED* response from each gateway along the path to some host.
260
261 #### Definition
262 ```python
263 traceroute(address, count=3, interval=0.05, timeout=2, id=PID, traffic_class=0, max_hops=30, fast_mode=False, **kwargs)
338 The Internet is a large and complex aggregation of network hardware, connected together by gateways. Tracking the route one's packets follow can be difficult. This function uses the IP protocol time to live field and attempts to elicit an ICMP Time Exceeded response from each gateway along the path to some host.
339
340 *This function requires root privileges to run.*
341
342 ```python
343 traceroute(address, count=2, interval=0.05, timeout=2, id=PID, first_hop=1, max_hops=30, source=None, fast=False, **kwargs)
264344 ```
265345
266346 #### Parameters
267347 - `address`
268348
269 The destination IP address.
349 The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address.
270350
271351 - Type: `str`
272352
275355 The number of ping to perform per hop.
276356
277357 - Type: `int`
278 - Default: `3`
358 - Default: `2`
279359
280360 - `interval`
281361
293373
294374 - `id`
295375
296 The identifier of the request. Used to match the reply with the request.<br>
297 In practice, a unique identifier is used for every ping process.
376 The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process.
298377
299378 - Type: `int`
300379 - Default: `PID`
301380
302 - `traffic_class`
303
304 The traffic class of packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery).
305
306 Intermediate routers must be able to support this feature.<br>
307 *Only available on Unix systems. Ignored on Windows.*
308
309 - Type: `int`
310 - Default: `0`
381 - `first_hop`
382
383 The initial time to live value used in outgoing probe packets.
384
385 - Type: `int`
386 - Default: `1`
311387
312388 - `max_hops`
313389
316392 - Type: `int`
317393 - Default: `30`
318394
319 - `fast_mode`
320
321 When this option is enabled and an intermediate router has been reached, skip to the next hop rather than perform additional requests. The `count` parameter then becomes the maximum number of requests in case of no responses.
395 - `source`
396
397 The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination.
398
399 - Type: `str`
400 - Default: `None`
401
402 - `fast`
403
404 When this option is enabled and an intermediate router has been reached, skip to the next hop rather than perform additional requests. The `count` parameter then becomes the maximum number of requests in the event of no response.
322405
323406 - Type: `bool`
324407 - Default: `False`
325408
326 - `**kwargs`
327
328 `Optional` Advanced use: arguments passed to `ICMPRequest` objects.
409 - `payload`
410
411 The payload content in bytes. A random payload is used by default.
412
413 - Type: `bytes`
414 - Default: `None`
415
416 - `payload_size`
417
418 The payload size. Ignored when the `payload` parameter is set.
419
420 - Type: `int`
421 - Default: `56`
422
423 - `traffic_class`
424
425 The traffic class of ICMP packets. Provides a defined level of service to packets by setting the DS Field (formerly TOS) or the Traffic Class field of IP headers. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
426
427 *Only available on Unix systems. Ignored on Windows.*
428
429 - Type: `int`
430 - Default: `0`
329431
330432 #### Return value
331 - `List of Hop`
332
333 A `list of Hop` objects representing the route to the desired host. A `Hop` is a `Host` object with an additional attribute: a `distance`. The list is sorted in ascending order according to the distance (in terms of hops) that separates the remote host from the current machine.
433 - A `list of Hop` objects representing the route to the desired destination. A `Hop` has the same properties as a `Host` object but it also has a `distance`.
434
435 The list is sorted in ascending order according to the distance, in terms of hops, that separates the remote host from the current machine. Gateways that do not respond to requests are not added to this list.
334436
335437 #### Exceptions
438 - `NameLookupError`
439
440 If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved.
441
336442 - `SocketPermissionError`
337443
338 If the permissions are insufficient to create a socket.
444 If the privileges are insufficient to create the socket.
445
446 - `SocketAddressError`
447
448 If the source address cannot be assigned to the socket.
449
450 - `ICMPSocketError`
451
452 If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details.
339453
340454 #### Example
341455 ```python
342456 >>> hops = traceroute('1.1.1.1')
343457
344 >>> print('Distance (ttl) Address Average round-trip time')
458 >>> print('Distance/TTL Address Average round-trip time')
345459 >>> last_distance = 0
346460
347461 >>> for hop in hops:
348462 ... if last_distance + 1 != hop.distance:
349 ... print('Some routers are not responding')
463 ... print('Some gateways are not responding')
350464 ...
351465 ... # See the Hop class for details
352466 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
354468 ... last_distance = hop.distance
355469 ...
356470
357 # Distance (ttl) Address Average round-trip time
358 # 1 10.0.0.1 5.196 ms
359 # 2 194.149.169.49 7.552 ms
360 # 3 194.149.166.54 12.21 ms
361 # * Some routers are not responding
362 # 5 212.73.205.22 22.15 ms
363 # 6 1.1.1.1 13.59 ms
471 # Distance/TTL Address Average round-trip time
472 # 1 10.0.0.1 5.196 ms
473 # 2 194.149.169.49 7.552 ms
474 # 3 194.149.166.54 12.21 ms
475 # * Some gateways are not responding
476 # 5 212.73.205.22 22.15 ms
477 # 6 1.1.1.1 13.59 ms
364478 ```
365479
366480 <br>
367481
368482 ## ICMP sockets
369483
370 If you want to create your own functions and classes using the ICMP protocol, you can use the `ICMPv4Socket` (for IPv4) and the `ICMPv6Socket` (for IPv6 only). These classes have many methods and attributes in common. They manipulate `ICMPRequest` and `ICMPReply` objects.
371
372 ```
373 ┌─────────────────┐
374 ┌─────────────────┐ send(...) │ ICMPSocket: │ receive() ┌─────────────────┐
375 │ ICMPRequest │ ────────────> │ ICMPv4Socket or │ ────────────> │ ICMPReply │
376 └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘
377 └─────────────────┘
484 If you want to create your own functions and classes using the ICMP protocol, you can use the `ICMPv4Socket` (for IPv4 only) and the `ICMPv6Socket` (for IPv6 only). These classes have many methods and properties in common. They manipulate `ICMPRequest` and `ICMPReply` objects.
485
486 ```
487 ┌──────────────────┐
488 ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐
489 │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │
490 └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘
491 └──────────────────┘
378492 ```
379493
380494 ### ICMPRequest
381 A user-created object that represents an *ICMP ECHO_REQUEST*.
382
383 #### Definition
384 ```python
385 ICMPRequest(destination, id, sequence, payload=None, payload_size=56, timeout=2, ttl=64, traffic_class=0)
386 ```
387
388 #### Parameters / Getters
495 A user-created object that represents an ICMP Echo Request.
496
497 ```python
498 ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0)
499 ```
500
501 #### Parameters and properties
389502 - `destination`
390503
391 The IP address of the gateway or host to which the message should be sent.
504 The IP address of the host to which the message should be sent.
392505
393506 - Type: `str`
394507
395508 - `id`
396509
397 The identifier of the request. Used to match the reply with the request.<br>
398 In practice, a unique identifier is used for every ping process.
510 The identifier of the request. Used to match the reply with the request. In practice, a unique identifier is used for every ping process. On Linux, this identifier is automatically replaced if the request is sent from an unprivileged socket.
399511
400512 - Type: `int`
401513
402514 - `sequence`
403515
404 The sequence number. Used to match the reply with the request.<br>
405 Typically, the sequence number is incremented for each packet sent during the process.
516 The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process.
406517
407518 - Type: `int`
408519
420531 - Type: `int`
421532 - Default: `56`
422533
423 - `timeout`
424
425 The maximum waiting time for receiving a reply in seconds.
426
427 - Type: `int` or `float`
428 - Default: `2`
429
430534 - `ttl`
431535
432 The time to live of the packet in seconds.
536 The time to live of the packet in terms of hops.
433537
434538 - Type: `int`
435539 - Default: `64`
436540
437541 - `traffic_class`
438542
439 The traffic class of the packet. Provides a defined level of service to the packet by setting the DS Field (formerly TOS) or the Traffic Class field of the IP header. Packets are delivered with the minimum priority by default (Best-effort delivery).
440
441 Intermediate routers must be able to support this feature.<br>
543 The traffic class of the packet. Provides a defined level of service to the packet by setting the DS Field (formerly TOS) or the Traffic Class field of the IP header. Packets are delivered with the minimum priority by default (Best-effort delivery). Intermediate routers must be able to support this feature.
544
442545 *Only available on Unix systems. Ignored on Windows.*
443546
444547 - Type: `int`
445548 - Default: `0`
446549
447 #### Getters only
550 #### Properties only
448551 - `time`
449552
450 The timestamp of the ICMP request. Initialized to zero when creating the request and replaced by `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
553 The timestamp of the ICMP request. Initialized to zero when creating the request and replaced by the `send` method of `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
451554
452555 - Type: `float`
453556
454557 <br>
455558
456559 ### ICMPReply
457 A class that represents an ICMP reply. Generated from an `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`).
458
459 #### Definition
560 A class that represents an ICMP reply. Generated from an ICMP socket (`ICMPv4Socket` or `ICMPv6Socket`).
561
460562 ```python
461563 ICMPReply(source, id, sequence, type, code, bytes_received, time)
462564 ```
463565
464 #### Parameters / Getters
566 #### Parameters and properties
465567 - `source`
466568
467569 The IP address of the gateway or host that composes the ICMP message.
470572
471573 - `id`
472574
473 The identifier of the request. Used to match the reply with the request.
575 The identifier of the reply. Used to match the reply with the request.
474576
475577 - Type: `int`
476578
482584
483585 - `type`
484586
485 The type of message.
587 The type of ICMP message.
486588
487589 - Type: `int`
488590
489591 - `code`
490592
491 The error code.
593 The ICMP error code.
492594
493595 - Type: `int`
494596
507609 #### Methods
508610 - `raise_for_status()`
509611
510 Throw an exception if the reply is not an *ICMP ECHO_REPLY*.<br>
511 Otherwise, do nothing.
512
513 - Raises `ICMPv4DestinationUnreachable`: If the ICMPv4 reply is type 3.
514 - Raises `ICMPv4TimeExceeded`: If the ICMPv4 reply is type 11.
515 - Raises `ICMPv6DestinationUnreachable`: If the ICMPv6 reply is type 1.
516 - Raises `ICMPv6TimeExceeded`: If the ICMPv6 reply is type 3.
517 - Raises `ICMPError`: If the reply is of another type and is not an *ICMP ECHO_REPLY*.
612 Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing.
613
614 - Raises `DestinationUnreachable`: If the destination is unreachable for some reason.
615 - Raises `TimeExceeded`: If the time to live field of the ICMP request has reached zero.
616 - Raises `ICMPError`: Raised for any other type and ICMP error code, except ICMP Echo Reply messages.
518617
519618 <br>
520619
521620 ### ICMPv4Socket
522 Socket for sending and receiving ICMPv4 packets.
523
524 #### Definition
525 ```python
526 ICMPv4Socket()
527 ```
621 Class for sending and receiving ICMPv4 packets.
622
623 ```python
624 ICMPv4Socket(address=None, privileged=True)
625 ```
626
627 #### Parameters
628 - `source`
629
630 The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces.
631
632 - Type: `str`
633 - Default: `None`
634
635 - `privileged`
636
637 When this option is enabled, the socket fully manages the exchanges and the structure of the ICMP packets. Disable this option if you want to instantiate and use the socket without root privileges and let the kernel handle ICMP headers.
638
639 *Only available on Unix systems. Ignored on Windows.*
640
641 - Type: `bool`
642 - Default: `True`
528643
529644 #### Methods
530 - `__init__()`
645 - `__init__(address=None, privileged=True)`
531646
532647 *Constructor. Automatically called: do not call it directly.*
533648
534 - Raises `SocketPermissionError`: If the permissions are insufficient to create the socket.
649 - Raises `SocketPermissionError`: If the privileges are insufficient to create the socket.
650 - Raises `SocketAddressError`: If the requested address cannot be assigned to the socket.
651 - Raises `ICMPSocketError`: If another error occurs while creating the socket.
535652
536653 - `__del__()`
537654
538655 *Destructor. Automatically called: do not call it directly.*
539656
540 - Call the `close` method.
657 Call the `close` method.
541658
542659 - `send(request)`
543660
544 Send a request to a host.
545
661 Send an ICMP request message over the network to a remote host.<br>
546662 This operation is non-blocking. Use the `receive` method to get the reply.
547663
548 - Parameter `ICMPRequest`: The ICMP request you have created.
664 - Parameter `request` *(ICMPRequest)*: The ICMP request you have created. If the socket is used in non-privileged mode on a Linux system, the identifier defined in the request will be replaced by the kernel.
549665 - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only).
550666 - Raises `SocketUnavailableError`: If the socket is closed.
551667 - Raises `ICMPSocketError`: If another error occurs while sending.
552668
553 - `receive()`
554
555 Receive a reply from a host.
556
557 This method can be called multiple times if you expect several responses (as with a broadcast address).
558
559 - Raises `TimeoutExceeded`: If no response is received before the timeout defined in the request. This exception is also useful for stopping a possible loop in case of multiple responses.
669 - `receive(request=None, timeout=2)`
670
671 Receive an ICMP reply message from the socket.<br>
672 This method can be called multiple times if you expect several responses as with a broadcast address.
673
674 - Parameter `request` *(ICMPRequest)*: The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned.
675 - Parameter `timeout` *(int or float)*: The maximum waiting time for receiving the response in seconds. Default to `2`.
676 - Raises `TimeoutExceeded`: If no response is received before the timeout specified in parameters.
560677 - Raises `SocketUnavailableError`: If the socket is closed.
561678 - Raises `ICMPSocketError`: If another error occurs while receiving.
562 - Returns `ICMPReply`: An `ICMPReply` object containing the reply of the desired destination. See the `ICMPReply` class for details.
679
680 Returns an `ICMPReply` object representing the response of the desired destination or an upstream gateway.
563681
564682 - `close()`
565683
566684 Close the socket. It cannot be used after this call.
567685
568 #### Getters only
686 #### Properties
687 - `address`
688
689 The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces.
690
691 - Type: `str`
692
693 - `is_privileged`
694
695 Indicate whether the socket is running in privileged mode.
696
697 - Type: `bool`
698
569699 - `is_closed`
570700
571701 Indicate whether the socket is closed.
572702
573703 - Type: `bool`
574704
575 #### Getters / Setters
705 #### Properties and setters
576706 - `broadcast`
577707
578708 Enable or disable the broadcast support on the socket.
583713 <br>
584714
585715 ### ICMPv6Socket
586 Socket for sending and receiving ICMPv6 packets.
587
588 #### Definition
589 ```python
590 ICMPv6Socket()
591 ```
592
593 #### Methods
594 The same methods as for the `ICMPv4Socket` class.
716 Class for sending and receiving ICMPv6 packets.
717
718 ```python
719 ICMPv6Socket(address=None, privileged=True)
720 ```
721
722 #### Methods and properties
723 The same methods and properties as for the `ICMPv4Socket` class, except the `broadcast` property.
595724
596725 <br>
597726
600729
601730 ```
602731 ICMPLibError
732 ├─ NameLookupError
603733 ├─ ICMPSocketError
734 │ ├─ SocketAddressError
604735 │ ├─ SocketPermissionError
605736 │ ├─ SocketUnavailableError
606737 │ ├─ SocketBroadcastError
607738 │ └─ TimeoutExceeded
608
739
609740 └─ ICMPError
610741 ├─ DestinationUnreachable
611742 │ ├─ ICMPv4DestinationUnreachable
612743 │ └─ ICMPv6DestinationUnreachable
613
744
614745 └─ TimeExceeded
615746 ├─ ICMPv4TimeExceeded
616747 └─ ICMPv6TimeExceeded
617748 ```
618749
619750 - `ICMPLibError`: Exception class for the icmplib package.
751 - `NameLookupError`: Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames.
620752 - `ICMPSocketError`: Base class for ICMP sockets exceptions.
621 - `SocketPermissionError`: Raised when the permissions are insufficient to create a socket.
753 - `SocketAddressError`: Raised when the requested address cannot be assigned to the socket.
754 - `SocketPermissionError`: Raised when the privileges are insufficient to create the socket.
622755 - `SocketUnavailableError`: Raised when an action is performed while the socket is closed.
623756 - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket.
624757 - `TimeoutExceeded`: Raised when a timeout occurs on a socket.
626759 - `DestinationUnreachable`: Destination Unreachable message is generated by the host or its inbound gateway to inform the client that the destination is unreachable for some reason.
627760 - `TimeExceeded`: Time Exceeded message is generated by a gateway to inform the source of a discarded datagram due to the time to live field reaching zero. A Time Exceeded message may also be sent by a host if it fails to reassemble a fragmented datagram within its time limit.
628761
629 Use the `message` property to retrieve the error message.
630
631 `ICMPError` subclasses have properties to retrieve the response (`reply` property) and the specific message of the error (`message` property).
762 Use the `message` property to get the error message. `ICMPError` subclasses have a `reply` property to retrieve the response.
632763
633764 <br>
634765
637768 ```python
638769 def single_ping(address, timeout=2, id=PID):
639770 # Create an ICMP socket
640 socket = ICMPv4Socket()
641
642 # Create a request
643 # See the ICMPRequest class for details
771 sock = ICMPv4Socket()
772
773 # Create an ICMP request
774 # See the 'ICMPRequest' class for details
644775 request = ICMPRequest(
645776 destination=address,
646777 id=id,
647 sequence=1,
648 timeout=timeout)
778 sequence=1)
649779
650780 try:
651 socket.send(request)
652
653 # If the program arrives in this section,
654 # it means that the packet has been transmitted
655
656 reply = socket.receive()
657
658 # If the program arrives in this section,
659 # it means that a packet has been received
660 # The reply has the same identifier and sequence number that
661 # the request but it can come from an intermediate gateway
781 sock.send(request)
782
783 # If the program arrives in this section, it means that the
784 # packet has been transmitted.
785
786 reply = sock.receive(request, timeout)
787
788 # If the program arrives in this section, it means that a
789 # packet has been received. The reply has the same identifier
790 # and sequence number that the request but it can come from
791 # an intermediate gateway.
662792
663793 reply.raise_for_status()
664794
665 # If the program arrives in this section,
666 # it means that the destination host has responded to
667 # the request
795 # If the program arrives in this section, it means that the
796 # destination host has responded to the request.
668797
669798 except TimeoutExceeded as err:
670799 # The timeout has been reached
671 # Equivalent to print(err.message)
672800 print(err)
673801
674802 except DestinationUnreachable as err:
675 # The reply indicates that the destination host is
676 # unreachable
803 # The reply indicates that the destination host is unreachable
677804 print(err)
678805
679806 # Retrieve the response
680807 reply = err.reply
681808
682809 except TimeExceeded as err:
683 # The reply indicates that the time to live exceeded
684 # in transit
810 # The reply indicates that the time to live exceeded in transit
685811 print(err)
686812
687813 # Retrieve the response
697823 #### Verbose ping
698824 ```python
699825 def verbose_ping(address, count=4, interval=1, timeout=2, id=PID):
700 # ICMPRequest uses a payload of 56 bytes by default
701 # You can modify it using the payload_size parameter
702 print(f'PING {address}: 56 data bytes')
703
704 # Detection of the socket to use
826 # A payload of 56 bytes is used by default. You can modify it using
827 # the 'payload_size' parameter of your ICMP request.
828 print(f'PING {address}: 56 data bytes\n')
829
830 # We detect the socket to use from the specified IP address
705831 if is_ipv6_address(address):
706 socket = ICMPv6Socket()
832 sock = ICMPv6Socket()
707833
708834 else:
709 socket = ICMPv4Socket()
835 sock = ICMPv4Socket()
710836
711837 for sequence in range(count):
712838 # We create an ICMP request
713839 request = ICMPRequest(
714840 destination=address,
715841 id=id,
716 sequence=sequence,
717 timeout=timeout)
842 sequence=sequence)
718843
719844 try:
720845 # We send the request
721 socket.send(request)
846 sock.send(request)
722847
723848 # We are awaiting receipt of an ICMP reply
724 reply = socket.receive()
849 reply = sock.receive(request, timeout)
725850
726851 # We received a reply
727852 # We display some information
767892
768893 ## FAQ
769894
770 ### How to resolve a FQDN / domain name?
771 Python has a method to do this in its libraries:
772 ```python
773 >>> import socket
774 >>> socket.gethostbyname('github.com')
895 ### How to resolve a FQDN/domain name or a hostname?
896 The use of the built-in `resolve` function is recommended:
897
898 ```python
899 >>> resolve('github.com')
775900 '140.82.118.4'
776901 ```
777902
903 - If several IP addresses are available, only the first one is returned. This function searches for IPv4 addresses first before searching for IPv6 addresses.
904 - If you pass an IP address, no lookup is done. The same address is returned.
905 - Raises a `NameLookupError` exception if the requested name does not exist or cannot be resolved.
906
907 ### Why I have no response from a remote host?
908 In the event of no response from a remote host, several causes are possible:
909 - Your computer's firewall may not be properly configured. This impacts in particular the `traceroute` function which can no longer receive ICMP Time Exceeded messages.
910 - The remote host or an upstream gateway is down.
911 - The remote host or an upstream gateway drops ICMP messages for security reasons.
912 - In the case of the `traceroute` function, if the last host in the list is not the one expected, more than 30 hops (default) may be needed to reach it. You can try increasing the value of the `max_hops` parameter.
913
778914 ## Contributing
779915
780916 Comments and enhancements are welcome.
2020 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
2121 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
2222 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
23
24 Completed.
2325 ```
2426
2527 - [verbose-traceroute](verbose_traceroute.py)
3234 Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max
3335
3436 1 192.168.0.254 192.168.0.254 9.86 ms
35 2 194.149.164.56 194.149.164.56 4.6 ms
36 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.99 ms
37 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 7.81 ms
37 2 194.149.164.56 194.149.164.56 4.61 ms
38 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.97 ms
39 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 15.81 ms
3840 5 * * *
39 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms
41 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.12 ms
4042 7 198.27.92.1 www.ovh.com 10.87 ms
43
44 Completed.
4145 ```
4246
4347 - [broadcast-ping](broadcast_ping.py)
6468 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms
6569 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms
6670 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms
71
72 Completed.
6773 ```
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
1518 64 bytes from 10.0.0.17: icmp_seq=0 time=1.065 ms
1619 64 bytes from 10.0.0.40: icmp_seq=0 time=1.595 ms
1720 64 bytes from 10.0.0.41: icmp_seq=0 time=9.471 ms
18
1921 64 bytes from 10.0.0.17: icmp_seq=1 time=0.983 ms
2022 64 bytes from 10.0.0.40: icmp_seq=1 time=1.579 ms
2123 64 bytes from 10.0.0.41: icmp_seq=1 time=9.345 ms
22
2324 64 bytes from 10.0.0.17: icmp_seq=2 time=0.916 ms
2425 64 bytes from 10.0.0.40: icmp_seq=2 time=2.031 ms
2526 64 bytes from 10.0.0.41: icmp_seq=2 time=9.554 ms
26
2727 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms
2828 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms
2929 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms
30
31 Completed.
3032 '''
3133
32 from icmplib import (
33 ICMPv4Socket,
34 ICMPRequest,
35 TimeoutExceeded,
36 ICMPLibError,
37 PID)
34 from icmplib import ICMPv4Socket, ICMPRequest
35 from icmplib import ICMPLibError, TimeoutExceeded, PID
3836
3937
4038 def broadcast_ping(address, count=4, timeout=1, id=PID):
41 # ICMPRequest uses a payload of 56 bytes by default
42 # You can modify it using the payload_size parameter
43 print(f'PING {address}: 56 data bytes')
39 # A payload of 56 bytes is used by default. You can modify it using
40 # the 'payload_size' parameter of your ICMP request.
41 print(f'PING {address}: 56 data bytes\n')
4442
4543 # Broadcast is only possible in IPv4
46 socket = ICMPv4Socket()
44 sock = ICMPv4Socket()
4745
4846 # We allow the socket to send broadcast packets
49 socket.broadcast = True
47 sock.broadcast = True
5048
5149 for sequence in range(count):
5250 # We create an ICMP request
5351 request = ICMPRequest(
5452 destination=address,
5553 id=id,
56 sequence=sequence,
57 timeout=timeout)
58
59 print()
54 sequence=sequence)
6055
6156 try:
6257 # We send the request
63 socket.send(request)
58 sock.send(request)
6459
6560 while 'we receive replies':
66 # We are awaiting receipt of an ICMP reply
67 # If there is no more responses, the TimeoutExceeded
68 # exception is thrown and the loop is stopped
69 reply = socket.receive()
61 # We are awaiting receipt of an ICMP reply. If there is
62 # no more responses, the 'TimeoutExceeded' exception is
63 # thrown and the loop is stopped.
64 reply = sock.receive(request, timeout)
7065
71 # We calculate the round-trip time of the reply
66 # We calculate the round-trip time
7267 round_trip_time = (reply.time - request.time) * 1000
7368
7469 # We display some information
8378
8479 except ICMPLibError:
8580 # All other errors
86 print('An error has occurred.')
81 print(' An error has occurred.')
82
83 print('\nCompleted.')
8784
8885
8986 # Limited broadcast
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
1114 Example: multiping
1215 '''
1316
14 from icmplib import multiping
17 from icmplib import resolve, multiping
1518
1619
1720 addresses = [
18 # A fully qualified domain name (FQDN) is allowed. The first
19 # address returned from the DNS resolution will be used.
20 # For deterministic behavior, prefer to use an IP address.
21 'github.com',
22
2321 # IPv4 addresses
2422 '1.1.1.1',
2523 '8.8.8.8',
2826
2927 # IPv6 addresses
3028 '::1',
29
30 # Hostnames and Fully Qualified Domain Names (FQDNs) are not
31 # allowed. You can easily retrieve their IP address by calling the
32 # built-in 'resolve' function. The first address returned from the
33 # DNS resolution will be used. For deterministic behavior, prefer
34 # to use an IP address.
35 resolve('github.com')
3136 ]
3237
33 hosts = multiping(addresses, count=2, interval=0.5, timeout=1)
38 hosts = multiping(addresses, count=2, timeout=1)
3439
3540 hosts_alive = []
3641 hosts_dead = []
4348 hosts_dead.append(host.address)
4449
4550 print(hosts_alive)
46 # ['github.com', '1.1.1.1', '8.8.8.8', '::1']
51 # ['1.1.1.1', '8.8.8.8', '::1', '140.82.121.4']
4752
4853 print(hosts_dead)
4954 # ['10.0.0.100', '10.0.0.200']
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
2023 print(host.address)
2124 # '1.1.1.1'
2225
23 # The minimum round-trip time
26 # The minimum round-trip time in milliseconds
2427 print(host.min_rtt)
2528 # 12.2
2629
27 # The average round-trip time
30 # The average round-trip time in milliseconds
2831 print(host.avg_rtt)
2932 # 13.2
3033
31 # The maximum round-trip time
34 # The maximum round-trip time in milliseconds
3235 print(host.max_rtt)
3336 # 17.6
3437
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
1417 from icmplib import traceroute
1518
1619
17 hops = traceroute('1.1.1.1', timeout=1, fast_mode=True)
20 hops = traceroute('1.1.1.1', timeout=1, fast=True)
1821
1922 print(hops)
20 # [<Hop 1 [192.168.0.254]>, <Hop 2 [194.149.169.162]>,
21 # <Hop 4 [149.11.115.13]>, <Hop 5 [154.54.61.21]>,
22 # <Hop 6 [154.54.60.126]>, <Hop 7 [149.11.0.126]>,
23 # <Hop 8 [1.1.1.1]>]
24
23 # [ <Hop 1 [10.0.0.1]>,
24 # <Hop 2 [194.149.169.49]>,
25 # <Hop 3 [194.149.166.54]>,
26 # <Hop 5 [212.73.205.22]>,
27 # <Hop 6 [1.1.1.1]> ]
2528
2629 last_distance = 0
2730
2831 for hop in hops:
2932 if last_distance + 1 != hop.distance:
30 print(' * Some routers are not responding')
33 print(' * Some gateways are not responding')
3134
32 print(f'{hop.distance:4} {hop.address:15} '
35 print(f' {hop.distance:<2} {hop.address:15} '
3336 f'{hop.avg_rtt} ms')
3437
3538 last_distance = hop.distance
3639
37 # 1 192.168.0.254 11.327 ms
38 # 2 194.149.169.162 16.354 ms
39 # * Some routers are not responding
40 # 4 149.11.115.13 11.498 ms
41 # 5 154.54.61.21 4.335 ms
42 # 6 154.54.60.126 5.645 ms
43 # 7 149.11.0.126 5.873 ms
44 # 8 1.1.1.1 4.561 ms
40 # 1 10.0.0.1 5.196 ms
41 # 2 194.149.169.49 7.552 ms
42 # 3 194.149.166.54 12.21 ms
43 # * Some gateways are not responding
44 # 5 212.73.205.22 22.15 ms
45 # 6 1.1.1.1 13.59 ms
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
1619 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms
1720 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms
1821 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms
22
23 Completed.
1924 '''
20
21 from icmplib import (
22 ICMPv4Socket,
23 ICMPv6Socket,
24 ICMPRequest,
25 TimeoutExceeded,
26 ICMPError,
27 ICMPLibError,
28 is_ipv6_address,
29 PID)
3025
3126 from time import sleep
3227
28 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest
29 from icmplib import ICMPLibError, ICMPError, TimeoutExceeded
30 from icmplib import PID, is_ipv6_address
31
3332
3433 def verbose_ping(address, count=4, interval=1, timeout=2, id=PID):
35 # ICMPRequest uses a payload of 56 bytes by default
36 # You can modify it using the payload_size parameter
34 # A payload of 56 bytes is used by default. You can modify it using
35 # the 'payload_size' parameter of your ICMP request.
3736 print(f'PING {address}: 56 data bytes\n')
3837
39 # Detection of the socket to use
38 # We detect the socket to use from the specified IP address
4039 if is_ipv6_address(address):
41 socket = ICMPv6Socket()
40 sock = ICMPv6Socket()
4241
4342 else:
44 socket = ICMPv4Socket()
43 sock = ICMPv4Socket()
4544
4645 for sequence in range(count):
4746 # We create an ICMP request
4847 request = ICMPRequest(
4948 destination=address,
5049 id=id,
51 sequence=sequence,
52 timeout=timeout)
50 sequence=sequence)
5351
5452 try:
5553 # We send the request
56 socket.send(request)
54 sock.send(request)
5755
5856 # We are awaiting receipt of an ICMP reply
59 reply = socket.receive()
57 reply = sock.receive(request, timeout)
6058
6159 # We received a reply
6260 # We display some information
7876
7977 except TimeoutExceeded:
8078 # The timeout has been reached
81 print(f'Request timeout for icmp_seq {sequence}')
79 print(f' Request timeout for icmp_seq {sequence}')
8280
8381 except ICMPError as err:
8482 # An ICMP error message has been received
8684
8785 except ICMPLibError:
8886 # All other errors
89 print('An error has occurred.')
87 print(' An error has occurred.')
88
89 print('\nCompleted.')
9090
9191
9292 verbose_ping('1.1.1.1')
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
1316 Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max
1417
1518 1 192.168.0.254 192.168.0.254 9.86 ms
16 2 194.149.164.56 194.149.164.56 4.6 ms
17 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.99 ms
18 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 7.81 ms
19 2 194.149.164.56 194.149.164.56 4.61 ms
20 3 213.186.32.181 be100-159.th2-1-a9.fr.eu 11.97 ms
21 4 94.23.122.146 be102.rbx-g1-nc5.fr.eu 15.81 ms
1922 5 * * *
20 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms
23 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.12 ms
2124 7 198.27.92.1 www.ovh.com 10.87 ms
25
26 Completed.
2227 '''
2328
24 from icmplib import (
25 ICMPv4Socket,
26 ICMPv6Socket,
27 ICMPRequest,
28 TimeoutExceeded,
29 TimeExceeded,
30 ICMPLibError,
31 is_ipv6_address,
32 PID)
33
34 from socket import getfqdn, gethostbyname
29 from socket import getfqdn
3530 from time import sleep
3631
32 from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest
33 from icmplib import ICMPLibError, TimeoutExceeded, TimeExceeded
34 from icmplib import PID, resolve, is_ipv6_address
3735
38 def verbose_traceroute(address, count=3, interval=0.05, timeout=2,
36
37 def verbose_traceroute(address, count=2, interval=0.05, timeout=2,
3938 id=PID, max_hops=30):
40 # ICMPRequest uses a payload of 56 bytes by default
41 # You can modify it using the payload_size parameter
42 print(f'Traceroute to {address} ({gethostbyname(address)}): '
39 # We perform a DNS resolution of the address passed in parameters.
40 # If the address is already an IP address, no lookup is done. The
41 # same address is returned.
42 ip_address = resolve(address)
43
44 # A payload of 56 bytes is used by default. You can modify it using
45 # the 'payload_size' parameter of your ICMP request.
46 print(f'Traceroute to {address} ({ip_address}): '
4347 f'56 data bytes, {max_hops} hops max\n')
4448
45 # Detection of the socket to use
46 if is_ipv6_address(address):
47 socket = ICMPv6Socket()
49 # We detect the socket to use from the specified IP address
50 if is_ipv6_address(ip_address):
51 sock = ICMPv6Socket()
4852
4953 else:
50 socket = ICMPv4Socket()
54 sock = ICMPv4Socket()
5155
5256 ttl = 1
5357 host_reached = False
5660 for sequence in range(count):
5761 # We create an ICMP request
5862 request = ICMPRequest(
59 destination=address,
63 destination=ip_address,
6064 id=id,
6165 sequence=sequence,
62 timeout=timeout,
6366 ttl=ttl)
6467
6568 try:
6669 # We send the request
67 socket.send(request)
70 sock.send(request)
6871
6972 # We are awaiting receipt of an ICMP reply
70 reply = socket.receive()
73 reply = sock.receive(request, timeout)
7174
7275 # We received a reply
7376 # We display some information
7477 source_name = getfqdn(reply.source)
7578
76 print(f'{ttl:3} {reply.source:15} '
79 print(f' {ttl:<2} {reply.source:15} '
7780 f'{source_name:40} ', end='')
7881
7982 # We throw an exception if it is an ICMP error message
103106
104107 except TimeoutExceeded:
105108 # The timeout has been reached and no host or gateway
106 # has responded after multiple attemps
109 # has responded after multiple attempts
107110 if sequence >= count - 1:
108 print(f'{ttl:3} * * *')
111 print(f' {ttl:<2} * * *')
109112
110113 except ICMPLibError:
111114 # Other errors are ignored
113116
114117 ttl += 1
115118
116 print()
119 print('\nCompleted.')
117120
118121
122 # This function supports both FQDNs and IP addresses. See the 'resolve'
123 # function for details.
119124 verbose_traceroute('ovh.com')
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
2831 from .ping import ping, multiping
2932 from .traceroute import traceroute
3033 from .exceptions import *
31 from .utils import PID, is_ipv4_address, is_ipv6_address
34 from .utils import PID, resolve, is_ipv4_address, is_ipv6_address
3235
3336
3437 __author__ = 'Valentin BELYN'
3538 __copyright__ = 'Copyright 2017-2020 Valentin BELYN'
3639 __license__ = 'GNU Lesser General Public License v3.0'
3740
38 __version__ = '1.2.2'
39 __build__ = '201010'
41 __version__ = '2.0'
42 __build__ = '201115'
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
4043 return self._message
4144
4245
46 class NameLookupError(ICMPLibError):
47 '''
48 Raised when the requested name does not exist or cannot be
49 resolved. This concerns both Fully Qualified Domain Names and
50 hostnames.
51
52 '''
53 def __init__(self, name):
54 message = f'The name \'{name}\' cannot be resolved'
55 super().__init__(message)
56
57
4358 class ICMPSocketError(ICMPLibError):
4459 '''
4560 Base class for ICMP sockets exceptions.
4762 '''
4863
4964
65 class SocketAddressError(ICMPSocketError):
66 '''
67 Raised when the requested address cannot be assigned to the socket.
68
69 '''
70 def __init__(self, address):
71 message = f'The requested address ({address}) cannot be ' \
72 'assigned to the socket'
73 super().__init__(message)
74
75
5076 class SocketPermissionError(ICMPSocketError):
5177 '''
52 Raised when the permissions are insufficient to create a socket.
78 Raised when the privileges are insufficient to create the socket.
5379
5480 '''
5581 def __init__(self):
75101 '''
76102 def __init__(self):
77103 message = 'Broadcast is not allowed: ' \
78 'please use broadcast method (setter) to allow it'
104 'please use the \'broadcast\' property to allow it'
79105 super().__init__(message)
80106
81107
+0
-152
icmplib/ip.py less more
0 '''
1 icmplib
2 ~~~~~~~
3
4 https://github.com/ValentinBELYN/icmplib
5
6 :copyright: Copyright 2017-2020 Valentin BELYN.
7 :license: GNU LGPLv3, see the LICENSE for details.
8
9 ~~~~~~~
10
11 This program is free software: you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public License
13 as published by the Free Software Foundation, either version 3 of
14 the License, or (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU Lesser General Public License for more details.
20
21 You should have received a copy of the GNU Lesser General Public
22 License along with this program. If not, see
23 <https://www.gnu.org/licenses/>.
24 '''
25
26 import socket
27 from .utils import PLATFORM_WINDOWS
28
29
30 # Fix for Windows
31 if PLATFORM_WINDOWS:
32 socket.IPPROTO_IPV6 = 41
33
34
35 class IPSocket:
36 def __init__(self, family, protocol):
37 self._socket = socket.socket(
38 family=family,
39 type=socket.SOCK_RAW,
40 proto=protocol)
41
42 self.timeout = 5
43 self.ttl = 64
44 self.traffic_class = 0
45
46 def send(self, payload, address, port):
47 return self._socket.sendto(payload, (address, port))
48
49 def receive(self, buffer_size=1024):
50 packet = self._socket.recvfrom(buffer_size)
51
52 payload = packet[0]
53 address = packet[1][0]
54 port = packet[1][1]
55
56 return payload, address, port
57
58 def close(self):
59 self._socket.close()
60
61 @property
62 def timeout(self):
63 return self._timeout
64
65 @timeout.setter
66 def timeout(self, timeout):
67 self._socket.settimeout(timeout)
68 self._timeout = timeout
69
70 @property
71 def ttl(self):
72 return self._ttl
73
74 @ttl.setter
75 def ttl(self, ttl):
76 self._ttl = ttl
77
78 @property
79 def traffic_class(self):
80 return self._traffic_class
81
82 @traffic_class.setter
83 def traffic_class(self, traffic_class):
84 self._traffic_class = traffic_class
85
86
87 class IPv4Socket(IPSocket):
88 def __init__(self, protocol):
89 super().__init__(
90 family=socket.AF_INET,
91 protocol=protocol)
92
93 self._broadcast = False
94
95 @IPSocket.ttl.setter
96 def ttl(self, ttl):
97 self._ttl = ttl
98
99 self._socket.setsockopt(
100 socket.IPPROTO_IP,
101 socket.IP_TTL,
102 ttl)
103
104 @IPSocket.traffic_class.setter
105 def traffic_class(self, traffic_class):
106 self._traffic_class = traffic_class
107
108 if not PLATFORM_WINDOWS:
109 self._socket.setsockopt(
110 socket.IPPROTO_IP,
111 socket.IP_TOS,
112 traffic_class)
113
114 @property
115 def broadcast(self):
116 return self._broadcast
117
118 @broadcast.setter
119 def broadcast(self, allow):
120 self._broadcast = allow
121
122 self._socket.setsockopt(
123 socket.SOL_SOCKET,
124 socket.SO_BROADCAST,
125 allow)
126
127
128 class IPv6Socket(IPSocket):
129 def __init__(self, protocol):
130 super().__init__(
131 family=socket.AF_INET6,
132 protocol=protocol)
133
134 @IPSocket.ttl.setter
135 def ttl(self, ttl):
136 self._ttl = ttl
137
138 self._socket.setsockopt(
139 socket.IPPROTO_IPV6,
140 socket.IPV6_MULTICAST_HOPS,
141 ttl)
142
143 @IPSocket.traffic_class.setter
144 def traffic_class(self, traffic_class):
145 self._traffic_class = traffic_class
146
147 if not PLATFORM_WINDOWS:
148 self._socket.setsockopt(
149 socket.IPPROTO_IPV6,
150 socket.IPV6_TCLASS,
151 traffic_class)
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
2427 '''
2528
2629 from .exceptions import *
27 from .utils import is_ipv6_address
2830
2931
3032 class ICMPRequest:
3133 '''
32 A user-created object that represents an ICMP ECHO_REQUEST.
34 A user-created object that represents an ICMP Echo Request.
3335
3436 :type destination: str
35 :param destination: The IP address of the gateway or host to which
36 the message should be sent.
37 :param destination: The IP address of the host to which the message
38 should be sent.
3739
3840 :type id: int
3941 :param id: The identifier of the request. Used to match the reply
4042 with the request. In practice, a unique identifier is used for
41 every ping process.
43 every ping process. On Linux, this identifier is automatically
44 replaced if the request is sent from an unprivileged socket.
4245
4346 :type sequence: int
4447 :param sequence: The sequence number. Used to match the reply with
4548 the request. Typically, the sequence number is incremented for
4649 each packet sent during the process.
4750
48 :type payload: bytes
49 :param payload: (Optional) The payload content in bytes. A random
50 payload is used by default.
51
52 :type payload_size: int
53 :param payload_size: (Optional) The payload size. Ignored when the
54 `payload` parameter is set.
55
56 :type timeout: int or float
57 :param timeout: (Optional) The maximum waiting time for receiving
58 the reply in seconds.
59
60 :type ttl: int
61 :param ttl: (Optional) The time to live of the packet in seconds.
62
63 :type traffic_class: int
64 :param traffic_class: (Optional) The traffic class of the packet.
51 :type payload: bytes, optional
52 :param payload: The payload content in bytes. A random payload is
53 used by default.
54
55 :type payload_size: int, optional
56 :param payload_size: The payload size. Ignored when the `payload`
57 parameter is set. Default to 56.
58
59 :type ttl: int, optional
60 :param ttl: The time to live of the packet in terms of hops.
61 Default to 64.
62
63 :type traffic_class: int, optional
64 :param traffic_class: The traffic class of the packet.
6565 Provides a defined level of service to the packet by setting
6666 the DS Field (formerly TOS) or the Traffic Class field of the
6767 IP header. Packets are delivered with the minimum priority by
7171
7272 '''
7373 def __init__(self, destination, id, sequence, payload=None,
74 payload_size=56, timeout=2, ttl=64, traffic_class=0):
75
76 id &= 0xffff
77 sequence &= 0xffff
74 payload_size=56, ttl=64, traffic_class=0):
7875
7976 if payload:
8077 payload_size = len(payload)
8178
8279 self._destination = destination
83 self._id = id
84 self._sequence = sequence
80 self._id = id & 0xffff
81 self._sequence = sequence & 0xffff
8582 self._payload = payload
8683 self._payload_size = payload_size
87 self._timeout = timeout
8884 self._ttl = ttl
8985 self._traffic_class = traffic_class
9086 self._time = 0
9591 @property
9692 def destination(self):
9793 '''
98 The IP address of the gateway or host to which the message
99 should be sent.
94 The IP address of the host to which the message should be sent.
10095
10196 '''
10297 return self._destination
137132 return self._payload_size
138133
139134 @property
140 def timeout(self):
141 '''
142 The maximum waiting time for receiving the reply in seconds.
143
144 '''
145 return self._timeout
146
147 @property
148135 def ttl(self):
149136 '''
150 The time to live of the packet in seconds.
137 The time to live of the packet in terms of hops.
151138
152139 '''
153140 return self._ttl
164151 def time(self):
165152 '''
166153 The timestamp of the ICMP request.
154
167155 Initialized to zero when creating the request and replaced by
168 `ICMPv4Socket` or `ICMPv6Socket` with the time of sending.
156 the `send` method of `ICMPv4Socket` or `ICMPv6Socket` with the
157 time of sending.
169158
170159 '''
171160 return self._time
173162
174163 class ICMPReply:
175164 '''
176 A class that represents an ICMP reply. Generated from an
177 `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`).
165 A class that represents an ICMP reply. Generated from an ICMP
166 socket (`ICMPv4Socket` or `ICMPv6Socket`).
178167
179168 :type source: str
180169 :param source: The IP address of the gateway or host that composes
189178 the request.
190179
191180 :type type: int
192 :param type: The type of message.
181 :param type: The type of ICMP message.
193182
194183 :type code: int
195 :param code: The error code.
184 :param code: The ICMP error code.
196185
197186 :type bytes_received: int
198187 :param bytes_received: The number of bytes received.
217206
218207 def raise_for_status(self):
219208 '''
220 Throw an exception if the reply is not an ICMP ECHO_REPLY.
209 Throw an exception if the reply is not an ICMP Echo Reply.
221210 Otherwise, do nothing.
222211
223 :raises ICMPv4DestinationUnreachable: If the ICMPv4 reply is
224 type 3.
225 :raises ICMPv4TimeExceeded: If the ICMPv4 reply is type 11.
226 :raises ICMPv6DestinationUnreachable: If the ICMPv6 reply is
227 type 1.
228 :raises ICMPv6TimeExceeded: If the ICMPv6 reply is type 3.
229 :raises ICMPError: If the reply is of another type and is not
230 an ICMP ECHO_REPLY.
231
232 '''
233 if is_ipv6_address(self._source):
212 :raises DestinationUnreachable: If the destination is
213 unreachable for some reason.
214 :raises TimeExceeded: If the time to live field of the ICMP
215 request has reached zero.
216 :raises ICMPError: Raised for any other type and ICMP error
217 code, except ICMP Echo Reply messages.
218
219 '''
220 if ':' in self._source:
234221 echo_reply_type = 129
222
235223 errors = {
236224 1: ICMPv6DestinationUnreachable,
237225 3: ICMPv6TimeExceeded
239227
240228 else:
241229 echo_reply_type = 0
230
242231 errors = {
243232 3: ICMPv4DestinationUnreachable,
244233 11: ICMPv4TimeExceeded
265254 @property
266255 def id(self):
267256 '''
268 The identifier of the request.
257 The identifier of the reply.
269258 Used to match the reply with the request.
270259
271260 '''
283272 @property
284273 def type(self):
285274 '''
286 The type of message.
275 The type of ICMP message.
287276
288277 '''
289278 return self._type
291280 @property
292281 def code(self):
293282 '''
294 The error code.
283 The ICMP error code.
295284
296285 '''
297286 return self._code
305294 return self._bytes_received
306295
307296 @property
308 def received_bytes(self):
309 '''
310 Deprecated: use the `bytes_received` property instead.
311
312 '''
313 print('[icmplib] Deprecation Warning: The `received_bytes` '
314 'property will be removed from icmplib 2.0. Use the '
315 '`bytes_received` property instead.')
316
317 return self._bytes_received
318
319 @property
320297 def time(self):
321298 '''
322299 The timestamp of the ICMP reply.
327304
328305 class Host:
329306 '''
330 A class that represents a host. Simplifies the exploitation of
331 results from `ping` and `traceroute` functions.
307 A class that represents a host. It simplifies the use of the
308 results from the `ping`, `multiping` and `traceroute` functions.
332309
333310 :type address: str
334311 :param address: The IP address of the gateway or host that
335312 responded to the request.
336313
337314 :type min_rtt: float
338 :param min_rtt: The minimum round-trip time.
315 :param min_rtt: The minimum round-trip time in milliseconds.
339316
340317 :type avg_rtt: float
341 :param avg_rtt: The average round-trip time.
318 :param avg_rtt: The average round-trip time in milliseconds.
342319
343320 :type max_rtt: float
344 :param max_rtt: The maximum round-trip time.
321 :param max_rtt: The maximum round-trip time in milliseconds.
345322
346323 :type packets_sent: int
347324 :param packets_sent: The number of packets transmitted to the
377354 @property
378355 def min_rtt(self):
379356 '''
380 The minimum round-trip time.
357 The minimum round-trip time in milliseconds.
381358
382359 '''
383360 return self._min_rtt
385362 @property
386363 def avg_rtt(self):
387364 '''
388 The average round-trip time.
365 The average round-trip time in milliseconds.
389366
390367 '''
391368 return self._avg_rtt
393370 @property
394371 def max_rtt(self):
395372 '''
396 The maximum round-trip time.
373 The maximum round-trip time in milliseconds.
397374
398375 '''
399376 return self._max_rtt
404381 The number of packets transmitted to the destination host.
405382
406383 '''
407 return self._packets_sent
408
409 @property
410 def transmitted_packets(self):
411 '''
412 Deprecated: use the `packets_sent` property instead.
413
414 '''
415 print('[icmplib] Deprecation Warning: The '
416 '`transmitted_packets` property will be removed from '
417 'icmplib 2.0. Use the `packets_sent` property instead.')
418
419384 return self._packets_sent
420385
421386 @property
428393 return self._packets_received
429394
430395 @property
431 def received_packets(self):
432 '''
433 Deprecated: use the `packets_received` property instead.
434
435 '''
436 print('[icmplib] Deprecation Warning: The `received_packets` '
437 'property will be removed from icmplib 2.0. Use the '
438 '`packets_received` property instead.')
439
440 return self._packets_received
441
442 @property
443396 def packet_loss(self):
444397 '''
445398 Packet loss occurs when packets fail to reach their destination.
449402 if not self._packets_sent:
450403 return 0.0
451404
452 return 1 - self._packets_received / self._packets_sent
405 packet_loss = 1 - self._packets_received / self._packets_sent
406
407 return round(packet_loss, 3)
453408
454409 @property
455410 def is_alive(self):
456411 '''
457 Indicate whether the host is reachable. Return a `boolean`.
412 Indicate whether the host is reachable.
413 Return a `boolean`.
458414
459415 '''
460416 return self._packets_received > 0
470426 responded to the request.
471427
472428 :type min_rtt: float
473 :param min_rtt: The minimum round-trip time.
429 :param min_rtt: The minimum round-trip time in milliseconds.
474430
475431 :type avg_rtt: float
476 :param avg_rtt: The average round-trip time.
432 :param avg_rtt: The average round-trip time in milliseconds.
477433
478434 :type max_rtt: float
479 :param max_rtt: The maximum round-trip time.
435 :param max_rtt: The maximum round-trip time in milliseconds.
480436
481437 :type packets_sent: int
482438 :param packets_sent: The number of packets transmitted to the
487443 host and received by the current host.
488444
489445 :type distance: int
490 :param distance: The distance (in terms of hops) that separates the
446 :param distance: The distance, in terms of hops, that separates the
491447 remote host from the current machine.
492448
493449 '''
505461 @property
506462 def distance(self):
507463 '''
508 The distance (in terms of hops) that separates the remote host
464 The distance, in terms of hops, that separates the remote host
509465 from the current machine.
510466
511467 '''
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
2326 <https://www.gnu.org/licenses/>.
2427 '''
2528
26 from threading import Thread
2729 from time import sleep
2830
29 from .sockets import ICMPv4Socket, ICMPv6Socket
31 from .sockets import ICMPv4Socket, ICMPv6Socket, BufferedSocket
3032 from .models import ICMPRequest, Host
3133 from .exceptions import *
32 from .utils import PID, resolve, is_ipv6_address
33
34
35 class PingThread(Thread):
36 def __init__(self, **kwargs):
37 super().__init__()
38 self._kwargs = kwargs
39 self._host = None
40
41 def run(self):
42 self._host = ping(**self._kwargs)
43
44 @property
45 def host(self):
46 return self._host
47
48
49 def ping(address, count=4, interval=1, timeout=2, id=PID, **kwargs):
34 from .utils import PID, PLATFORM_LINUX, resolve, is_ipv6_address
35
36
37 def ping(address, count=4, interval=1, timeout=2, id=PID, source=None,
38 privileged=True, **kwargs):
5039 '''
51 Send ICMP ECHO_REQUEST packets to a network host.
40 Send ICMP Echo Request packets to a network host.
5241
5342 :type address: str
54 :param address: The IP address of the gateway or host to which
55 the message should be sent.
56
57 :type count: int
58 :param count: (Optional) The number of ping to perform.
59
60 :type interval: int or float
61 :param interval: (Optional) The interval in seconds between sending
62 each packet.
63
64 :type timeout: int or float
65 :param timeout: (Optional) The maximum waiting time for receiving
66 a reply in seconds.
67
68 :type id: int
69 :param id: (Optional) The identifier of the request. Used to match
70 the reply with the request. In practice, a unique identifier is
71 used for every ping process.
72
73 :param **kwargs: (Optional) Advanced use: arguments passed to
74 `ICMPRequest` objects.
43 :param address: The IP address, hostname or FQDN of the host to
44 which messages should be sent. For deterministic behavior,
45 prefer to use an IP address.
46
47 :type count: int, optional
48 :param count: The number of ping to perform. Default to 4.
49
50 :type interval: int or float, optional
51 :param interval: The interval in seconds between sending each
52 packet. Default to 1.
53
54 :type timeout: int or float, optional
55 :param timeout: The maximum waiting time for receiving a reply in
56 seconds. Default to 2.
57
58 :type id: int, optional
59 :param id: The identifier of ICMP requests. Used to match the
60 responses with requests. In practice, a unique identifier
61 should be used for every ping process. On Linux, this
62 identifier is ignored when the `privileged` parameter is
63 disabled.
64
65 :type source: str, optional
66 :param source: The IP address from which you want to send packets.
67 By default, the interface is automatically chosen according to
68 the specified destination.
69
70 :type privileged: bool, optional
71 :param privileged: When this option is enabled, this library fully
72 manages the exchanges and the structure of ICMP packets.
73 Disable this option if you want to use this function without
74 root privileges and let the kernel handle ICMP headers.
75 Default to True.
76 Only available on Unix systems. Ignored on Windows.
77
78 Advanced (**kwags):
79
80 :type payload: bytes, optional
81 :param payload: The payload content in bytes. A random payload is
82 used by default.
83
84 :type payload_size: int, optional
85 :param payload_size: The payload size. Ignored when the `payload`
86 parameter is set. Default to 56.
87
88 :type traffic_class: int, optional
89 :param traffic_class: The traffic class of ICMP packets.
90 Provides a defined level of service to packets by setting the
91 DS Field (formerly TOS) or the Traffic Class field of IP
92 headers. Packets are delivered with the minimum priority by
93 default (Best-effort delivery).
94 Intermediate routers must be able to support this feature.
95 Only available on Unix systems. Ignored on Windows.
7596
7697 :rtype: Host
7798 :returns: A `Host` object containing statistics about the desired
7899 destination.
79100
80 :raises SocketPermissionError: If the permissions are insufficient
81 to create a socket.
101 :raises NameLookupError: If you pass a hostname or FQDN in
102 parameters and it does not exist or cannot be resolved.
103 :raises SocketPermissionError: If the privileges are insufficient
104 to create the socket.
105 :raises SocketAddressError: If the source address cannot be
106 assigned to the socket.
107 :raises ICMPSocketError: If another error occurs. See the
108 `ICMPv4Socket` or `ICMPv6Socket` class for details.
82109
83110 Usage::
84111
85112 >>> from icmplib import ping
86113 >>> host = ping('1.1.1.1')
87
88114 >>> host.avg_rtt
89115 13.2
90
91116 >>> host.is_alive
92117 True
93118
97122 address = resolve(address)
98123
99124 if is_ipv6_address(address):
100 socket = ICMPv6Socket()
125 sock = ICMPv6Socket(
126 address=source,
127 privileged=privileged)
101128
102129 else:
103 socket = ICMPv4Socket()
130 sock = ICMPv4Socket(
131 address=source,
132 privileged=privileged)
104133
105134 packets_sent = 0
106135 packets_received = 0
114143 destination=address,
115144 id=id,
116145 sequence=sequence,
117 timeout=timeout,
118146 **kwargs)
119147
120148 try:
121 socket.send(request)
149 sock.send(request)
122150 packets_sent += 1
123151
124 reply = socket.receive()
152 reply = sock.receive(request, timeout)
125153 reply.raise_for_status()
126154 packets_received += 1
127155
150178 packets_sent=packets_sent,
151179 packets_received=packets_received)
152180
153 socket.close()
181 sock.close()
154182
155183 return host
156184
157185
158 def multiping(addresses, count=2, interval=1, timeout=2, id=PID,
159 max_threads=10, **kwargs):
186 def multiping(addresses, count=2, interval=0.01, timeout=2, id=PID,
187 source=None, privileged=True, **kwargs):
160188 '''
161 Send ICMP ECHO_REQUEST packets to multiple network hosts.
189 Send ICMP Echo Request packets to several network hosts.
190
191 This function relies on a single thread to send multiple packets
192 simultaneously. If you mix IPv4 and IPv6 addresses, up to two
193 threads are used.
162194
163195 :type addresses: list of str
164 :param addresses: The IP addresses of the gateways or hosts to
165 which messages should be sent.
166
167 :type count: int
168 :param count: (Optional) The number of ping to perform per address.
169
170 :type interval: int or float
171 :param interval: (Optional) The interval in seconds between sending
172 each packet.
173
174 :type timeout: int or float
175 :param timeout: (Optional) The maximum waiting time for receiving
176 a reply in seconds.
177
178 :type id: int
179 :param id: (Optional) The identifier of the requests. This
180 identifier will be incremented by one for each destination.
181
182 :type max_threads: int
183 :param max_threads: (Optional) The number of threads allowed to
184 speed up processing.
185
186 :param **kwargs: (Optional) Advanced use: arguments passed to
187 `ICMPRequest` objects.
196 :param addresses: The IP addresses of the hosts to which messages
197 should be sent. Hostnames and FQDNs are not allowed. You can
198 easily retrieve their IP address by calling the built-in
199 `resolve` function.
200
201 :type count: int, optional
202 :param count: The number of ping to perform per address.
203 Default to 2.
204
205 :type interval: int or float, optional
206 :param interval: The interval in seconds between sending each
207 packet. Default to 0.01.
208
209 :type timeout: int or float, optional
210 :param timeout: The maximum waiting time for receiving all
211 responses in seconds. Default to 2.
212
213 :type id: int, optional
214 :param id: The identifier of ICMP requests. Used to match the
215 responses with requests. This identifier will be incremented by
216 one for each destination. On Linux, this identifier is ignored
217 when the `privileged` parameter is disabled.
218
219 :type source: str, optional
220 :param source: The IP address from which you want to send packets.
221 By default, the interface is automatically chosen according to
222 the specified destinations. This parameter should not be used
223 if you are passing both IPv4 and IPv6 addresses to this
224 function.
225
226 :type privileged: bool, optional
227 :param privileged: When this option is enabled, this library fully
228 manages the exchanges and the structure of ICMP packets.
229 Disable this option if you want to use this function without
230 root privileges and let the kernel handle ICMP headers.
231 Default to True.
232 Only available on Unix systems. Ignored on Windows.
233
234 Advanced (**kwags):
235
236 :type payload: bytes, optional
237 :param payload: The payload content in bytes. A random payload is
238 used by default.
239
240 :type payload_size: int, optional
241 :param payload_size: The payload size. Ignored when the `payload`
242 parameter is set. Default to 56.
243
244 :type traffic_class: int, optional
245 :param traffic_class: The traffic class of ICMP packets.
246 Provides a defined level of service to packets by setting the
247 DS Field (formerly TOS) or the Traffic Class field of IP
248 headers. Packets are delivered with the minimum priority by
249 default (Best-effort delivery).
250 Intermediate routers must be able to support this feature.
251 Only available on Unix systems. Ignored on Windows.
188252
189253 :rtype: list of Host
190254 :returns: A list of `Host` objects containing statistics about the
191255 desired destinations. The list is sorted in the same order as
192256 the addresses passed in parameters.
193257
194 :raises SocketPermissionError: If the permissions are insufficient
195 to create a socket.
258 :raises SocketPermissionError: If the privileges are insufficient
259 to create the socket.
260 :raises SocketAddressError: If the source address cannot be
261 assigned to the socket.
262 :raises ICMPSocketError: If another error occurs. See the
263 `ICMPv4Socket` or `ICMPv6Socket` class for details.
196264
197265 Usage::
198266
213281 See the `Host` class for details.
214282
215283 '''
284 index = {}
285 sock_ipv4 = None
286 sock_ipv6 = None
287 sequence_offset = 0
288
289 # We create the ICMP requests and instantiate the sockets
290 for i, address in enumerate(addresses):
291 if not privileged and PLATFORM_LINUX:
292 sequence_offset = i * count
293
294 requests = [
295 ICMPRequest(
296 destination=address,
297 id=id + i,
298 sequence=sequence + sequence_offset,
299 **kwargs)
300
301 for sequence in range(count)
302 ]
303
304 if is_ipv6_address(address):
305 if not sock_ipv6:
306 sock_ipv6 = BufferedSocket(
307 ICMPv6Socket(
308 address=source,
309 privileged=privileged))
310
311 sock = sock_ipv6
312
313 else:
314 if not sock_ipv4:
315 sock_ipv4 = BufferedSocket(
316 ICMPv4Socket(
317 address=source,
318 privileged=privileged))
319
320 sock = sock_ipv4
321
322 index[address] = requests, sock
323
324 # We send the ICMP requests
325 for sequence in range(count):
326 for address in addresses:
327 request = index[address][0][sequence]
328 sock = index[address][1]
329
330 try:
331 sock.send(request)
332 sleep(interval)
333
334 except ICMPSocketError:
335 pass
336
216337 hosts = []
217 inactive_threads = []
218 active_threads = []
219
220 for i, address in enumerate(addresses):
221 thread = PingThread(
338
339 # We retrieve the responses and relate them to the ICMP requests
340 for address in addresses:
341 requests = index[address][0]
342 sock = index[address][1]
343
344 packets_received = 0
345 min_rtt = float('inf')
346 avg_rtt = 0.0
347 max_rtt = 0.0
348
349 for request in requests:
350 try:
351 reply = sock.receive(request, timeout)
352 reply.raise_for_status()
353 packets_received += 1
354
355 round_trip_time = (reply.time - request.time) * 1000
356 avg_rtt += round_trip_time
357 min_rtt = min(round_trip_time, min_rtt)
358 max_rtt = max(round_trip_time, max_rtt)
359
360 except ICMPLibError:
361 pass
362
363 if packets_received:
364 avg_rtt /= packets_received
365
366 else:
367 min_rtt = 0.0
368
369 host = Host(
222370 address=address,
223 count=count,
224 interval=interval,
225 timeout=timeout,
226 id=id + i,
227 **kwargs)
228
229 inactive_threads.append(thread)
230
231 while inactive_threads:
232 thread = inactive_threads.pop(0)
233 thread.start()
234 active_threads.append(thread)
235
236 if (inactive_threads and
237 len(active_threads) < max_threads):
238 sleep(0.05)
239 continue
240
241 while active_threads:
242 thread = active_threads.pop(0)
243 thread.join()
244 hosts.append(thread.host)
371 min_rtt=min_rtt,
372 avg_rtt=avg_rtt,
373 max_rtt=max_rtt,
374 packets_sent=len(requests),
375 packets_received=packets_received)
376
377 hosts.append(host)
378
379 if sock_ipv4:
380 sock_ipv4.close()
381
382 if sock_ipv6:
383 sock_ipv6.close()
245384
246385 return hosts
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
2427 '''
2528
2629 import socket
30
31 from threading import Thread, Lock, Event
2732 from struct import pack, unpack
2833 from time import time
2934
30 from .ip import IPv4Socket, IPv6Socket
3135 from .models import ICMPRequest, ICMPReply
3236 from .exceptions import *
33 from .utils import random_byte_message
34
35
36 # Echo Request and Echo Reply messages -- RFC 792 / 4443
37 #
38 # 0 1 2 3
39 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
40 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 # | Type | Code | Checksum |
42 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
43 # | Identifier | Sequence Number |
44 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
45 # | Data ...
46 # +-+-+-+-+-
47 #
48 # ICMPv4 Error message -- RFC 792
49 #
50 # 0 1 2 3
51 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
52 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53 # | Type | Code | Checksum |
54 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55 # | Unused / Depends on the error |
56 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57 # | Internet Header + 64 bits of Original Data Datagram |
58 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
59 #
60 # ICMPv6 Error message -- RFC 4443
61 #
62 # 0 1 2 3
63 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
64 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 # | Type | Code | Checksum |
66 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67 # | Unused / Depends on the error |
68 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69 # | Original packet without exceed the minimum IPv6 MTU |
70 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71
72
73 class ICMPConfig:
74 IP_PROTOCOL = -1
75 IP_SOCKET = None
76
77 ICMP_HEADER_OFFSET = -1
78 ICMP_CODE_OFFSET = ICMP_HEADER_OFFSET + 1
79 ICMP_CHECKSUM_OFFSET = ICMP_HEADER_OFFSET + 2
80 ICMP_ID_OFFSET = ICMP_HEADER_OFFSET + 4
81 ICMP_SEQUENCE_OFFSET = ICMP_HEADER_OFFSET + 6
82 ICMP_PAYLOAD_OFFSET = ICMP_HEADER_OFFSET + 8
83
84 ICMP_ECHO_REQUEST = -1
85 ICMP_ECHO_REPLY = -1
86
87
88 class ICMPv4Config(ICMPConfig):
89 IP_PROTOCOL = 1
90 IP_SOCKET = IPv4Socket
91
92 ICMP_HEADER_OFFSET = 20
93 ICMP_CODE_OFFSET = ICMP_HEADER_OFFSET + 1
94 ICMP_CHECKSUM_OFFSET = ICMP_HEADER_OFFSET + 2
95 ICMP_ID_OFFSET = ICMP_HEADER_OFFSET + 4
96 ICMP_SEQUENCE_OFFSET = ICMP_HEADER_OFFSET + 6
97 ICMP_PAYLOAD_OFFSET = ICMP_HEADER_OFFSET + 8
98
99 ICMP_ECHO_REQUEST = 8
100 ICMP_ECHO_REPLY = 0
101
102
103 class ICMPv6Config(ICMPConfig):
104 IP_PROTOCOL = 58
105 IP_SOCKET = IPv6Socket
106
107 ICMP_HEADER_OFFSET = 0
108 ICMP_CODE_OFFSET = ICMP_HEADER_OFFSET + 1
109 ICMP_CHECKSUM_OFFSET = ICMP_HEADER_OFFSET + 2
110 ICMP_ID_OFFSET = ICMP_HEADER_OFFSET + 4
111 ICMP_SEQUENCE_OFFSET = ICMP_HEADER_OFFSET + 6
112 ICMP_PAYLOAD_OFFSET = ICMP_HEADER_OFFSET + 8
113
114 ICMP_ECHO_REQUEST = 128
115 ICMP_ECHO_REPLY = 129
37 from .utils import *
11638
11739
11840 class ICMPSocket:
11941 '''
12042 Base class for ICMP sockets.
12143
122 :type config: ICMPConfig
123 :param config: The ICMP socket configuration used to create and
124 read ICMP packets.
125
126 :raises SocketPermissionError: If the permissions are insufficient
44 :type address: str, optional
45 :param address: The IP address from which you want to listen and
46 send packets. By default, the socket listens on all interfaces.
47
48 :type privileged: bool, optional
49 :param privileged: When this option is enabled, the socket fully
50 manages the exchanges and the structure of the ICMP packets.
51 Disable this option if you want to instantiate and use the
52 socket without root privileges and let the kernel handle ICMP
53 headers. Default to True.
54 Only available on Unix systems. Ignored on Windows.
55
56 :raises SocketPermissionError: If the privileges are insufficient
12757 to create the socket.
58 :raises SocketAddressError: If the requested address cannot be
59 assigned to the socket.
60 :raises ICMPSocketError: If another error occurs while creating the
61 socket.
12862
12963 '''
130 def __init__(self, config):
131 self._socket = None
132 self._config = config
133 self._last_request = None
64 _ICMP_HEADER_OFFSET = -1
65 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
66 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
67 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
68 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
69 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
70
71 _ICMP_ECHO_REQUEST = -1
72 _ICMP_ECHO_REPLY = -1
73
74 def __init__(self, address=None, privileged=True):
75 self._sock = None
76 self._address = address
77
78 # The Linux kernel allows unprivileged users to use datagram
79 # sockets (SOCK_DGRAM) to send ICMP requests. This feature is
80 # now supported by the majority of Unix systems.
81 # Windows is not compatible.
82 self._privileged = privileged or PLATFORM_WINDOWS
13483
13584 try:
136 self._socket = config.IP_SOCKET(
137 config.IP_PROTOCOL)
138
139 except OSError:
140 raise SocketPermissionError
85 self._sock = self._create_socket(
86 socket.SOCK_RAW if self._privileged else
87 socket.SOCK_DGRAM)
88
89 if address:
90 self._sock.bind((address, 0))
91
92 except OSError as err:
93 if err.errno in (1, 10013):
94 raise SocketPermissionError
95
96 if err.errno in (-9, 49, 99, 10049, 11001):
97 raise SocketAddressError(address)
98
99 raise ICMPSocketError(str(err))
100
101 def __enter__(self):
102 '''
103 Return this object.
104
105 '''
106 return self
107
108 def __exit__(self, type, value, traceback):
109 '''
110 Call the `close` method.
111
112 '''
113 self.close()
141114
142115 def __del__(self):
143116 '''
146119 '''
147120 self.close()
148121
149 def _create_header(self, type, code, checksum, id, sequence):
150 '''
151 Create the ICMP header of a packet.
152
153 '''
154 # B: 8 bits
155 # H: 16 bits
156 return pack('!2B3H', type, code, checksum, id, sequence)
122 def _create_socket(self, type):
123 '''
124 Create and return a new socket. Must be overridden.
125
126 '''
127 raise NotImplementedError
128
129 def _set_ttl(self, ttl):
130 '''
131 Set the time to live of every IP packet originating from this
132 socket. Must be overridden.
133
134 '''
135 raise NotImplementedError
136
137 def _set_traffic_class(self, traffic_class):
138 '''
139 Set the DS Field (formerly TOS) or the Traffic Class field of
140 every IP packet originating from this socket. Must be
141 overridden.
142
143 '''
144 raise NotImplementedError
157145
158146 def _checksum(self, data):
159147 '''
160 Calculate the checksum of a packet.
161
162 '''
148 Compute the checksum of an ICMP packet. Checksums are used to
149 verify the integrity of packets.
150
151 '''
152 sum = 0
163153 data += b'\x00'
164 end = len(data) - 1
165 sum = 0
166
167 for i in range(0, end, 2):
154
155 for i in range(0, len(data) - 1, 2):
168156 sum += (data[i] << 8) + data[i + 1]
169 sum = (sum >> 16) + (sum & 0xffff)
157 sum = (sum & 0xffff) + (sum >> 16)
170158
171159 sum = ~sum & 0xffff
172160
174162
175163 def _create_packet(self, id, sequence, payload):
176164 '''
177 Create a packet.
178
179 '''
180 type = self._config.ICMP_ECHO_REQUEST
181
182 # Temporary header to calculate the checksum
183 header = self._create_header(type, 0, 0, id, sequence)
165 Build an ICMP packet from an identifier, a sequence number and
166 a payload.
167
168 This method returns the newly created ICMP header concatenated
169 to the payload passed in parameters.
170
171 '''
172 checksum = 0
173
174 # Temporary ICMP header to compute the checksum
175 header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum,
176 id, sequence)
177
184178 checksum = self._checksum(header + payload)
185179
186 # Definitive header
187 header = self._create_header(type, 0, checksum, id, sequence)
180 # Definitive ICMP header
181 header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum,
182 id, sequence)
188183
189184 return header + payload
190185
191 def _read_reply(self, packet, source, reply_time):
192 '''
193 Read a reply from bytes. Return an `ICMPReply` object or `None`
194 if the reply cannot be parsed.
195
196 '''
197 bytes_received = (
198 len(packet)
199 - self._config.ICMP_HEADER_OFFSET)
200
201 if len(packet) < self._config.ICMP_CHECKSUM_OFFSET:
186 def _parse_reply(self, packet, source, current_time):
187 '''
188 Parse an ICMP reply from bytes.
189
190 This method returns an `ICMPReply` object or `None` if the
191 reply cannot be parsed.
192
193 '''
194 bytes_received = len(packet) - self._ICMP_HEADER_OFFSET
195
196 if len(packet) < self._ICMP_CHECKSUM_OFFSET:
202197 return None
203198
204199 type, code = unpack('!2B', packet[
205 self._config.ICMP_HEADER_OFFSET:
206 self._config.ICMP_CHECKSUM_OFFSET])
207
208 if type != self._config.ICMP_ECHO_REPLY:
209 packet = packet[self._config.ICMP_PAYLOAD_OFFSET:]
210
211 if len(packet) < self._config.ICMP_PAYLOAD_OFFSET:
200 self._ICMP_HEADER_OFFSET:
201 self._ICMP_CHECKSUM_OFFSET])
202
203 if type != self._ICMP_ECHO_REPLY:
204 packet = packet[self._ICMP_PAYLOAD_OFFSET:]
205
206 if len(packet) < self._ICMP_PAYLOAD_OFFSET:
212207 return None
213208
214209 id, sequence = unpack('!2H', packet[
215 self._config.ICMP_ID_OFFSET:
216 self._config.ICMP_PAYLOAD_OFFSET
217 ])
218
219 reply = ICMPReply(
210 self._ICMP_ID_OFFSET:
211 self._ICMP_PAYLOAD_OFFSET])
212
213 return ICMPReply(
220214 source=source,
221215 id=id,
222216 sequence=sequence,
223217 type=type,
224218 code=code,
225219 bytes_received=bytes_received,
226 time=reply_time)
227
228 return reply
220 time=current_time)
229221
230222 def send(self, request):
231223 '''
232 Send a request to a host.
224 Send an ICMP request message over the network to a remote host.
233225
234226 This operation is non-blocking. Use the `receive` method to get
235227 the reply.
236228
237229 :type request: ICMPRequest
238 :param request: The ICMP request you have created.
230 :param request: The ICMP request you have created. If the
231 socket is used in non-privileged mode on a Linux system,
232 the identifier defined in the request will be replaced by
233 the kernel.
239234
240235 :raises SocketBroadcastError: If a broadcast address is used
241236 and the corresponding option is not enabled on the socket
244239 :raises ICMPSocketError: If another error occurs while sending.
245240
246241 '''
247 if not self._socket:
242 if not self._sock:
248243 raise SocketUnavailableError
249244
250 payload = request.payload
251
252 if not payload:
253 payload = random_byte_message(
254 size=request.payload_size)
245 payload = request.payload or \
246 random_byte_message(request.payload_size)
255247
256248 packet = self._create_packet(
257249 id=request.id,
258250 sequence=request.sequence,
259251 payload=payload)
260252
261 self._socket.ttl = request.ttl
262 self._socket.traffic_class = request.traffic_class
263
264 request._time = time()
265
266253 try:
267 # The packet is actually the Ethernet payload (without the
268 # IP header): the variable name will be changed in future
269 # versions
270 self._socket.send(
271 payload=packet,
272 address=request.destination,
273 port=0)
274
275 self._last_request = request
254 self._set_ttl(request.ttl)
255 self._set_traffic_class(request.traffic_class)
256
257 request._time = time()
258 self._sock.sendto(packet, (request.destination, 0))
259
260 # On Linux, the ICMP request identifier is replaced by the
261 # kernel with a random port number when a datagram socket
262 # is used (SOCK_DGRAM). So, we update the request created
263 # by the user to take this new identifier into account.
264 if not self._privileged and PLATFORM_LINUX:
265 request._id = self._sock.getsockname()[1]
276266
277267 except PermissionError:
278268 raise SocketBroadcastError
280270 except OSError as err:
281271 raise ICMPSocketError(str(err))
282272
283 def receive(self):
284 '''
285 Receive a reply from a host.
273 def receive(self, request=None, timeout=2):
274 '''
275 Receive an ICMP reply message from the socket.
286276
287277 This method can be called multiple times if you expect several
288 responses (as with a broadcast address).
278 responses as with a broadcast address.
279
280 :type request: ICMPRequest, optional
281 :param request: The ICMP request to use to match the response.
282 By default, all ICMP packets arriving on the socket are
283 returned.
284
285 :type timeout: int or float, optional
286 :param timeout: The maximum waiting time for receiving the
287 response in seconds. Default to 2.
288
289 :rtype: ICMPReply
290 :returns: An `ICMPReply` object representing the response of
291 the desired destination or an upstream gateway. See the
292 `ICMPReply` class for details.
289293
290294 :raises TimeoutExceeded: If no response is received before the
291 timeout defined in the request.
292 This exception is also useful for stopping a possible loop
293 in case of multiple responses.
295 timeout specified in parameters.
294296 :raises SocketUnavailableError: If the socket is closed.
295297 :raises ICMPSocketError: If another error occurs while
296298 receiving.
297299
298 :rtype: ICMPReply
299 :returns: An `ICMPReply` object containing the reply of the
300 desired destination.
301
302 See the `ICMPReply` class for details.
303
304 '''
305 if not self._socket:
300 '''
301 if not self._sock:
306302 raise SocketUnavailableError
307303
308 if not self._last_request:
309 raise TimeoutExceeded(0)
310
311 request = self._last_request
312
313 current_time = time()
314 self._socket.timeout = request.timeout
315 timeout = current_time + request.timeout
304 self._sock.settimeout(timeout)
305 time_limit = time() + timeout
316306
317307 try:
318308 while True:
319 packet, address, port = self._socket.receive()
320 reply_time = time()
321
322 if reply_time > timeout:
309 response = self._sock.recvfrom(1024)
310 current_time = time()
311
312 packet = response[0]
313 source = response[1][0]
314
315 if current_time > time_limit:
323316 raise socket.timeout
324317
325 reply = self._read_reply(
318 # On Linux, the IP header is missing when a datagram
319 # socket is used (SOCK_DGRAM). To keep the same
320 # behavior on all operating systems including macOS
321 # which has this header, we add a padding of the size
322 # of the missing IP header.
323 if not self._privileged and PLATFORM_LINUX:
324 padding = b'\x00' * self._ICMP_HEADER_OFFSET
325 packet = padding + packet
326
327 reply = self._parse_reply(
326328 packet=packet,
327 source=address,
328 reply_time=reply_time)
329
330 if (reply and request.id == reply.id and
329 source=source,
330 current_time=current_time)
331
332 if (reply and not request or
333 reply and request.id == reply.id and
331334 request.sequence == reply.sequence):
332335 return reply
333336
334337 except socket.timeout:
335 raise TimeoutExceeded(request.timeout)
338 raise TimeoutExceeded(timeout)
336339
337340 except OSError as err:
338341 raise ICMPSocketError(str(err))
342345 Close the socket. It cannot be used after this call.
343346
344347 '''
345 if self._socket:
346 self._socket.close()
347 self._socket = None
348 if self._sock:
349 self._sock.close()
350 self._sock = None
351
352 @property
353 def address(self):
354 '''
355 The IP address from which the socket listens and sends packets.
356 Return `None` if the socket listens on all interfaces.
357
358 '''
359 return self._address
360
361 @property
362 def is_privileged(self):
363 '''
364 Indicate whether the socket is running in privileged mode.
365 Return a `boolean`.
366
367 '''
368 return self._privileged
348369
349370 @property
350371 def is_closed(self):
351372 '''
352 Indicate whether the socket is closed. Return a `boolean`.
353
354 '''
355 return self._socket is None
373 Indicate whether the socket is closed.
374 Return a `boolean`.
375
376 '''
377 return self._sock is None
378
379
380 # Echo Request and Echo Reply messages RFC 792
381 #
382 # 0 1 2 3
383 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
384 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
385 # | Type | Code | Checksum |
386 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
387 # | Identifier | Sequence Number |
388 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
389 # | Data ...
390 # +-+-+-+-+-
391 #
392 # ICMPv4 Error message RFC 792
393 #
394 # 0 1 2 3
395 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
396 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 # | Type | Code | Checksum |
398 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 # | Unused / Depends on the error |
400 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 # | Internet Header + 64 bits of Original Data Datagram |
402 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
356403
357404
358405 class ICMPv4Socket(ICMPSocket):
359406 '''
360 Socket for sending and receiving ICMPv4 packets.
361
362 :raises SocketPermissionError: If the permissions are insufficient
407 Class for sending and receiving ICMPv4 packets.
408
409 :type address: str, optional
410 :param address: The IP address from which you want to listen and
411 send packets. By default, the socket listens on all interfaces.
412
413 :type privileged: bool, optional
414 :param privileged: When this option is enabled, the socket fully
415 manages the exchanges and the structure of the ICMP packets.
416 Disable this option if you want to instantiate and use the
417 socket without root privileges and let the kernel handle ICMP
418 headers. Default to True.
419 Only available on Unix systems. Ignored on Windows.
420
421 :raises SocketPermissionError: If the privileges are insufficient
363422 to create the socket.
423 :raises SocketAddressError: If the requested address cannot be
424 assigned to the socket.
425 :raises ICMPSocketError: If another error occurs while creating the
426 socket.
364427
365428 '''
366 def __init__(self):
367 super().__init__(ICMPv4Config)
429 _ICMP_HEADER_OFFSET = 20
430 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
431 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
432 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
433 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
434 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
435
436 _ICMP_ECHO_REQUEST = 8
437 _ICMP_ECHO_REPLY = 0
438
439 def _create_socket(self, type):
440 '''
441 Create and return a new socket.
442
443 '''
444 return socket.socket(
445 family=socket.AF_INET,
446 type=type,
447 proto=socket.IPPROTO_ICMP)
448
449 def _set_ttl(self, ttl):
450 '''
451 Set the time to live of every IP packet originating from this
452 socket.
453
454 '''
455 self._sock.setsockopt(
456 socket.IPPROTO_IP,
457 socket.IP_TTL,
458 ttl)
459
460 def _set_traffic_class(self, traffic_class):
461 '''
462 Set the DS Field (formerly TOS) of every IP packet originating
463 from this socket.
464
465 Only available on Unix systems. Ignored on Windows.
466
467 '''
468 if PLATFORM_WINDOWS:
469 return
470
471 self._sock.setsockopt(
472 socket.IPPROTO_IP,
473 socket.IP_TOS,
474 traffic_class)
368475
369476 @property
370477 def broadcast(self):
377484 icmp_socket.broadcast = True
378485
379486 '''
380 return self._socket.broadcast
487 return self._sock.getsockopt(
488 socket.SOL_SOCKET,
489 socket.SO_BROADCAST) > 0
381490
382491 @broadcast.setter
383492 def broadcast(self, allow):
384 self._socket.broadcast = allow
493 self._sock.setsockopt(
494 socket.SOL_SOCKET,
495 socket.SO_BROADCAST,
496 allow)
497
498
499 # Echo Request and Echo Reply messages RFC 4443
500 #
501 # 0 1 2 3
502 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
503 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
504 # | Type | Code | Checksum |
505 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
506 # | Identifier | Sequence Number |
507 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
508 # | Data ...
509 # +-+-+-+-+-
510 #
511 # ICMPv6 Error message RFC 4443
512 #
513 # 0 1 2 3
514 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
515 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
516 # | Type | Code | Checksum |
517 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
518 # | Unused / Depends on the error |
519 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
520 # | Original packet without exceed the minimum IPv6 MTU |
521 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
522
523 # Windows IPv6 compatibility
524 if PLATFORM_WINDOWS: socket.IPPROTO_IPV6 = 41
385525
386526
387527 class ICMPv6Socket(ICMPSocket):
388528 '''
389 Socket for sending and receiving ICMPv6 packets.
390
391 :raises SocketPermissionError: If the permissions are insufficient
529 Class for sending and receiving ICMPv6 packets.
530
531 :type address: str, optional
532 :param address: The IP address from which you want to listen and
533 send packets. By default, the socket listens on all interfaces.
534
535 :type privileged: bool, optional
536 :param privileged: When this option is enabled, the socket fully
537 manages the exchanges and the structure of the ICMP packets.
538 Disable this option if you want to instantiate and use the
539 socket without root privileges and let the kernel handle ICMP
540 headers. Default to True.
541 Only available on Unix systems. Ignored on Windows.
542
543 :raises SocketPermissionError: If the privileges are insufficient
392544 to create the socket.
545 :raises SocketAddressError: If the requested address cannot be
546 assigned to the socket.
547 :raises ICMPSocketError: If another error occurs while creating the
548 socket.
393549
394550 '''
395 def __init__(self):
396 super().__init__(ICMPv6Config)
551 _ICMP_HEADER_OFFSET = 0
552 _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1
553 _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2
554 _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4
555 _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6
556 _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8
557
558 _ICMP_ECHO_REQUEST = 128
559 _ICMP_ECHO_REPLY = 129
560
561 def _create_socket(self, type):
562 '''
563 Create and return a new socket.
564
565 '''
566 return socket.socket(
567 family=socket.AF_INET6,
568 type=type,
569 proto=socket.IPPROTO_ICMPV6)
570
571 def _set_ttl(self, ttl):
572 '''
573 Set the time to live of every IP packet originating from this
574 socket.
575
576 '''
577 self._sock.setsockopt(
578 socket.IPPROTO_IPV6,
579 socket.IPV6_MULTICAST_HOPS,
580 ttl)
581
582 def _set_traffic_class(self, traffic_class):
583 '''
584 Set the Traffic Class field of every IP packet originating from
585 this socket.
586
587 Only available on Unix systems. Ignored on Windows.
588
589 '''
590 if PLATFORM_WINDOWS:
591 return
592
593 self._sock.setsockopt(
594 socket.IPPROTO_IPV6,
595 socket.IPV6_TCLASS,
596 traffic_class)
597
598
599 class BufferedSocket:
600 '''
601 A wrapper for ICMP sockets that reads and classifies incoming ICMP
602 packets into a buffer, in real time.
603
604 Useful if you want to send several ICMP packets consecutively
605 without waiting for a response between each sending. For this, an
606 internal thread is used. It stops automatically when you call the
607 `detach` or `close` method.
608
609 This feature is experimental. There is no guarantee that it will be
610 retained in future versions.
611
612 :type sock: ICMPSocket
613 :param sock: An ICMP socket. Once the wrapper instantiated, this
614 socket should no longer be used directly.
615
616 '''
617 def __init__(self, sock):
618 self._sock = sock
619 self._buffer = {}
620
621 self._buffer_event = Event()
622 self._buffer_lock = Lock()
623
624 self._thread = Thread(target=self._read_from_socket)
625 self._thread.start()
626
627 def __enter__(self):
628 '''
629 Return this object.
630
631 '''
632 return self
633
634 def __exit__(self, type, value, traceback):
635 '''
636 Call the `close` method.
637
638 '''
639 self.close()
640
641 def __del__(self):
642 '''
643 Call the `close` method.
644
645 '''
646 self.close()
647
648 def _read_from_socket(self):
649 '''
650 Internal thread which retrieves incoming ICMP packets from the
651 socket and adds them to a buffer.
652
653 '''
654 while self._sock:
655 try:
656 reply = self._sock.receive(timeout=1)
657
658 with self._buffer_lock:
659 buffer_id = reply.id, reply.sequence
660
661 if buffer_id not in self._buffer:
662 self._buffer[buffer_id] = []
663
664 self._buffer[buffer_id].append(reply)
665 self._buffer_event.set()
666
667 except ICMPSocketError:
668 pass
669
670 def send(self, request):
671 '''
672 Send an ICMP request message over the network to a remote host.
673
674 This operation is non-blocking. Use the `receive` method to get
675 the reply.
676
677 :type request: ICMPRequest
678 :param request: The ICMP request you have created. If the
679 underlying socket is used in non-privileged mode on a Linux
680 system, the identifier defined in the request will be
681 replaced by the kernel.
682
683 :raises SocketBroadcastError: If a broadcast address is used
684 and the corresponding option is not enabled on the
685 underlying socket (ICMPv4 only).
686 :raises SocketUnavailableError: If the socket is closed.
687 :raises ICMPSocketError: If another error occurs while sending.
688
689 '''
690 if not self._sock:
691 raise SocketUnavailableError
692
693 self._sock.send(request)
694
695 def receive(self, request, timeout=2):
696 '''
697 Retrieve an ICMP reply message from the buffer.
698
699 This method can be called multiple times if you expect several
700 responses as with a broadcast address.
701
702 :type request: ICMPRequest
703 :param request: The ICMP request to use to match the response.
704
705 :type timeout: int or float, optional
706 :param timeout: The maximum waiting time for receiving the
707 response in seconds. This parameter takes into account the
708 timestamp of the request. Default to 2.
709
710 :rtype: ICMPReply
711 :returns: An `ICMPReply` object representing the response of
712 the desired destination or an upstream gateway. See the
713 `ICMPReply` class for details.
714
715 :raises TimeoutExceeded: If no response is received before the
716 timeout specified in parameters.
717
718 '''
719 buffer_id = request.id, request.sequence
720
721 while True:
722 with self._buffer_lock:
723 self._buffer_event.clear()
724
725 if buffer_id in self._buffer:
726 reply = self._buffer[buffer_id].pop(0)
727
728 if not self._buffer[buffer_id]:
729 del self._buffer[buffer_id]
730
731 return reply
732
733 remaining_time = request.time + timeout - time()
734
735 if not self._buffer_event.wait(remaining_time):
736 raise TimeoutExceeded(timeout)
737
738 def detach(self):
739 '''
740 Detach the socket from the wrapper and return it. The wrapper
741 cannot be used after this call but the socket can be reused for
742 other purposes.
743
744 '''
745 sock = self._sock
746
747 if self._sock:
748 self._sock = None
749 self._thread.join()
750
751 return sock
752
753 def close(self):
754 '''
755 Close this object and the underlying socket. Both cannot be
756 used after this call.
757
758 '''
759 if self._sock:
760 self.detach().close()
761
762 @property
763 def is_closed(self):
764 '''
765 Indicate whether the wrapper is still operational or not.
766 Return a `boolean`.
767
768 '''
769 return self._sock is None
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
3134 from .utils import PID, resolve, is_ipv6_address
3235
3336
34 def traceroute(address, count=3, interval=0.05, timeout=2, id=PID,
35 traffic_class=0, max_hops=30, fast_mode=False, **kwargs):
37 def traceroute(address, count=2, interval=0.05, timeout=2, id=PID,
38 first_hop=1, max_hops=30, source=None, fast=False, **kwargs):
3639 '''
3740 Determine the route to a destination host.
3841
3942 The Internet is a large and complex aggregation of network hardware,
4043 connected together by gateways. Tracking the route one's packets
41 follow can be difficult. This function utilizes the IP protocol
42 time to live field and attempts to elicit an ICMP TIME_EXCEEDED
43 response from each gateway along the path to some host.
44 follow can be difficult. This function uses the IP protocol time to
45 live field and attempts to elicit an ICMP Time Exceeded response
46 from each gateway along the path to some host.
47
48 This function requires root privileges to run.
4449
4550 :type address: str
46 :param address: The destination IP address.
47
48 :type count: int
49 :param count: (Optional) The number of ping to perform per hop.
50
51 :type interval: int or float
52 :param interval: (Optional) The interval in seconds between sending
53 each packet.
54
55 :type timeout: int or float
56 :param timeout: (Optional) The maximum waiting time for receiving
57 a reply in seconds.
58
59 :type id: int
60 :param id: (Optional) The identifier of the request. Used to match
61 the reply with the request. In practice, a unique identifier is
62 used for every ping process.
63
64 :type traffic_class: int
65 :param traffic_class: (Optional) The traffic class of packets.
51 :param address: The IP address, hostname or FQDN of the host to
52 reach. For deterministic behavior, prefer to use an IP address.
53
54 :type count: int, optional
55 :param count: The number of ping to perform per hop. Default to 2.
56
57 :type interval: int or float, optional
58 :param interval: The interval in seconds between sending each
59 packet. Default to 0.05.
60
61 :type timeout: int or float, optional
62 :param timeout: The maximum waiting time for receiving a reply in
63 seconds. Default to 2.
64
65 :type id: int, optional
66 :param id: The identifier of ICMP requests. Used to match the
67 responses with requests. In practice, a unique identifier
68 should be used for every traceroute process. By default, the
69 identifier corresponds to the PID.
70
71 :type first_hop: int, optional
72 :param first_hop: The initial time to live value used in outgoing
73 probe packets. Default to 1.
74
75 :type max_hops: int, optional
76 :param max_hops: The maximum time to live (max number of hops) used
77 in outgoing probe packets. Default to 30.
78
79 :type source: str, optional
80 :param source: The IP address from which you want to send packets.
81 By default, the interface is automatically chosen according to
82 the specified destination.
83
84 :type fast: bool, optional
85 :param fast: When this option is enabled and an intermediate router
86 has been reached, skip to the next hop rather than perform
87 additional requests. The `count` parameter then becomes the
88 maximum number of requests in the event of no response.
89 Default to False.
90
91 Advanced (**kwags):
92
93 :type payload: bytes, optional
94 :param payload: The payload content in bytes. A random payload is
95 used by default.
96
97 :type payload_size: int, optional
98 :param payload_size: The payload size. Ignored when the `payload`
99 parameter is set. Default to 56.
100
101 :type traffic_class: int, optional
102 :param traffic_class: The traffic class of ICMP packets.
66103 Provides a defined level of service to packets by setting the
67104 DS Field (formerly TOS) or the Traffic Class field of IP
68105 headers. Packets are delivered with the minimum priority by
70107 Intermediate routers must be able to support this feature.
71108 Only available on Unix systems. Ignored on Windows.
72109
73 :type max_hops: int
74 :param max_hops: (Optional) The maximum time to live (max number of
75 hops) used in outgoing probe packets.
76
77 :type fast_mode: bool
78 :param fast_mode: (Optional) When this option is enabled and an
79 intermediate router has been reached, skip to the next hop
80 rather than perform additional requests. The `count` parameter
81 then becomes the maximum number of requests in case of no
82 responses.
83
84 :param **kwargs: (Optional) Advanced use: arguments passed to
85 `ICMPRequest` objects.
86
87110 :rtype: list of Hop
88111 :returns: A list of `Hop` objects representing the route to the
89 desired host. The list is sorted in ascending order according
90 to the distance (in terms of hops) that separates the remote
91 host from the current machine.
92
93 :raises SocketPermissionError: If the permissions are insufficient
94 to create a socket.
112 desired destination. The list is sorted in ascending order
113 according to the distance, in terms of hops, that separates the
114 remote host from the current machine. Gateways that do not
115 respond to requests are not added to this list.
116
117 :raises NameLookupError: If you pass a hostname or FQDN in
118 parameters and it does not exist or cannot be resolved.
119 :raises SocketPermissionError: If the privileges are insufficient
120 to create the socket.
121 :raises SocketAddressError: If the source address cannot be
122 assigned to the socket.
123 :raises ICMPSocketError: If another error occurs. See the
124 `ICMPv4Socket` or `ICMPv6Socket` class for details.
95125
96126 Usage::
97127
101131
102132 >>> for hop in hops:
103133 ... if last_distance + 1 != hop.distance:
104 ... print('Some routers are not responding')
134 ... print('Some gateways are not responding')
105135 ...
106136 ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms')
107137 ...
108138 ... last_distance = hop.distance
109139 ...
110 1 10.0.0.1 5.196 ms
111 2 194.149.169.49 7.552 ms
112 3 194.149.166.54 12.21 ms
113 * Some routers are not responding
114 5 212.73.205.22 22.15 ms
115 6 1.1.1.1 13.59 ms
140 1 10.0.0.1 5.196 ms
141 2 194.149.169.49 7.552 ms
142 3 194.149.166.54 12.21 ms
143 * Some gateways are not responding
144 5 212.73.205.22 22.15 ms
145 6 1.1.1.1 13.59 ms
116146
117147 See the `Hop` class for details.
118148
120150 address = resolve(address)
121151
122152 if is_ipv6_address(address):
123 socket = ICMPv6Socket()
153 sock = ICMPv6Socket(source)
124154
125155 else:
126 socket = ICMPv4Socket()
127
128 ttl = 1
156 sock = ICMPv4Socket(source)
157
158 ttl = first_hop
129159 host_reached = False
130160 hops = []
131161
143173 destination=address,
144174 id=id,
145175 sequence=sequence,
146 timeout=timeout,
147176 ttl=ttl,
148 traffic_class=traffic_class,
149177 **kwargs)
150178
151179 try:
152 socket.send(request)
180 sock.send(request)
153181 packets_sent += 1
154182
155 reply = socket.receive()
183 reply = sock.receive(request, timeout)
156184 reply.raise_for_status()
157185 host_reached = True
158186
170198 min_rtt = min(round_trip_time, min_rtt)
171199 max_rtt = max(round_trip_time, max_rtt)
172200
173 if fast_mode:
201 if fast:
174202 break
175203
176204 if packets_received:
189217
190218 ttl += 1
191219
192 socket.close()
220 sock.close()
193221
194222 return hops
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58
2427 '''
2528
2629 import socket
30
2731 from sys import platform
2832 from os import getpid
2933 from re import match
3034 from random import choices
3135
36 from .exceptions import NameLookupError
37
3238
3339 PID = getpid()
40 PLATFORM_LINUX = platform == 'linux'
41 PLATFORM_MACOS = platform == 'darwin'
3442 PLATFORM_WINDOWS = platform == 'win32'
3543
3644
3947 Generate a random byte sequence of the specified size.
4048
4149 '''
42 bytes_available = (
50 sequence = choices(
4351 b'abcdefghijklmnopqrstuvwxyz'
4452 b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
45 b'1234567890'
46 )
53 b'1234567890', k=size)
4754
48 return bytes(
49 choices(bytes_available, k=size)
50 )
55 return bytes(sequence)
5156
5257
5358 def resolve(name):
5863 This function searches for IPv4 addresses first for compatibility
5964 reasons before searching for IPv6 addresses.
6065
61 If no address is found, the name specified in parameter is
62 returned so as not to impact the operation of other functions. This
63 behavior may change in future versions of icmplib.
66 If you pass an IP address, no lookup is done. The same address is
67 returned.
68
69 Raises a `NameLookupError` exception if the requested name does
70 not exist or cannot be resolved.
6471
6572 '''
6673 if is_ipv4_address(name) or is_ipv6_address(name):
8693 )[0][4][0]
8794
8895 except OSError:
89 return name
96 raise NameLookupError(name)
9097
9198
9299 def is_ipv4_address(address):
95102 Return a `boolean`.
96103
97104 '''
98 return match(
99 r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$',
100 address
101 ) is not None
105 pattern = r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$'
106 return match(pattern, address) is not None
102107
103108
104109 def is_ipv6_address(address):
Binary diff not shown
00 [metadata]
11 name = icmplib
2 version = 1.2.2
2 version = 2.0
33 description = Easily forge ICMP packets and make your own ping and traceroute.
44 keywords = pure, implementation, icmp, sockets, ping, multiping, traceroute, ipv4, ipv6, python3
55
00 '''
11 icmplib
22 ~~~~~~~
3
4 A powerful library for forging ICMP packets and performing ping
5 and traceroute.
36
47 https://github.com/ValentinBELYN/icmplib
58