Import upstream version 2.1.1+git20210515.1.6d8ca5c
Kali Janitor
3 years ago
0 | # Changelog | |
1 | ||
2 | All notable changes to this project will be documented in this file. | |
3 | ||
4 | ## [v1.2.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.2) - 2020-10-10 | |
5 | - Add support for hostnames and FQDN resolution to IPv6 addresses. | |
6 | - Performance improvement. | |
7 | ||
8 | ## [v1.2.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.1) - 2020-09-26 | |
9 | - Fix an issue in the `traceroute` function which gave the wrong value for the `avg_rtt` property. | |
10 | - Some other tweaks to the `traceroute` function. | |
11 | ||
12 | ## [v1.2.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.2.0) - 2020-09-12 | |
13 | - Add the ability to modify the traffic class of ICMP packets. | |
14 | - Add new optional parameters to the `traceroute` function. | |
15 | - Add a new exception `SocketUnavailableError` when an action is performed while a socket is closed. | |
16 | - Add a warning message on deprecated properties. | |
17 | - Explicit closure of sockets on built-in functions. | |
18 | - Fix a bug when ICMP responses are not correctly formatted (part 2). | |
19 | ||
20 | ## [v1.1.3](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.3) - 2020-09-03 | |
21 | - Fix a bug when ICMP responses are not correctly formatted. | |
22 | ||
23 | ## [v1.1.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.2) - 2020-08-29 | |
24 | - Fix a compatibility issue. | |
25 | ||
26 | ## [v1.1.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.1) - 2020-07-10 | |
27 | - Fix a bug when the source host does not have an IP address. | |
28 | ||
29 | ## [v1.1.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.1.0) - 2020-06-25 | |
30 | - Normalize the names of variables and properties: | |
31 | - `ICMPReply` class: the `received_bytes` property is deprecated. Use `bytes_received` instead. | |
32 | - `Host` and `Hop` classes: the `transmitted_packets` property is deprecated. Use `packets_sent` instead. | |
33 | - `Host` and `Hop` classes: the `received_packets` property is deprecated. Use `packets_received` instead. | |
34 | - Normalize docstrings. | |
35 | - Add support for odd size payloads. | |
36 | - Optimizations. | |
37 | ||
38 | ## [v1.0.4](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.4) - 2020-06-14 | |
39 | - Add the `is_closed` property to the `ICMPSocket` class. | |
40 | - Round round-trip time values by default. | |
41 | - Fix a bug in the `multiping` function: the `id` parameter was ignored. | |
42 | - Fix a bug in the `ICMPSocket` class when instantiated without root privileges. | |
43 | - Add an index for examples. | |
44 | ||
45 | ## [v1.0.3](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.3) - 2020-05-09 | |
46 | - Add the ability to customize the payload. | |
47 | - Improvements of `ping` and `multiping` functions. It is now possible to pass arguments to the `ICMPRequest` object using keywords arguments `**kwargs`. | |
48 | - Update some docstrings. | |
49 | - Add new examples. | |
50 | ||
51 | ## [v1.0.2](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.2) - 2019-10-20 | |
52 | - Change the license. This project now uses the more permissive license LGPLv3. | |
53 | ||
54 | ## [v1.0.1](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.1) - 2019-10-07 | |
55 | - Add some examples. | |
56 | - Rename `model.py` to `models.py`. | |
57 | - Update setup keywords. | |
58 | ||
59 | ## [v1.0.0](https://github.com/ValentinBELYN/icmplib/releases/tag/v1.0.0) - 2019-09-29 | |
60 | - :tada: Initial release. |
0 | Metadata-Version: 2.1 | |
1 | Name: icmplib | |
2 | Version: 3.0 | |
3 | Summary: Easily forge ICMP packets and make your own ping and traceroute. | |
4 | Home-page: https://github.com/ValentinBELYN/icmplib | |
5 | Author: Valentin BELYN | |
6 | Author-email: [email protected] | |
7 | License: GNU Lesser General Public License v3.0 | |
8 | Description: <div align="center"> | |
9 | <br> | |
10 | <img src="media/icmplib-logo-2.0.png" height="190" width="100" alt="icmplib"> | |
11 | <br> | |
12 | <br> | |
13 | ||
14 | <br> | |
15 | <div> | |
16 | <a href="#features">Features</a> | |
17 | <a href="#installation">Installation</a> | |
18 | <a href="#built-in-functions">Built-in functions</a> | |
19 | <a href="#icmp-sockets">ICMP sockets</a> | |
20 | <a href="#faq">FAQ</a> | |
21 | <a href="#contributing">Contributing</a> | |
22 | <a href="#donate">Donate</a> | |
23 | <a href="#license">License</a> | |
24 | </div> | |
25 | <br> | |
26 | ||
27 | <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python. | |
28 | Use the built-in functions or build your own, you have the choice! | |
29 | ||
30 | <a href="#how-to-use-the-library-without-root-privileges">- You can now use this library without root privileges -</a></pre> | |
31 | <a href="https://pypi.org/project/icmplib"> | |
32 | <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics"> | |
33 | </a> | |
34 | </div> | |
35 | ||
36 | <br> | |
37 | ||
38 | ## Features | |
39 | ||
40 | - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`. | |
41 | - :gem: **Modern:** This library uses the latest technologies offered by Python 3.6+ and is fully object-oriented. | |
42 | - :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! | |
43 | - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload and more! | |
44 | - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets. | |
45 | - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance. | |
46 | - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it). | |
47 | - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows. | |
48 | - :metal: **No dependency:** icmplib is a pure Python implementation of the ICMP protocol. It does not use any external dependencies. | |
49 | ||
50 | <br> | |
51 | ||
52 | ## Installation | |
53 | ||
54 | The recommended way to install or upgrade icmplib is to use `pip3`: | |
55 | ||
56 | ```shell | |
57 | $ pip3 install icmplib | |
58 | $ pip3 install --upgrade icmplib | |
59 | ``` | |
60 | ||
61 | *icmplib requires Python 3.6 or later.* | |
62 | ||
63 | To import icmplib into your project (only import what you need): | |
64 | ||
65 | ```python | |
66 | # For simple use | |
67 | from icmplib import ping, multiping, traceroute, resolve, Host, Hop | |
68 | ||
69 | # For advanced use (sockets) | |
70 | from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply | |
71 | ||
72 | # Exceptions | |
73 | from icmplib import ICMPLibError, NameLookupError, ICMPSocketError | |
74 | from icmplib import SocketAddressError, SocketPermissionError | |
75 | from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded | |
76 | from icmplib import ICMPError, DestinationUnreachable, TimeExceeded | |
77 | ``` | |
78 | ||
79 | <br> | |
80 | ||
81 | ## Built-in functions | |
82 | ||
83 | ### Ping | |
84 | Send ICMP Echo Request packets to a network host. | |
85 | ||
86 | ```python | |
87 | ping(address, count=4, interval=1, timeout=2, id=PID, source=None, privileged=True, **kwargs) | |
88 | ``` | |
89 | ||
90 | #### Parameters | |
91 | - `address` | |
92 | ||
93 | The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address. | |
94 | ||
95 | - Type: `str` | |
96 | ||
97 | - `count` | |
98 | ||
99 | The number of ping to perform. | |
100 | ||
101 | - Type: `int` | |
102 | - Default: `4` | |
103 | ||
104 | - `interval` | |
105 | ||
106 | The interval in seconds between sending each packet. | |
107 | ||
108 | - Type: `int` or `float` | |
109 | - Default: `1` | |
110 | ||
111 | - `timeout` | |
112 | ||
113 | The maximum waiting time for receiving a reply in seconds. | |
114 | ||
115 | - Type: `int` or `float` | |
116 | - Default: `2` | |
117 | ||
118 | - `id` | |
119 | ||
120 | 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. | |
121 | ||
122 | - Type: `int` | |
123 | - Default: `PID` | |
124 | ||
125 | - `source` | |
126 | ||
127 | The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. | |
128 | ||
129 | - Type: `str` | |
130 | - Default: `None` | |
131 | ||
132 | - `privileged` | |
133 | ||
134 | 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. | |
135 | ||
136 | *Only available on Unix systems. Ignored on Windows.* | |
137 | ||
138 | - Type: `bool` | |
139 | - Default: `True` | |
140 | ||
141 | - `payload` | |
142 | ||
143 | The payload content in bytes. A random payload is used by default. | |
144 | ||
145 | - Type: `bytes` | |
146 | - Default: `None` | |
147 | ||
148 | - `payload_size` | |
149 | ||
150 | The payload size. Ignored when the `payload` parameter is set. | |
151 | ||
152 | - Type: `int` | |
153 | - Default: `56` | |
154 | ||
155 | - `traffic_class` | |
156 | ||
157 | 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. | |
158 | ||
159 | *Only available on Unix systems. Ignored on Windows.* | |
160 | ||
161 | - Type: `int` | |
162 | - Default: `0` | |
163 | ||
164 | #### Return value | |
165 | - A `Host` object containing statistics about the desired destination:<br> | |
166 | `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive` | |
167 | ||
168 | #### Exceptions | |
169 | - `NameLookupError` | |
170 | ||
171 | If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved. | |
172 | ||
173 | - `SocketPermissionError` | |
174 | ||
175 | If the privileges are insufficient to create the socket. | |
176 | ||
177 | - `SocketAddressError` | |
178 | ||
179 | If the source address cannot be assigned to the socket. | |
180 | ||
181 | - `ICMPSocketError` | |
182 | ||
183 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
184 | ||
185 | #### Example | |
186 | ```python | |
187 | >>> host = ping('1.1.1.1', count=10, interval=0.2) | |
188 | ||
189 | >>> host.address # The IP address of the host that responded | |
190 | '1.1.1.1' # to the request | |
191 | ||
192 | >>> host.min_rtt # The minimum round-trip time | |
193 | 12.2 | |
194 | ||
195 | >>> host.avg_rtt # The average round-trip time | |
196 | 13.2 | |
197 | ||
198 | >>> host.max_rtt # The maximum round-trip time | |
199 | 17.6 | |
200 | ||
201 | >>> host.packets_sent # The number of packets transmitted to the | |
202 | 10 # destination host | |
203 | ||
204 | >>> host.packets_received # The number of packets sent by the remote | |
205 | 9 # host and received by the current host | |
206 | ||
207 | >>> host.packet_loss # Packet loss occurs when packets fail to | |
208 | 0.1 # reach their destination. Returns a float | |
209 | # between 0 and 1 (all packets are lost) | |
210 | >>> host.is_alive # Indicates whether the host is reachable | |
211 | True | |
212 | ``` | |
213 | ||
214 | <br> | |
215 | ||
216 | ### Multiping | |
217 | Send ICMP Echo Request packets to several network hosts. | |
218 | ||
219 | 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. | |
220 | ||
221 | ```python | |
222 | multiping(addresses, count=2, interval=0.01, timeout=2, id=PID, source=None, privileged=True, **kwargs) | |
223 | ``` | |
224 | ||
225 | #### Parameters | |
226 | - `addresses` | |
227 | ||
228 | 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. | |
229 | ||
230 | - Type: `list of str` | |
231 | ||
232 | - `count` | |
233 | ||
234 | The number of ping to perform per address. | |
235 | ||
236 | - Type: `int` | |
237 | - Default: `2` | |
238 | ||
239 | - `interval` | |
240 | ||
241 | The interval in seconds between sending each packet. | |
242 | ||
243 | - Type: `int` or `float` | |
244 | - Default: `0.01` | |
245 | ||
246 | - `timeout` | |
247 | ||
248 | The maximum waiting time for receiving all responses in seconds. | |
249 | ||
250 | - Type: `int` or `float` | |
251 | - Default: `2` | |
252 | ||
253 | - `id` | |
254 | ||
255 | 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. | |
256 | ||
257 | - Type: `int` | |
258 | - Default: `PID` | |
259 | ||
260 | - `source` | |
261 | ||
262 | 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. | |
263 | ||
264 | - Type: `str` | |
265 | - Default: `None` | |
266 | ||
267 | - `privileged` | |
268 | ||
269 | 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. | |
270 | ||
271 | *Only available on Unix systems. Ignored on Windows.* | |
272 | ||
273 | - Type: `bool` | |
274 | - Default: `True` | |
275 | ||
276 | - `payload` | |
277 | ||
278 | The payload content in bytes. A random payload is used by default. | |
279 | ||
280 | - Type: `bytes` | |
281 | - Default: `None` | |
282 | ||
283 | - `payload_size` | |
284 | ||
285 | The payload size. Ignored when the `payload` parameter is set. | |
286 | ||
287 | - Type: `int` | |
288 | - Default: `56` | |
289 | ||
290 | - `traffic_class` | |
291 | ||
292 | 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. | |
293 | ||
294 | *Only available on Unix systems. Ignored on Windows.* | |
295 | ||
296 | - Type: `int` | |
297 | - Default: `0` | |
298 | ||
299 | #### Return value | |
300 | - A `list of Host` objects containing statistics about the desired destinations:<br> | |
301 | `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive` | |
302 | ||
303 | The list is sorted in the same order as the addresses passed in parameters. | |
304 | ||
305 | #### Exceptions | |
306 | - `SocketPermissionError` | |
307 | ||
308 | If the privileges are insufficient to create the socket. | |
309 | ||
310 | - `SocketAddressError` | |
311 | ||
312 | If the source address cannot be assigned to the socket. | |
313 | ||
314 | - `ICMPSocketError` | |
315 | ||
316 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
317 | ||
318 | #### Example | |
319 | ```python | |
320 | >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1']) | |
321 | ||
322 | >>> for host in hosts: | |
323 | ... if host.is_alive: | |
324 | ... # See the Host class for details | |
325 | ... print(f'{host.address} is alive!') | |
326 | ... | |
327 | ... else: | |
328 | ... print(f'{host.address} is dead!') | |
329 | ... | |
330 | ||
331 | # 10.0.0.5 is dead! | |
332 | # 127.0.0.1 is alive! | |
333 | # ::1 is alive! | |
334 | ``` | |
335 | ||
336 | <br> | |
337 | ||
338 | ### Traceroute | |
339 | Determine the route to a destination host. | |
340 | ||
341 | 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. | |
342 | ||
343 | *This function requires root privileges to run.* | |
344 | ||
345 | ```python | |
346 | traceroute(address, count=2, interval=0.05, timeout=2, id=PID, first_hop=1, max_hops=30, source=None, fast=False, **kwargs) | |
347 | ``` | |
348 | ||
349 | #### Parameters | |
350 | - `address` | |
351 | ||
352 | The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address. | |
353 | ||
354 | - Type: `str` | |
355 | ||
356 | - `count` | |
357 | ||
358 | The number of ping to perform per hop. | |
359 | ||
360 | - Type: `int` | |
361 | - Default: `2` | |
362 | ||
363 | - `interval` | |
364 | ||
365 | The interval in seconds between sending each packet. | |
366 | ||
367 | - Type: `int` or `float` | |
368 | - Default: `0.05` | |
369 | ||
370 | - `timeout` | |
371 | ||
372 | The maximum waiting time for receiving a reply in seconds. | |
373 | ||
374 | - Type: `int` or `float` | |
375 | - Default: `2` | |
376 | ||
377 | - `id` | |
378 | ||
379 | The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process. | |
380 | ||
381 | - Type: `int` | |
382 | - Default: `PID` | |
383 | ||
384 | - `first_hop` | |
385 | ||
386 | The initial time to live value used in outgoing probe packets. | |
387 | ||
388 | - Type: `int` | |
389 | - Default: `1` | |
390 | ||
391 | - `max_hops` | |
392 | ||
393 | The maximum time to live (max number of hops) used in outgoing probe packets. | |
394 | ||
395 | - Type: `int` | |
396 | - Default: `30` | |
397 | ||
398 | - `source` | |
399 | ||
400 | The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. | |
401 | ||
402 | - Type: `str` | |
403 | - Default: `None` | |
404 | ||
405 | - `fast` | |
406 | ||
407 | 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. | |
408 | ||
409 | - Type: `bool` | |
410 | - Default: `False` | |
411 | ||
412 | - `payload` | |
413 | ||
414 | The payload content in bytes. A random payload is used by default. | |
415 | ||
416 | - Type: `bytes` | |
417 | - Default: `None` | |
418 | ||
419 | - `payload_size` | |
420 | ||
421 | The payload size. Ignored when the `payload` parameter is set. | |
422 | ||
423 | - Type: `int` | |
424 | - Default: `56` | |
425 | ||
426 | - `traffic_class` | |
427 | ||
428 | 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. | |
429 | ||
430 | *Only available on Unix systems. Ignored on Windows.* | |
431 | ||
432 | - Type: `int` | |
433 | - Default: `0` | |
434 | ||
435 | #### Return value | |
436 | - 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`. | |
437 | ||
438 | 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. | |
439 | ||
440 | #### Exceptions | |
441 | - `NameLookupError` | |
442 | ||
443 | If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved. | |
444 | ||
445 | - `SocketPermissionError` | |
446 | ||
447 | If the privileges are insufficient to create the socket. | |
448 | ||
449 | - `SocketAddressError` | |
450 | ||
451 | If the source address cannot be assigned to the socket. | |
452 | ||
453 | - `ICMPSocketError` | |
454 | ||
455 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
456 | ||
457 | #### Example | |
458 | ```python | |
459 | >>> hops = traceroute('1.1.1.1') | |
460 | ||
461 | >>> print('Distance/TTL Address Average round-trip time') | |
462 | >>> last_distance = 0 | |
463 | ||
464 | >>> for hop in hops: | |
465 | ... if last_distance + 1 != hop.distance: | |
466 | ... print('Some gateways are not responding') | |
467 | ... | |
468 | ... # See the Hop class for details | |
469 | ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms') | |
470 | ... | |
471 | ... last_distance = hop.distance | |
472 | ... | |
473 | ||
474 | # Distance/TTL Address Average round-trip time | |
475 | # 1 10.0.0.1 5.196 ms | |
476 | # 2 194.149.169.49 7.552 ms | |
477 | # 3 194.149.166.54 12.21 ms | |
478 | # * Some gateways are not responding | |
479 | # 5 212.73.205.22 22.15 ms | |
480 | # 6 1.1.1.1 13.59 ms | |
481 | ``` | |
482 | ||
483 | <br> | |
484 | ||
485 | ## ICMP sockets | |
486 | ||
487 | 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. | |
488 | ||
489 | ``` | |
490 | ┌──────────────────┐ | |
491 | ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐ | |
492 | │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │ | |
493 | └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘ | |
494 | └──────────────────┘ | |
495 | ``` | |
496 | ||
497 | ### ICMPRequest | |
498 | A user-created object that represents an ICMP Echo Request. | |
499 | ||
500 | ```python | |
501 | ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0) | |
502 | ``` | |
503 | ||
504 | #### Parameters and properties | |
505 | - `destination` | |
506 | ||
507 | The IP address of the host to which the message should be sent. | |
508 | ||
509 | - Type: `str` | |
510 | ||
511 | - `id` | |
512 | ||
513 | 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. | |
514 | ||
515 | - Type: `int` | |
516 | ||
517 | - `sequence` | |
518 | ||
519 | The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process. | |
520 | ||
521 | - Type: `int` | |
522 | ||
523 | - `payload` | |
524 | ||
525 | The payload content in bytes. A random payload is used by default. | |
526 | ||
527 | - Type: `bytes` | |
528 | - Default: `None` | |
529 | ||
530 | - `payload_size` | |
531 | ||
532 | The payload size. Ignored when the `payload` parameter is set. | |
533 | ||
534 | - Type: `int` | |
535 | - Default: `56` | |
536 | ||
537 | - `ttl` | |
538 | ||
539 | The time to live of the packet in terms of hops. | |
540 | ||
541 | - Type: `int` | |
542 | - Default: `64` | |
543 | ||
544 | - `traffic_class` | |
545 | ||
546 | 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. | |
547 | ||
548 | *Only available on Unix systems. Ignored on Windows.* | |
549 | ||
550 | - Type: `int` | |
551 | - Default: `0` | |
552 | ||
553 | #### Properties only | |
554 | - `time` | |
555 | ||
556 | 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. | |
557 | ||
558 | - Type: `float` | |
559 | ||
560 | <br> | |
561 | ||
562 | ### ICMPReply | |
563 | A class that represents an ICMP reply. Generated from an ICMP socket (`ICMPv4Socket` or `ICMPv6Socket`). | |
564 | ||
565 | ```python | |
566 | ICMPReply(source, id, sequence, type, code, bytes_received, time) | |
567 | ``` | |
568 | ||
569 | #### Parameters and properties | |
570 | - `source` | |
571 | ||
572 | The IP address of the gateway or host that composes the ICMP message. | |
573 | ||
574 | - Type: `str` | |
575 | ||
576 | - `id` | |
577 | ||
578 | The identifier of the reply. Used to match the reply with the request. | |
579 | ||
580 | - Type: `int` | |
581 | ||
582 | - `sequence` | |
583 | ||
584 | The sequence number. Used to match the reply with the request. | |
585 | ||
586 | - Type: `int` | |
587 | ||
588 | - `type` | |
589 | ||
590 | The type of ICMP message. | |
591 | ||
592 | - Type: `int` | |
593 | ||
594 | - `code` | |
595 | ||
596 | The ICMP error code. | |
597 | ||
598 | - Type: `int` | |
599 | ||
600 | - `bytes_received` | |
601 | ||
602 | The number of bytes received. | |
603 | ||
604 | - Type: `int` | |
605 | ||
606 | - `time` | |
607 | ||
608 | The timestamp of the ICMP reply. | |
609 | ||
610 | - Type: `float` | |
611 | ||
612 | #### Methods | |
613 | - `raise_for_status()` | |
614 | ||
615 | Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing. | |
616 | ||
617 | - Raises `DestinationUnreachable`: If the destination is unreachable for some reason. | |
618 | - Raises `TimeExceeded`: If the time to live field of the ICMP request has reached zero. | |
619 | - Raises `ICMPError`: Raised for any other type and ICMP error code, except ICMP Echo Reply messages. | |
620 | ||
621 | <br> | |
622 | ||
623 | ### ICMPv4Socket | |
624 | Class for sending and receiving ICMPv4 packets. | |
625 | ||
626 | ```python | |
627 | ICMPv4Socket(address=None, privileged=True) | |
628 | ``` | |
629 | ||
630 | #### Parameters | |
631 | - `source` | |
632 | ||
633 | The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces. | |
634 | ||
635 | - Type: `str` | |
636 | - Default: `None` | |
637 | ||
638 | - `privileged` | |
639 | ||
640 | 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. | |
641 | ||
642 | *Only available on Unix systems. Ignored on Windows.* | |
643 | ||
644 | - Type: `bool` | |
645 | - Default: `True` | |
646 | ||
647 | #### Methods | |
648 | - `__init__(address=None, privileged=True)` | |
649 | ||
650 | *Constructor. Automatically called: do not call it directly.* | |
651 | ||
652 | - Raises `SocketPermissionError`: If the privileges are insufficient to create the socket. | |
653 | - Raises `SocketAddressError`: If the requested address cannot be assigned to the socket. | |
654 | - Raises `ICMPSocketError`: If another error occurs while creating the socket. | |
655 | ||
656 | - `__del__()` | |
657 | ||
658 | *Destructor. Automatically called: do not call it directly.* | |
659 | ||
660 | Call the `close` method. | |
661 | ||
662 | - `send(request)` | |
663 | ||
664 | Send an ICMP request message over the network to a remote host.<br> | |
665 | This operation is non-blocking. Use the `receive` method to get the reply. | |
666 | ||
667 | - 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. | |
668 | - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only). | |
669 | - Raises `SocketUnavailableError`: If the socket is closed. | |
670 | - Raises `ICMPSocketError`: If another error occurs while sending. | |
671 | ||
672 | - `receive(request=None, timeout=2)` | |
673 | ||
674 | Receive an ICMP reply message from the socket.<br> | |
675 | This method can be called multiple times if you expect several responses as with a broadcast address. | |
676 | ||
677 | - Parameter `request` *(ICMPRequest)*: The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned. | |
678 | - Parameter `timeout` *(int or float)*: The maximum waiting time for receiving the response in seconds. Default to `2`. | |
679 | - Raises `TimeoutExceeded`: If no response is received before the timeout specified in parameters. | |
680 | - Raises `SocketUnavailableError`: If the socket is closed. | |
681 | - Raises `ICMPSocketError`: If another error occurs while receiving. | |
682 | ||
683 | Returns an `ICMPReply` object representing the response of the desired destination or an upstream gateway. | |
684 | ||
685 | - `close()` | |
686 | ||
687 | Close the socket. It cannot be used after this call. | |
688 | ||
689 | #### Properties | |
690 | - `address` | |
691 | ||
692 | The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces. | |
693 | ||
694 | - Type: `str` | |
695 | ||
696 | - `is_privileged` | |
697 | ||
698 | Indicate whether the socket is running in privileged mode. | |
699 | ||
700 | - Type: `bool` | |
701 | ||
702 | - `is_closed` | |
703 | ||
704 | Indicate whether the socket is closed. | |
705 | ||
706 | - Type: `bool` | |
707 | ||
708 | #### Properties and setters | |
709 | - `broadcast` | |
710 | ||
711 | Enable or disable the broadcast support on the socket. | |
712 | ||
713 | - Type: `bool` | |
714 | - Default: `False` | |
715 | ||
716 | <br> | |
717 | ||
718 | ### ICMPv6Socket | |
719 | Class for sending and receiving ICMPv6 packets. | |
720 | ||
721 | ```python | |
722 | ICMPv6Socket(address=None, privileged=True) | |
723 | ``` | |
724 | ||
725 | #### Methods and properties | |
726 | The same methods and properties as for the `ICMPv4Socket` class, except the `broadcast` property. | |
727 | ||
728 | <br> | |
729 | ||
730 | ### Exceptions | |
731 | The library contains many exceptions to adapt to your needs: | |
732 | ||
733 | ``` | |
734 | ICMPLibError | |
735 | ├─ NameLookupError | |
736 | ├─ ICMPSocketError | |
737 | │ ├─ SocketAddressError | |
738 | │ ├─ SocketPermissionError | |
739 | │ ├─ SocketUnavailableError | |
740 | │ ├─ SocketBroadcastError | |
741 | │ └─ TimeoutExceeded | |
742 | │ | |
743 | └─ ICMPError | |
744 | ├─ DestinationUnreachable | |
745 | │ ├─ ICMPv4DestinationUnreachable | |
746 | │ └─ ICMPv6DestinationUnreachable | |
747 | │ | |
748 | └─ TimeExceeded | |
749 | ├─ ICMPv4TimeExceeded | |
750 | └─ ICMPv6TimeExceeded | |
751 | ``` | |
752 | ||
753 | - `ICMPLibError`: Exception class for the icmplib package. | |
754 | - `NameLookupError`: Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames. | |
755 | - `ICMPSocketError`: Base class for ICMP sockets exceptions. | |
756 | - `SocketAddressError`: Raised when the requested address cannot be assigned to the socket. | |
757 | - `SocketPermissionError`: Raised when the privileges are insufficient to create the socket. | |
758 | - `SocketUnavailableError`: Raised when an action is performed while the socket is closed. | |
759 | - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket. | |
760 | - `TimeoutExceeded`: Raised when a timeout occurs on a socket. | |
761 | - `ICMPError`: Base class for ICMP error messages. | |
762 | - `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. | |
763 | - `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. | |
764 | ||
765 | Use the `message` property to get the error message. `ICMPError` subclasses have a `reply` property to retrieve the response. | |
766 | ||
767 | <br> | |
768 | ||
769 | ### Examples | |
770 | #### Sockets in action | |
771 | ```python | |
772 | def single_ping(address, timeout=2, id=PID): | |
773 | # Create an ICMP socket | |
774 | sock = ICMPv4Socket() | |
775 | ||
776 | # Create an ICMP request | |
777 | # See the 'ICMPRequest' class for details | |
778 | request = ICMPRequest( | |
779 | destination=address, | |
780 | id=id, | |
781 | sequence=1) | |
782 | ||
783 | try: | |
784 | sock.send(request) | |
785 | ||
786 | # If the program arrives in this section, it means that the | |
787 | # packet has been transmitted. | |
788 | ||
789 | reply = sock.receive(request, timeout) | |
790 | ||
791 | # If the program arrives in this section, it means that a | |
792 | # packet has been received. The reply has the same identifier | |
793 | # and sequence number that the request but it can come from | |
794 | # an intermediate gateway. | |
795 | ||
796 | reply.raise_for_status() | |
797 | ||
798 | # If the program arrives in this section, it means that the | |
799 | # destination host has responded to the request. | |
800 | ||
801 | except TimeoutExceeded as err: | |
802 | # The timeout has been reached | |
803 | print(err) | |
804 | ||
805 | except DestinationUnreachable as err: | |
806 | # The reply indicates that the destination host is unreachable | |
807 | print(err) | |
808 | ||
809 | # Retrieve the response | |
810 | reply = err.reply | |
811 | ||
812 | except TimeExceeded as err: | |
813 | # The reply indicates that the time to live exceeded in transit | |
814 | print(err) | |
815 | ||
816 | # Retrieve the response | |
817 | reply = err.reply | |
818 | ||
819 | except ICMPLibError as err: | |
820 | # All other errors | |
821 | print(err) | |
822 | ||
823 | # Automatic socket closure (garbage collector) | |
824 | ``` | |
825 | ||
826 | #### Verbose ping | |
827 | ```python | |
828 | def verbose_ping(address, count=4, interval=1, timeout=2, id=PID): | |
829 | # A payload of 56 bytes is used by default. You can modify it using | |
830 | # the 'payload_size' parameter of your ICMP request. | |
831 | print(f'PING {address}: 56 data bytes\n') | |
832 | ||
833 | # We detect the socket to use from the specified IP address | |
834 | if is_ipv6_address(address): | |
835 | sock = ICMPv6Socket() | |
836 | ||
837 | else: | |
838 | sock = ICMPv4Socket() | |
839 | ||
840 | for sequence in range(count): | |
841 | # We create an ICMP request | |
842 | request = ICMPRequest( | |
843 | destination=address, | |
844 | id=id, | |
845 | sequence=sequence) | |
846 | ||
847 | try: | |
848 | # We send the request | |
849 | sock.send(request) | |
850 | ||
851 | # We are awaiting receipt of an ICMP reply | |
852 | reply = sock.receive(request, timeout) | |
853 | ||
854 | # We received a reply | |
855 | # We display some information | |
856 | print(f'{reply.bytes_received} bytes from ' | |
857 | f'{reply.source}: ', end='') | |
858 | ||
859 | # We throw an exception if it is an ICMP error message | |
860 | reply.raise_for_status() | |
861 | ||
862 | # We calculate the round-trip time and we display it | |
863 | round_trip_time = (reply.time - request.time) * 1000 | |
864 | ||
865 | print(f'icmp_seq={sequence} ' | |
866 | f'time={round(round_trip_time, 3)} ms') | |
867 | ||
868 | # We pause before continuing | |
869 | if sequence < count - 1: | |
870 | sleep(interval) | |
871 | ||
872 | except TimeoutExceeded: | |
873 | # The timeout has been reached | |
874 | print(f'Request timeout for icmp_seq {sequence}') | |
875 | ||
876 | except ICMPError as err: | |
877 | # An ICMP error message has been received | |
878 | print(err) | |
879 | ||
880 | except ICMPLibError: | |
881 | # All other errors | |
882 | print('An error has occurred.') | |
883 | ||
884 | ||
885 | verbose_ping('1.1.1.1') | |
886 | ||
887 | # PING 1.1.1.1: 56 data bytes | |
888 | # 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms | |
889 | # 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms | |
890 | # 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms | |
891 | # 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms | |
892 | ``` | |
893 | ||
894 | <br> | |
895 | ||
896 | ## FAQ | |
897 | ||
898 | ### How to resolve a FQDN/domain name or a hostname? | |
899 | The use of the built-in `resolve` function is recommended: | |
900 | ||
901 | ```python | |
902 | >>> resolve('github.com') | |
903 | '140.82.118.4' | |
904 | ``` | |
905 | ||
906 | - If several IP addresses are available, only the first one is returned. This function searches for IPv4 addresses first before searching for IPv6 addresses. | |
907 | - If you pass an IP address, no lookup is done. The same address is returned. | |
908 | - Raises a `NameLookupError` exception if the requested name does not exist or cannot be resolved. | |
909 | ||
910 | ### How to use the library without root privileges? | |
911 | Since its version 2.0, icmplib can be used without root privileges. | |
912 | ||
913 | For this, you can set the `privileged` parameter to `False` on the `ping` and `multiping` functions, as well as the low level classes. By disabling this parameter, the kernel handles some parts of the ICMP headers. | |
914 | ||
915 | On some Linux systems, you must allow this feature: | |
916 | ||
917 | ```shell | |
918 | $ echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee -a /etc/sysctl.conf | |
919 | $ sudo sysctl -p | |
920 | ``` | |
921 | ||
922 | You can check the current value with the following command: | |
923 | ||
924 | ```shell | |
925 | $ sysctl net.ipv4.ping_group_range | |
926 | net.ipv4.ping_group_range = 0 2147483647 | |
927 | ``` | |
928 | ||
929 | *Since Ubuntu 20.04 LTS, this manipulation is no longer necessary.* | |
930 | ||
931 | [Read more on www.kernel.org](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt) | |
932 | ||
933 | ### Why I have no response from a remote host? | |
934 | In the event of no response from a remote host, several causes are possible: | |
935 | - 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. | |
936 | - The remote host or an upstream gateway is down. | |
937 | - The remote host or an upstream gateway drops ICMP messages for security reasons. | |
938 | - 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. | |
939 | ||
940 | <br> | |
941 | ||
942 | ## Contributing | |
943 | ||
944 | Comments and enhancements are welcome. | |
945 | ||
946 | All development is done on [GitHub](https://github.com/ValentinBELYN/icmplib). Use [Issues](https://github.com/ValentinBELYN/icmplib/issues) to report problems and submit feature requests. Please include a minimal example that reproduces the bug. | |
947 | ||
948 | ## Donate | |
949 | ||
950 | icmplib is completely free and open source. It has been fully developed on my free time. If you enjoy it, please consider donating to support the development. | |
951 | ||
952 | - [:tada: Donate via PayPal](https://paypal.me/ValentinBELYN) | |
953 | ||
954 | ## License | |
955 | ||
956 | Copyright 2017-2021 Valentin BELYN. | |
957 | ||
958 | Code released under the GNU LGPLv3 license. See the [LICENSE](LICENSE) for details. | |
959 | ||
960 | Keywords: pure,implementation,icmp,sockets,ping,multiping,traceroute,ipv4,ipv6,python3 | |
961 | Platform: UNKNOWN | |
962 | Classifier: Development Status :: 5 - Production/Stable | |
963 | Classifier: Intended Audience :: Developers | |
964 | Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) | |
965 | Classifier: Operating System :: OS Independent | |
966 | Classifier: Programming Language :: Python | |
967 | Classifier: Programming Language :: Python :: 3 | |
968 | Classifier: Programming Language :: Python :: 3 :: Only | |
969 | Classifier: Programming Language :: Python :: 3.7 | |
970 | Classifier: Programming Language :: Python :: 3.8 | |
971 | Classifier: Programming Language :: Python :: 3.9 | |
972 | Classifier: Programming Language :: Python :: 3.10 | |
973 | Classifier: Topic :: Software Development :: Libraries | |
974 | Classifier: Topic :: Software Development :: Libraries :: Python Modules | |
975 | Requires-Python: >=3.7 | |
976 | Description-Content-Type: text/markdown |
0 | 0 | <div align="center"> |
1 | 1 | <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"> | |
3 | 3 | <br> |
4 | 4 | <br> |
5 | 5 | |
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> | |
10 | 6 | <br> |
11 | <br> | |
12 | ||
13 | 7 | <div> |
14 | 8 | <a href="#features">Features</a> |
15 | 9 | <a href="#installation">Installation</a> |
21 | 15 | <a href="#license">License</a> |
22 | 16 | </div> |
23 | 17 | <br> |
24 | <br> | |
25 | 18 | |
26 | 19 | <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python. |
27 | 20 | Use the built-in functions or build your own, you have the choice! |
28 | 21 | |
29 | <strong>Root privileges are required to use this library (raw sockets).</strong></pre> | |
22 | <a href="#how-to-use-the-library-without-root-privileges">- You can now use this library without root privileges -</a></pre> | |
23 | <a href="https://pypi.org/project/icmplib"> | |
24 | <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics"> | |
25 | </a> | |
30 | 26 | </div> |
31 | 27 | |
32 | 28 | <br> |
35 | 31 | |
36 | 32 | - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`. |
37 | 33 | - :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. | |
34 | - :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! | |
35 | - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload and more! | |
36 | - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets. | |
40 | 37 | - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance. |
41 | 38 | - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it). |
42 | 39 | - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows. |
46 | 43 | |
47 | 44 | ## Installation |
48 | 45 | |
49 | Install, upgrade and uninstall icmplib with these commands: | |
46 | The recommended way to install or upgrade icmplib is to use `pip3`: | |
50 | 47 | |
51 | 48 | ```shell |
52 | 49 | $ pip3 install icmplib |
53 | 50 | $ 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): | |
51 | ``` | |
52 | ||
53 | *icmplib requires Python 3.6 or later.* | |
54 | ||
55 | To import icmplib into your project (only import what you need): | |
60 | 56 | |
61 | 57 | ```python |
62 | 58 | # For simple use |
63 | from icmplib import ping, multiping, traceroute, Host, Hop | |
59 | from icmplib import ping, multiping, traceroute, resolve, Host, Hop | |
64 | 60 | |
65 | 61 | # For advanced use (sockets) |
66 | 62 | from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply |
67 | 63 | |
68 | 64 | # Exceptions |
69 | from icmplib import ICMPLibError, ICMPSocketError, SocketPermissionError | |
65 | from icmplib import ICMPLibError, NameLookupError, ICMPSocketError | |
66 | from icmplib import SocketAddressError, SocketPermissionError | |
70 | 67 | from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded |
71 | 68 | from icmplib import ICMPError, DestinationUnreachable, TimeExceeded |
72 | 69 | ``` |
76 | 73 | ## Built-in functions |
77 | 74 | |
78 | 75 | ### 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) | |
76 | Send ICMP Echo Request packets to a network host. | |
77 | ||
78 | ```python | |
79 | ping(address, count=4, interval=1, timeout=2, id=PID, source=None, privileged=True, **kwargs) | |
84 | 80 | ``` |
85 | 81 | |
86 | 82 | #### Parameters |
87 | 83 | - `address` |
88 | 84 | |
89 | The IP address of the gateway or host to which the message should be sent. | |
85 | The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address. | |
90 | 86 | |
91 | 87 | - Type: `str` |
92 | 88 | |
113 | 109 | |
114 | 110 | - `id` |
115 | 111 | |
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. | |
112 | 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. | |
118 | 113 | |
119 | 114 | - Type: `int` |
120 | 115 | - Default: `PID` |
121 | 116 | |
122 | - `**kwargs` | |
123 | ||
124 | `Optional` Advanced use: arguments passed to `ICMPRequest` objects. | |
117 | - `source` | |
118 | ||
119 | The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. | |
120 | ||
121 | - Type: `str` | |
122 | - Default: `None` | |
123 | ||
124 | - `privileged` | |
125 | ||
126 | 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. | |
127 | ||
128 | *Only available on Unix systems. Ignored on Windows.* | |
129 | ||
130 | - Type: `bool` | |
131 | - Default: `True` | |
132 | ||
133 | - `payload` | |
134 | ||
135 | The payload content in bytes. A random payload is used by default. | |
136 | ||
137 | - Type: `bytes` | |
138 | - Default: `None` | |
139 | ||
140 | - `payload_size` | |
141 | ||
142 | The payload size. Ignored when the `payload` parameter is set. | |
143 | ||
144 | - Type: `int` | |
145 | - Default: `56` | |
146 | ||
147 | - `traffic_class` | |
148 | ||
149 | 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. | |
150 | ||
151 | *Only available on Unix systems. Ignored on Windows.* | |
152 | ||
153 | - Type: `int` | |
154 | - Default: `0` | |
125 | 155 | |
126 | 156 | #### 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`. | |
157 | - A `Host` object containing statistics about the desired destination:<br> | |
158 | `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive` | |
132 | 159 | |
133 | 160 | #### Exceptions |
161 | - `NameLookupError` | |
162 | ||
163 | If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved. | |
164 | ||
134 | 165 | - `SocketPermissionError` |
135 | 166 | |
136 | If the permissions are insufficient to create a socket. | |
167 | If the privileges are insufficient to create the socket. | |
168 | ||
169 | - `SocketAddressError` | |
170 | ||
171 | If the source address cannot be assigned to the socket. | |
172 | ||
173 | - `ICMPSocketError` | |
174 | ||
175 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
137 | 176 | |
138 | 177 | #### Example |
139 | 178 | ```python |
140 | 179 | >>> host = ping('1.1.1.1', count=10, interval=0.2) |
141 | 180 | |
142 | >>> host.address # The IP address of the gateway or host | |
143 | '1.1.1.1' # that responded to the request | |
181 | >>> host.address # The IP address of the host that responded | |
182 | '1.1.1.1' # to the request | |
144 | 183 | |
145 | 184 | >>> host.min_rtt # The minimum round-trip time |
146 | 185 | 12.2 |
167 | 206 | <br> |
168 | 207 | |
169 | 208 | ### 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) | |
209 | Send ICMP Echo Request packets to several network hosts. | |
210 | ||
211 | 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. | |
212 | ||
213 | ```python | |
214 | multiping(addresses, count=2, interval=0.01, timeout=2, id=PID, source=None, privileged=True, **kwargs) | |
175 | 215 | ``` |
176 | 216 | |
177 | 217 | #### Parameters |
178 | 218 | - `addresses` |
179 | 219 | |
180 | The IP addresses of the gateways or hosts to which messages should be sent. | |
220 | 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. | |
181 | 221 | |
182 | 222 | - Type: `list of str` |
183 | 223 | |
193 | 233 | The interval in seconds between sending each packet. |
194 | 234 | |
195 | 235 | - Type: `int` or `float` |
196 | - Default: `1` | |
236 | - Default: `0.01` | |
197 | 237 | |
198 | 238 | - `timeout` |
199 | 239 | |
200 | The maximum waiting time for receiving a reply in seconds. | |
240 | The maximum waiting time for receiving all responses in seconds. | |
201 | 241 | |
202 | 242 | - Type: `int` or `float` |
203 | 243 | - Default: `2` |
204 | 244 | |
205 | 245 | - `id` |
206 | 246 | |
207 | The identifier of the requests. This identifier will be incremented by one for each destination. | |
247 | 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. | |
208 | 248 | |
209 | 249 | - Type: `int` |
210 | 250 | - Default: `PID` |
211 | 251 | |
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. | |
252 | - `source` | |
253 | ||
254 | 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. | |
255 | ||
256 | - Type: `str` | |
257 | - Default: `None` | |
258 | ||
259 | - `privileged` | |
260 | ||
261 | 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. | |
262 | ||
263 | *Only available on Unix systems. Ignored on Windows.* | |
264 | ||
265 | - Type: `bool` | |
266 | - Default: `True` | |
267 | ||
268 | - `payload` | |
269 | ||
270 | The payload content in bytes. A random payload is used by default. | |
271 | ||
272 | - Type: `bytes` | |
273 | - Default: `None` | |
274 | ||
275 | - `payload_size` | |
276 | ||
277 | The payload size. Ignored when the `payload` parameter is set. | |
278 | ||
279 | - Type: `int` | |
280 | - Default: `56` | |
281 | ||
282 | - `traffic_class` | |
283 | ||
284 | 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. | |
285 | ||
286 | *Only available on Unix systems. Ignored on Windows.* | |
287 | ||
288 | - Type: `int` | |
289 | - Default: `0` | |
222 | 290 | |
223 | 291 | #### 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> | |
292 | - A `list of Host` objects containing statistics about the desired destinations:<br> | |
293 | `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive` | |
294 | ||
229 | 295 | The list is sorted in the same order as the addresses passed in parameters. |
230 | 296 | |
231 | 297 | #### Exceptions |
232 | 298 | - `SocketPermissionError` |
233 | 299 | |
234 | If the permissions are insufficient to create a socket. | |
300 | If the privileges are insufficient to create the socket. | |
301 | ||
302 | - `SocketAddressError` | |
303 | ||
304 | If the source address cannot be assigned to the socket. | |
305 | ||
306 | - `ICMPSocketError` | |
307 | ||
308 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
235 | 309 | |
236 | 310 | #### Example |
237 | 311 | ```python |
256 | 330 | ### Traceroute |
257 | 331 | Determine the route to a destination host. |
258 | 332 | |
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) | |
333 | 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. | |
334 | ||
335 | *This function requires root privileges to run.* | |
336 | ||
337 | ```python | |
338 | traceroute(address, count=2, interval=0.05, timeout=2, id=PID, first_hop=1, max_hops=30, source=None, fast=False, **kwargs) | |
264 | 339 | ``` |
265 | 340 | |
266 | 341 | #### Parameters |
267 | 342 | - `address` |
268 | 343 | |
269 | The destination IP address. | |
344 | The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address. | |
270 | 345 | |
271 | 346 | - Type: `str` |
272 | 347 | |
275 | 350 | The number of ping to perform per hop. |
276 | 351 | |
277 | 352 | - Type: `int` |
278 | - Default: `3` | |
353 | - Default: `2` | |
279 | 354 | |
280 | 355 | - `interval` |
281 | 356 | |
293 | 368 | |
294 | 369 | - `id` |
295 | 370 | |
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. | |
371 | The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process. | |
298 | 372 | |
299 | 373 | - Type: `int` |
300 | 374 | - Default: `PID` |
301 | 375 | |
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` | |
376 | - `first_hop` | |
377 | ||
378 | The initial time to live value used in outgoing probe packets. | |
379 | ||
380 | - Type: `int` | |
381 | - Default: `1` | |
311 | 382 | |
312 | 383 | - `max_hops` |
313 | 384 | |
316 | 387 | - Type: `int` |
317 | 388 | - Default: `30` |
318 | 389 | |
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. | |
390 | - `source` | |
391 | ||
392 | The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. | |
393 | ||
394 | - Type: `str` | |
395 | - Default: `None` | |
396 | ||
397 | - `fast` | |
398 | ||
399 | 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. | |
322 | 400 | |
323 | 401 | - Type: `bool` |
324 | 402 | - Default: `False` |
325 | 403 | |
326 | - `**kwargs` | |
327 | ||
328 | `Optional` Advanced use: arguments passed to `ICMPRequest` objects. | |
404 | - `payload` | |
405 | ||
406 | The payload content in bytes. A random payload is used by default. | |
407 | ||
408 | - Type: `bytes` | |
409 | - Default: `None` | |
410 | ||
411 | - `payload_size` | |
412 | ||
413 | The payload size. Ignored when the `payload` parameter is set. | |
414 | ||
415 | - Type: `int` | |
416 | - Default: `56` | |
417 | ||
418 | - `traffic_class` | |
419 | ||
420 | 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. | |
421 | ||
422 | *Only available on Unix systems. Ignored on Windows.* | |
423 | ||
424 | - Type: `int` | |
425 | - Default: `0` | |
329 | 426 | |
330 | 427 | #### 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. | |
428 | - 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`. | |
429 | ||
430 | 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. | |
334 | 431 | |
335 | 432 | #### Exceptions |
433 | - `NameLookupError` | |
434 | ||
435 | If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved. | |
436 | ||
336 | 437 | - `SocketPermissionError` |
337 | 438 | |
338 | If the permissions are insufficient to create a socket. | |
439 | If the privileges are insufficient to create the socket. | |
440 | ||
441 | - `SocketAddressError` | |
442 | ||
443 | If the source address cannot be assigned to the socket. | |
444 | ||
445 | - `ICMPSocketError` | |
446 | ||
447 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
339 | 448 | |
340 | 449 | #### Example |
341 | 450 | ```python |
342 | 451 | >>> hops = traceroute('1.1.1.1') |
343 | 452 | |
344 | >>> print('Distance (ttl) Address Average round-trip time') | |
453 | >>> print('Distance/TTL Address Average round-trip time') | |
345 | 454 | >>> last_distance = 0 |
346 | 455 | |
347 | 456 | >>> for hop in hops: |
348 | 457 | ... if last_distance + 1 != hop.distance: |
349 | ... print('Some routers are not responding') | |
458 | ... print('Some gateways are not responding') | |
350 | 459 | ... |
351 | 460 | ... # See the Hop class for details |
352 | 461 | ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms') |
354 | 463 | ... last_distance = hop.distance |
355 | 464 | ... |
356 | 465 | |
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 | |
466 | # Distance/TTL Address Average round-trip time | |
467 | # 1 10.0.0.1 5.196 ms | |
468 | # 2 194.149.169.49 7.552 ms | |
469 | # 3 194.149.166.54 12.21 ms | |
470 | # * Some gateways are not responding | |
471 | # 5 212.73.205.22 22.15 ms | |
472 | # 6 1.1.1.1 13.59 ms | |
364 | 473 | ``` |
365 | 474 | |
366 | 475 | <br> |
367 | 476 | |
368 | 477 | ## ICMP sockets |
369 | 478 | |
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 | └─────────────────┘ | |
479 | 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. | |
480 | ||
481 | ``` | |
482 | ┌──────────────────┐ | |
483 | ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐ | |
484 | │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │ | |
485 | └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘ | |
486 | └──────────────────┘ | |
378 | 487 | ``` |
379 | 488 | |
380 | 489 | ### 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 | |
490 | A user-created object that represents an ICMP Echo Request. | |
491 | ||
492 | ```python | |
493 | ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0) | |
494 | ``` | |
495 | ||
496 | #### Parameters and properties | |
389 | 497 | - `destination` |
390 | 498 | |
391 | The IP address of the gateway or host to which the message should be sent. | |
499 | The IP address of the host to which the message should be sent. | |
392 | 500 | |
393 | 501 | - Type: `str` |
394 | 502 | |
395 | 503 | - `id` |
396 | 504 | |
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. | |
505 | 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. | |
399 | 506 | |
400 | 507 | - Type: `int` |
401 | 508 | |
402 | 509 | - `sequence` |
403 | 510 | |
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. | |
511 | The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process. | |
406 | 512 | |
407 | 513 | - Type: `int` |
408 | 514 | |
420 | 526 | - Type: `int` |
421 | 527 | - Default: `56` |
422 | 528 | |
423 | - `timeout` | |
424 | ||
425 | The maximum waiting time for receiving a reply in seconds. | |
426 | ||
427 | - Type: `int` or `float` | |
428 | - Default: `2` | |
429 | ||
430 | 529 | - `ttl` |
431 | 530 | |
432 | The time to live of the packet in seconds. | |
531 | The time to live of the packet in terms of hops. | |
433 | 532 | |
434 | 533 | - Type: `int` |
435 | 534 | - Default: `64` |
436 | 535 | |
437 | 536 | - `traffic_class` |
438 | 537 | |
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> | |
538 | 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. | |
539 | ||
442 | 540 | *Only available on Unix systems. Ignored on Windows.* |
443 | 541 | |
444 | 542 | - Type: `int` |
445 | 543 | - Default: `0` |
446 | 544 | |
447 | #### Getters only | |
545 | #### Properties only | |
448 | 546 | - `time` |
449 | 547 | |
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. | |
548 | 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. | |
451 | 549 | |
452 | 550 | - Type: `float` |
453 | 551 | |
454 | 552 | <br> |
455 | 553 | |
456 | 554 | ### ICMPReply |
457 | A class that represents an ICMP reply. Generated from an `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`). | |
458 | ||
459 | #### Definition | |
555 | A class that represents an ICMP reply. Generated from an ICMP socket (`ICMPv4Socket` or `ICMPv6Socket`). | |
556 | ||
460 | 557 | ```python |
461 | 558 | ICMPReply(source, id, sequence, type, code, bytes_received, time) |
462 | 559 | ``` |
463 | 560 | |
464 | #### Parameters / Getters | |
561 | #### Parameters and properties | |
465 | 562 | - `source` |
466 | 563 | |
467 | 564 | The IP address of the gateway or host that composes the ICMP message. |
470 | 567 | |
471 | 568 | - `id` |
472 | 569 | |
473 | The identifier of the request. Used to match the reply with the request. | |
570 | The identifier of the reply. Used to match the reply with the request. | |
474 | 571 | |
475 | 572 | - Type: `int` |
476 | 573 | |
482 | 579 | |
483 | 580 | - `type` |
484 | 581 | |
485 | The type of message. | |
582 | The type of ICMP message. | |
486 | 583 | |
487 | 584 | - Type: `int` |
488 | 585 | |
489 | 586 | - `code` |
490 | 587 | |
491 | The error code. | |
588 | The ICMP error code. | |
492 | 589 | |
493 | 590 | - Type: `int` |
494 | 591 | |
507 | 604 | #### Methods |
508 | 605 | - `raise_for_status()` |
509 | 606 | |
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*. | |
607 | Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing. | |
608 | ||
609 | - Raises `DestinationUnreachable`: If the destination is unreachable for some reason. | |
610 | - Raises `TimeExceeded`: If the time to live field of the ICMP request has reached zero. | |
611 | - Raises `ICMPError`: Raised for any other type and ICMP error code, except ICMP Echo Reply messages. | |
518 | 612 | |
519 | 613 | <br> |
520 | 614 | |
521 | 615 | ### ICMPv4Socket |
522 | Socket for sending and receiving ICMPv4 packets. | |
523 | ||
524 | #### Definition | |
525 | ```python | |
526 | ICMPv4Socket() | |
527 | ``` | |
616 | Class for sending and receiving ICMPv4 packets. | |
617 | ||
618 | ```python | |
619 | ICMPv4Socket(address=None, privileged=True) | |
620 | ``` | |
621 | ||
622 | #### Parameters | |
623 | - `source` | |
624 | ||
625 | The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces. | |
626 | ||
627 | - Type: `str` | |
628 | - Default: `None` | |
629 | ||
630 | - `privileged` | |
631 | ||
632 | 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. | |
633 | ||
634 | *Only available on Unix systems. Ignored on Windows.* | |
635 | ||
636 | - Type: `bool` | |
637 | - Default: `True` | |
528 | 638 | |
529 | 639 | #### Methods |
530 | - `__init__()` | |
640 | - `__init__(address=None, privileged=True)` | |
531 | 641 | |
532 | 642 | *Constructor. Automatically called: do not call it directly.* |
533 | 643 | |
534 | - Raises `SocketPermissionError`: If the permissions are insufficient to create the socket. | |
644 | - Raises `SocketPermissionError`: If the privileges are insufficient to create the socket. | |
645 | - Raises `SocketAddressError`: If the requested address cannot be assigned to the socket. | |
646 | - Raises `ICMPSocketError`: If another error occurs while creating the socket. | |
535 | 647 | |
536 | 648 | - `__del__()` |
537 | 649 | |
538 | 650 | *Destructor. Automatically called: do not call it directly.* |
539 | 651 | |
540 | - Call the `close` method. | |
652 | Call the `close` method. | |
541 | 653 | |
542 | 654 | - `send(request)` |
543 | 655 | |
544 | Send a request to a host. | |
545 | ||
656 | Send an ICMP request message over the network to a remote host.<br> | |
546 | 657 | This operation is non-blocking. Use the `receive` method to get the reply. |
547 | 658 | |
548 | - Parameter `ICMPRequest`: The ICMP request you have created. | |
659 | - 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. | |
549 | 660 | - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only). |
550 | 661 | - Raises `SocketUnavailableError`: If the socket is closed. |
551 | 662 | - Raises `ICMPSocketError`: If another error occurs while sending. |
552 | 663 | |
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. | |
664 | - `receive(request=None, timeout=2)` | |
665 | ||
666 | Receive an ICMP reply message from the socket.<br> | |
667 | This method can be called multiple times if you expect several responses as with a broadcast address. | |
668 | ||
669 | - Parameter `request` *(ICMPRequest)*: The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned. | |
670 | - Parameter `timeout` *(int or float)*: The maximum waiting time for receiving the response in seconds. Default to `2`. | |
671 | - Raises `TimeoutExceeded`: If no response is received before the timeout specified in parameters. | |
560 | 672 | - Raises `SocketUnavailableError`: If the socket is closed. |
561 | 673 | - 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. | |
674 | ||
675 | Returns an `ICMPReply` object representing the response of the desired destination or an upstream gateway. | |
563 | 676 | |
564 | 677 | - `close()` |
565 | 678 | |
566 | 679 | Close the socket. It cannot be used after this call. |
567 | 680 | |
568 | #### Getters only | |
681 | #### Properties | |
682 | - `address` | |
683 | ||
684 | The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces. | |
685 | ||
686 | - Type: `str` | |
687 | ||
688 | - `is_privileged` | |
689 | ||
690 | Indicate whether the socket is running in privileged mode. | |
691 | ||
692 | - Type: `bool` | |
693 | ||
569 | 694 | - `is_closed` |
570 | 695 | |
571 | 696 | Indicate whether the socket is closed. |
572 | 697 | |
573 | 698 | - Type: `bool` |
574 | 699 | |
575 | #### Getters / Setters | |
700 | #### Properties and setters | |
576 | 701 | - `broadcast` |
577 | 702 | |
578 | 703 | Enable or disable the broadcast support on the socket. |
583 | 708 | <br> |
584 | 709 | |
585 | 710 | ### 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. | |
711 | Class for sending and receiving ICMPv6 packets. | |
712 | ||
713 | ```python | |
714 | ICMPv6Socket(address=None, privileged=True) | |
715 | ``` | |
716 | ||
717 | #### Methods and properties | |
718 | The same methods and properties as for the `ICMPv4Socket` class, except the `broadcast` property. | |
595 | 719 | |
596 | 720 | <br> |
597 | 721 | |
600 | 724 | |
601 | 725 | ``` |
602 | 726 | ICMPLibError |
727 | ├─ NameLookupError | |
603 | 728 | ├─ ICMPSocketError |
729 | │ ├─ SocketAddressError | |
604 | 730 | │ ├─ SocketPermissionError |
605 | 731 | │ ├─ SocketUnavailableError |
606 | 732 | │ ├─ SocketBroadcastError |
607 | 733 | │ └─ TimeoutExceeded |
608 | │ | |
734 | │ | |
609 | 735 | └─ ICMPError |
610 | 736 | ├─ DestinationUnreachable |
611 | 737 | │ ├─ ICMPv4DestinationUnreachable |
612 | 738 | │ └─ ICMPv6DestinationUnreachable |
613 | │ | |
739 | │ | |
614 | 740 | └─ TimeExceeded |
615 | 741 | ├─ ICMPv4TimeExceeded |
616 | 742 | └─ ICMPv6TimeExceeded |
617 | 743 | ``` |
618 | 744 | |
619 | 745 | - `ICMPLibError`: Exception class for the icmplib package. |
746 | - `NameLookupError`: Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames. | |
620 | 747 | - `ICMPSocketError`: Base class for ICMP sockets exceptions. |
621 | - `SocketPermissionError`: Raised when the permissions are insufficient to create a socket. | |
748 | - `SocketAddressError`: Raised when the requested address cannot be assigned to the socket. | |
749 | - `SocketPermissionError`: Raised when the privileges are insufficient to create the socket. | |
622 | 750 | - `SocketUnavailableError`: Raised when an action is performed while the socket is closed. |
623 | 751 | - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket. |
624 | 752 | - `TimeoutExceeded`: Raised when a timeout occurs on a socket. |
626 | 754 | - `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. |
627 | 755 | - `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. |
628 | 756 | |
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). | |
757 | Use the `message` property to get the error message. `ICMPError` subclasses have a `reply` property to retrieve the response. | |
632 | 758 | |
633 | 759 | <br> |
634 | 760 | |
637 | 763 | ```python |
638 | 764 | def single_ping(address, timeout=2, id=PID): |
639 | 765 | # Create an ICMP socket |
640 | socket = ICMPv4Socket() | |
641 | ||
642 | # Create a request | |
643 | # See the ICMPRequest class for details | |
766 | sock = ICMPv4Socket() | |
767 | ||
768 | # Create an ICMP request | |
769 | # See the 'ICMPRequest' class for details | |
644 | 770 | request = ICMPRequest( |
645 | 771 | destination=address, |
646 | 772 | id=id, |
647 | sequence=1, | |
648 | timeout=timeout) | |
773 | sequence=1) | |
649 | 774 | |
650 | 775 | 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 | |
776 | sock.send(request) | |
777 | ||
778 | # If the program arrives in this section, it means that the | |
779 | # packet has been transmitted. | |
780 | ||
781 | reply = sock.receive(request, timeout) | |
782 | ||
783 | # If the program arrives in this section, it means that a | |
784 | # packet has been received. The reply has the same identifier | |
785 | # and sequence number that the request but it can come from | |
786 | # an intermediate gateway. | |
662 | 787 | |
663 | 788 | reply.raise_for_status() |
664 | 789 | |
665 | # If the program arrives in this section, | |
666 | # it means that the destination host has responded to | |
667 | # the request | |
790 | # If the program arrives in this section, it means that the | |
791 | # destination host has responded to the request. | |
668 | 792 | |
669 | 793 | except TimeoutExceeded as err: |
670 | 794 | # The timeout has been reached |
671 | # Equivalent to print(err.message) | |
672 | 795 | print(err) |
673 | 796 | |
674 | 797 | except DestinationUnreachable as err: |
675 | # The reply indicates that the destination host is | |
676 | # unreachable | |
798 | # The reply indicates that the destination host is unreachable | |
677 | 799 | print(err) |
678 | 800 | |
679 | 801 | # Retrieve the response |
680 | 802 | reply = err.reply |
681 | 803 | |
682 | 804 | except TimeExceeded as err: |
683 | # The reply indicates that the time to live exceeded | |
684 | # in transit | |
805 | # The reply indicates that the time to live exceeded in transit | |
685 | 806 | print(err) |
686 | 807 | |
687 | 808 | # Retrieve the response |
697 | 818 | #### Verbose ping |
698 | 819 | ```python |
699 | 820 | 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 | |
821 | # A payload of 56 bytes is used by default. You can modify it using | |
822 | # the 'payload_size' parameter of your ICMP request. | |
823 | print(f'PING {address}: 56 data bytes\n') | |
824 | ||
825 | # We detect the socket to use from the specified IP address | |
705 | 826 | if is_ipv6_address(address): |
706 | socket = ICMPv6Socket() | |
827 | sock = ICMPv6Socket() | |
707 | 828 | |
708 | 829 | else: |
709 | socket = ICMPv4Socket() | |
830 | sock = ICMPv4Socket() | |
710 | 831 | |
711 | 832 | for sequence in range(count): |
712 | 833 | # We create an ICMP request |
713 | 834 | request = ICMPRequest( |
714 | 835 | destination=address, |
715 | 836 | id=id, |
716 | sequence=sequence, | |
717 | timeout=timeout) | |
837 | sequence=sequence) | |
718 | 838 | |
719 | 839 | try: |
720 | 840 | # We send the request |
721 | socket.send(request) | |
841 | sock.send(request) | |
722 | 842 | |
723 | 843 | # We are awaiting receipt of an ICMP reply |
724 | reply = socket.receive() | |
844 | reply = sock.receive(request, timeout) | |
725 | 845 | |
726 | 846 | # We received a reply |
727 | 847 | # We display some information |
767 | 887 | |
768 | 888 | ## FAQ |
769 | 889 | |
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') | |
890 | ### How to resolve a FQDN/domain name or a hostname? | |
891 | The use of the built-in `resolve` function is recommended: | |
892 | ||
893 | ```python | |
894 | >>> resolve('github.com') | |
775 | 895 | '140.82.118.4' |
776 | 896 | ``` |
777 | 897 | |
898 | - If several IP addresses are available, only the first one is returned. This function searches for IPv4 addresses first before searching for IPv6 addresses. | |
899 | - If you pass an IP address, no lookup is done. The same address is returned. | |
900 | - Raises a `NameLookupError` exception if the requested name does not exist or cannot be resolved. | |
901 | ||
902 | ### How to use the library without root privileges? | |
903 | Since its version 2.0, icmplib can be used without root privileges. | |
904 | ||
905 | For this, you can set the `privileged` parameter to `False` on the `ping` and `multiping` functions, as well as the low level classes. By disabling this parameter, the kernel handles some parts of the ICMP headers. | |
906 | ||
907 | On some Linux systems, you must allow this feature: | |
908 | ||
909 | ```shell | |
910 | $ echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee -a /etc/sysctl.conf | |
911 | $ sudo sysctl -p | |
912 | ``` | |
913 | ||
914 | You can check the current value with the following command: | |
915 | ||
916 | ```shell | |
917 | $ sysctl net.ipv4.ping_group_range | |
918 | net.ipv4.ping_group_range = 0 2147483647 | |
919 | ``` | |
920 | ||
921 | *Since Ubuntu 20.04 LTS, this manipulation is no longer necessary.* | |
922 | ||
923 | [Read more on www.kernel.org](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt) | |
924 | ||
925 | ### Why I have no response from a remote host? | |
926 | In the event of no response from a remote host, several causes are possible: | |
927 | - 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. | |
928 | - The remote host or an upstream gateway is down. | |
929 | - The remote host or an upstream gateway drops ICMP messages for security reasons. | |
930 | - 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. | |
931 | ||
932 | <br> | |
933 | ||
778 | 934 | ## Contributing |
779 | 935 | |
780 | 936 | Comments and enhancements are welcome. |
789 | 945 | |
790 | 946 | ## License |
791 | 947 | |
792 | Copyright 2017-2020 Valentin BELYN. | |
948 | Copyright 2017-2021 Valentin BELYN. | |
793 | 949 | |
794 | 950 | Code released under the GNU LGPLv3 license. See the [LICENSE](LICENSE) for details. |
0 | # Examples | |
1 | ||
2 | ## Built-in functions | |
3 | ||
4 | - [ping](ping.py) | |
5 | - [multiping](multiping.py) | |
6 | - [traceroute](traceroute.py) | |
7 | ||
8 | ## Sockets (advanced) | |
9 | ||
10 | - [verbose-ping](verbose_ping.py) | |
11 | ||
12 | This example shows how to reproduce the ping command using sockets of `icmplib`. | |
13 | ||
14 | *Output:* | |
15 | ||
16 | ```console | |
17 | PING 1.1.1.1: 56 data bytes | |
18 | ||
19 | 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms | |
20 | 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms | |
21 | 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms | |
22 | 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms | |
23 | ``` | |
24 | ||
25 | - [verbose-traceroute](verbose_traceroute.py) | |
26 | ||
27 | This example shows how to reproduce the traceroute command. | |
28 | ||
29 | *Output:* | |
30 | ||
31 | ```console | |
32 | Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max | |
33 | ||
34 | 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 | |
38 | 5 * * * | |
39 | 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms | |
40 | 7 198.27.92.1 www.ovh.com 10.87 ms | |
41 | ``` | |
42 | ||
43 | - [broadcast-ping](broadcast_ping.py) | |
44 | ||
45 | This example shows how to enable the broadcast support on a socket. | |
46 | ||
47 | *Output:* | |
48 | ||
49 | ```console | |
50 | PING 255.255.255.255: 56 data bytes | |
51 | ||
52 | 64 bytes from 10.0.0.17: icmp_seq=0 time=1.065 ms | |
53 | 64 bytes from 10.0.0.40: icmp_seq=0 time=1.595 ms | |
54 | 64 bytes from 10.0.0.41: icmp_seq=0 time=9.471 ms | |
55 | ||
56 | 64 bytes from 10.0.0.17: icmp_seq=1 time=0.983 ms | |
57 | 64 bytes from 10.0.0.40: icmp_seq=1 time=1.579 ms | |
58 | 64 bytes from 10.0.0.41: icmp_seq=1 time=9.345 ms | |
59 | ||
60 | 64 bytes from 10.0.0.17: icmp_seq=2 time=0.916 ms | |
61 | 64 bytes from 10.0.0.40: icmp_seq=2 time=2.031 ms | |
62 | 64 bytes from 10.0.0.41: icmp_seq=2 time=9.554 ms | |
63 | ||
64 | 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms | |
65 | 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms | |
66 | 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms | |
67 | ``` |
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 | Example: broadcast ping (advanced) | |
12 | ||
13 | PING 255.255.255.255: 56 data bytes | |
14 | ||
15 | 64 bytes from 10.0.0.17: icmp_seq=0 time=1.065 ms | |
16 | 64 bytes from 10.0.0.40: icmp_seq=0 time=1.595 ms | |
17 | 64 bytes from 10.0.0.41: icmp_seq=0 time=9.471 ms | |
18 | ||
19 | 64 bytes from 10.0.0.17: icmp_seq=1 time=0.983 ms | |
20 | 64 bytes from 10.0.0.40: icmp_seq=1 time=1.579 ms | |
21 | 64 bytes from 10.0.0.41: icmp_seq=1 time=9.345 ms | |
22 | ||
23 | 64 bytes from 10.0.0.17: icmp_seq=2 time=0.916 ms | |
24 | 64 bytes from 10.0.0.40: icmp_seq=2 time=2.031 ms | |
25 | 64 bytes from 10.0.0.41: icmp_seq=2 time=9.554 ms | |
26 | ||
27 | 64 bytes from 10.0.0.17: icmp_seq=3 time=1.112 ms | |
28 | 64 bytes from 10.0.0.40: icmp_seq=3 time=1.384 ms | |
29 | 64 bytes from 10.0.0.41: icmp_seq=3 time=9.565 ms | |
30 | ''' | |
31 | ||
32 | from icmplib import ( | |
33 | ICMPv4Socket, | |
34 | ICMPRequest, | |
35 | TimeoutExceeded, | |
36 | ICMPLibError, | |
37 | PID) | |
38 | ||
39 | ||
40 | 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') | |
44 | ||
45 | # Broadcast is only possible in IPv4 | |
46 | socket = ICMPv4Socket() | |
47 | ||
48 | # We allow the socket to send broadcast packets | |
49 | socket.broadcast = True | |
50 | ||
51 | for sequence in range(count): | |
52 | # We create an ICMP request | |
53 | request = ICMPRequest( | |
54 | destination=address, | |
55 | id=id, | |
56 | sequence=sequence, | |
57 | timeout=timeout) | |
58 | ||
59 | print() | |
60 | ||
61 | try: | |
62 | # We send the request | |
63 | socket.send(request) | |
64 | ||
65 | 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() | |
70 | ||
71 | # We calculate the round-trip time of the reply | |
72 | round_trip_time = (reply.time - request.time) * 1000 | |
73 | ||
74 | # We display some information | |
75 | print(f' {reply.bytes_received} bytes from ' | |
76 | f'{reply.source}: icmp_seq={sequence} ' | |
77 | f'time={round(round_trip_time, 3)} ms') | |
78 | ||
79 | except TimeoutExceeded: | |
80 | # The timeout has been reached | |
81 | # We use this exception to break the while loop | |
82 | pass | |
83 | ||
84 | except ICMPLibError: | |
85 | # All other errors | |
86 | print('An error has occurred.') | |
87 | ||
88 | ||
89 | # Limited broadcast | |
90 | broadcast_ping('255.255.255.255') | |
91 | ||
92 | # Directed broadcast | |
93 | # broadcast_ping('192.168.0.255') |
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 | Example: multiping | |
12 | ''' | |
13 | ||
14 | from icmplib import multiping | |
15 | ||
16 | ||
17 | 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 | ||
23 | # IPv4 addresses | |
24 | '1.1.1.1', | |
25 | '8.8.8.8', | |
26 | '10.0.0.100', | |
27 | '10.0.0.200', | |
28 | ||
29 | # IPv6 addresses | |
30 | '::1', | |
31 | ] | |
32 | ||
33 | hosts = multiping(addresses, count=2, interval=0.5, timeout=1) | |
34 | ||
35 | hosts_alive = [] | |
36 | hosts_dead = [] | |
37 | ||
38 | for host in hosts: | |
39 | if host.is_alive: | |
40 | hosts_alive.append(host.address) | |
41 | ||
42 | else: | |
43 | hosts_dead.append(host.address) | |
44 | ||
45 | print(hosts_alive) | |
46 | # ['github.com', '1.1.1.1', '8.8.8.8', '::1'] | |
47 | ||
48 | print(hosts_dead) | |
49 | # ['10.0.0.100', '10.0.0.200'] |
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 | Example: ping | |
12 | ''' | |
13 | ||
14 | from icmplib import ping | |
15 | ||
16 | ||
17 | host = ping('1.1.1.1', count=10, interval=0.5, timeout=1) | |
18 | ||
19 | # The IP address of the gateway or host that responded to the request | |
20 | print(host.address) | |
21 | # '1.1.1.1' | |
22 | ||
23 | # The minimum round-trip time | |
24 | print(host.min_rtt) | |
25 | # 12.2 | |
26 | ||
27 | # The average round-trip time | |
28 | print(host.avg_rtt) | |
29 | # 13.2 | |
30 | ||
31 | # The maximum round-trip time | |
32 | print(host.max_rtt) | |
33 | # 17.6 | |
34 | ||
35 | # The number of packets transmitted to the destination host | |
36 | print(host.packets_sent) | |
37 | # 10 | |
38 | ||
39 | # The number of packets sent by the remote host and received by the | |
40 | # current host | |
41 | print(host.packets_received) | |
42 | # 9 | |
43 | ||
44 | # Packet loss occurs when packets fail to reach their destination | |
45 | # Returns a float between 0 and 1 (all packets are lost) | |
46 | print(host.packet_loss) | |
47 | # 0.1 | |
48 | ||
49 | # Indicates whether the host is reachable | |
50 | print(host.is_alive) | |
51 | # True |
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 | Example: traceroute | |
12 | ''' | |
13 | ||
14 | from icmplib import traceroute | |
15 | ||
16 | ||
17 | hops = traceroute('1.1.1.1', timeout=1, fast_mode=True) | |
18 | ||
19 | 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 | ||
25 | ||
26 | last_distance = 0 | |
27 | ||
28 | for hop in hops: | |
29 | if last_distance + 1 != hop.distance: | |
30 | print(' * Some routers are not responding') | |
31 | ||
32 | print(f'{hop.distance:4} {hop.address:15} ' | |
33 | f'{hop.avg_rtt} ms') | |
34 | ||
35 | last_distance = hop.distance | |
36 | ||
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 |
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 | Example: verbose ping (advanced) | |
12 | ||
13 | PING 1.1.1.1: 56 data bytes | |
14 | ||
15 | 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms | |
16 | 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms | |
17 | 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms | |
18 | 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms | |
19 | ''' | |
20 | ||
21 | from icmplib import ( | |
22 | ICMPv4Socket, | |
23 | ICMPv6Socket, | |
24 | ICMPRequest, | |
25 | TimeoutExceeded, | |
26 | ICMPError, | |
27 | ICMPLibError, | |
28 | is_ipv6_address, | |
29 | PID) | |
30 | ||
31 | from time import sleep | |
32 | ||
33 | ||
34 | 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 | |
37 | print(f'PING {address}: 56 data bytes\n') | |
38 | ||
39 | # Detection of the socket to use | |
40 | if is_ipv6_address(address): | |
41 | socket = ICMPv6Socket() | |
42 | ||
43 | else: | |
44 | socket = ICMPv4Socket() | |
45 | ||
46 | for sequence in range(count): | |
47 | # We create an ICMP request | |
48 | request = ICMPRequest( | |
49 | destination=address, | |
50 | id=id, | |
51 | sequence=sequence, | |
52 | timeout=timeout) | |
53 | ||
54 | try: | |
55 | # We send the request | |
56 | socket.send(request) | |
57 | ||
58 | # We are awaiting receipt of an ICMP reply | |
59 | reply = socket.receive() | |
60 | ||
61 | # We received a reply | |
62 | # We display some information | |
63 | print(f' {reply.bytes_received} bytes from ' | |
64 | f'{reply.source}: ', end='') | |
65 | ||
66 | # We throw an exception if it is an ICMP error message | |
67 | reply.raise_for_status() | |
68 | ||
69 | # We calculate the round-trip time and we display it | |
70 | round_trip_time = (reply.time - request.time) * 1000 | |
71 | ||
72 | print(f'icmp_seq={sequence} ' | |
73 | f'time={round(round_trip_time, 3)} ms') | |
74 | ||
75 | # We pause before continuing | |
76 | if sequence < count - 1: | |
77 | sleep(interval) | |
78 | ||
79 | except TimeoutExceeded: | |
80 | # The timeout has been reached | |
81 | print(f'Request timeout for icmp_seq {sequence}') | |
82 | ||
83 | except ICMPError as err: | |
84 | # An ICMP error message has been received | |
85 | print(err) | |
86 | ||
87 | except ICMPLibError: | |
88 | # All other errors | |
89 | print('An error has occurred.') | |
90 | ||
91 | ||
92 | verbose_ping('1.1.1.1') |
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 | Example: verbose traceroute (advanced) | |
12 | ||
13 | Traceroute to ovh.com (198.27.92.1): 56 data bytes, 30 hops max | |
14 | ||
15 | 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 | 5 * * * | |
20 | 6 37.187.231.75 be5.rbx-iplb1-a70.fr.eu 17.1 ms | |
21 | 7 198.27.92.1 www.ovh.com 10.87 ms | |
22 | ''' | |
23 | ||
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 | |
35 | from time import sleep | |
36 | ||
37 | ||
38 | def verbose_traceroute(address, count=3, interval=0.05, timeout=2, | |
39 | 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)}): ' | |
43 | f'56 data bytes, {max_hops} hops max\n') | |
44 | ||
45 | # Detection of the socket to use | |
46 | if is_ipv6_address(address): | |
47 | socket = ICMPv6Socket() | |
48 | ||
49 | else: | |
50 | socket = ICMPv4Socket() | |
51 | ||
52 | ttl = 1 | |
53 | host_reached = False | |
54 | ||
55 | while not host_reached and ttl <= max_hops: | |
56 | for sequence in range(count): | |
57 | # We create an ICMP request | |
58 | request = ICMPRequest( | |
59 | destination=address, | |
60 | id=id, | |
61 | sequence=sequence, | |
62 | timeout=timeout, | |
63 | ttl=ttl) | |
64 | ||
65 | try: | |
66 | # We send the request | |
67 | socket.send(request) | |
68 | ||
69 | # We are awaiting receipt of an ICMP reply | |
70 | reply = socket.receive() | |
71 | ||
72 | # We received a reply | |
73 | # We display some information | |
74 | source_name = getfqdn(reply.source) | |
75 | ||
76 | print(f'{ttl:3} {reply.source:15} ' | |
77 | f'{source_name:40} ', end='') | |
78 | ||
79 | # We throw an exception if it is an ICMP error message | |
80 | reply.raise_for_status() | |
81 | ||
82 | # We reached the destination host | |
83 | # We calculate the round-trip time and we display it | |
84 | round_trip_time = (reply.time - request.time) * 1000 | |
85 | print(round(round_trip_time, 2), 'ms') | |
86 | ||
87 | # We can stop the search | |
88 | host_reached = True | |
89 | break | |
90 | ||
91 | except TimeExceeded as err: | |
92 | # An ICMP Time Exceeded message has been received | |
93 | # The message was probably generated by an intermediate | |
94 | # gateway | |
95 | reply = err.reply | |
96 | ||
97 | # We calculate the round-trip time and we display it | |
98 | round_trip_time = (reply.time - request.time) * 1000 | |
99 | print(round(round_trip_time, 2), 'ms') | |
100 | ||
101 | sleep(interval) | |
102 | break | |
103 | ||
104 | except TimeoutExceeded: | |
105 | # The timeout has been reached and no host or gateway | |
106 | # has responded after multiple attemps | |
107 | if sequence >= count - 1: | |
108 | print(f'{ttl:3} * * *') | |
109 | ||
110 | except ICMPLibError: | |
111 | # Other errors are ignored | |
112 | pass | |
113 | ||
114 | ttl += 1 | |
115 | ||
116 | print() | |
117 | ||
118 | ||
119 | verbose_traceroute('ovh.com') |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |
23 | 26 | <https://www.gnu.org/licenses/>. |
24 | 27 | ''' |
25 | 28 | |
26 | from .sockets import ICMPv4Socket, ICMPv6Socket | |
29 | from .sockets import ICMPv4Socket, ICMPv6Socket, AsyncSocket | |
27 | 30 | from .models import ICMPRequest, ICMPReply, Host, Hop |
28 | from .ping import ping, multiping | |
31 | from .ping import ping, async_ping | |
32 | from .multiping import multiping, async_multiping | |
29 | 33 | from .traceroute import traceroute |
30 | 34 | from .exceptions import * |
31 | from .utils import PID, is_ipv4_address, is_ipv6_address | |
35 | from .utils import is_hostname, is_ipv4_address, is_ipv6_address | |
36 | from .utils import PID, resolve, async_resolve | |
32 | 37 | |
33 | 38 | |
34 | 39 | __author__ = 'Valentin BELYN' |
35 | __copyright__ = 'Copyright 2017-2020 Valentin BELYN' | |
40 | __copyright__ = 'Copyright 2017-2021 Valentin BELYN' | |
36 | 41 | __license__ = 'GNU Lesser General Public License v3.0' |
37 | 42 | |
38 | __version__ = '1.2.2' | |
39 | __build__ = '201010' | |
43 | __version__ = '3.0-alpha' | |
44 | __build__ = '210515' |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |
40 | 43 | return self._message |
41 | 44 | |
42 | 45 | |
46 | class NameLookupError(ICMPLibError): | |
47 | ''' | |
48 | Raised when the requested name does not exist or cannot be resolved. | |
49 | This concerns both Fully Qualified Domain Names and hostnames. | |
50 | ||
51 | ''' | |
52 | def __init__(self, name): | |
53 | message = f'The name \'{name}\' cannot be resolved' | |
54 | super().__init__(message) | |
55 | ||
56 | ||
43 | 57 | class ICMPSocketError(ICMPLibError): |
44 | 58 | ''' |
45 | 59 | Base class for ICMP sockets exceptions. |
47 | 61 | ''' |
48 | 62 | |
49 | 63 | |
64 | class SocketAddressError(ICMPSocketError): | |
65 | ''' | |
66 | Raised when the requested address cannot be assigned to the socket. | |
67 | ||
68 | ''' | |
69 | def __init__(self, address): | |
70 | message = f'The requested address ({address}) cannot be ' \ | |
71 | 'assigned to the socket' | |
72 | super().__init__(message) | |
73 | ||
74 | ||
50 | 75 | class SocketPermissionError(ICMPSocketError): |
51 | 76 | ''' |
52 | Raised when the permissions are insufficient to create a socket. | |
77 | Raised when the privileges are insufficient to create the socket. | |
53 | 78 | |
54 | 79 | ''' |
55 | 80 | def __init__(self): |
69 | 94 | |
70 | 95 | class SocketBroadcastError(ICMPSocketError): |
71 | 96 | ''' |
72 | Raised when a broadcast address is used and the corresponding | |
73 | option is not enabled on the socket. | |
97 | Raised when a broadcast address is used and the corresponding option | |
98 | is not enabled on the socket. | |
74 | 99 | |
75 | 100 | ''' |
76 | 101 | def __init__(self): |
77 | 102 | message = 'Broadcast is not allowed: ' \ |
78 | 'please use broadcast method (setter) to allow it' | |
103 | 'please use the \'broadcast\' property to allow it' | |
79 | 104 | super().__init__(message) |
80 | 105 | |
81 | 106 | |
161 | 186 | ''' |
162 | 187 | Base class for ICMP Time Exceeded messages. |
163 | 188 | |
164 | Time Exceeded message is generated by a gateway to inform the | |
165 | source of a discarded datagram due to the time to live field | |
166 | reaching zero. A Time Exceeded message may also be sent by a host | |
167 | if it fails to reassemble a fragmented datagram within its time | |
168 | limit. | |
189 | Time Exceeded message is generated by a gateway to inform the source | |
190 | of a discarded datagram due to the time to live field reaching zero. | |
191 | A Time Exceeded message may also be sent by a host if it fails to | |
192 | reassemble a fragmented datagram within its time limit. | |
169 | 193 | |
170 | 194 | ''' |
171 | 195 | _CODES = {} |
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) |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |
24 | 27 | ''' |
25 | 28 | |
26 | 29 | from .exceptions import * |
27 | from .utils import is_ipv6_address | |
28 | 30 | |
29 | 31 | |
30 | 32 | class ICMPRequest: |
31 | 33 | ''' |
32 | A user-created object that represents an ICMP ECHO_REQUEST. | |
34 | A user-created object that represents an ICMP Echo Request. | |
33 | 35 | |
34 | 36 | :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. | |
37 | 39 | |
38 | 40 | :type id: int |
39 | 41 | :param id: The identifier of the request. Used to match the reply |
40 | 42 | 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. | |
42 | 45 | |
43 | 46 | :type sequence: int |
44 | 47 | :param sequence: The sequence number. Used to match the reply with |
45 | 48 | the request. Typically, the sequence number is incremented for |
46 | 49 | each packet sent during the process. |
47 | 50 | |
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. | |
65 | 65 | Provides a defined level of service to the packet by setting |
66 | 66 | the DS Field (formerly TOS) or the Traffic Class field of the |
67 | 67 | IP header. Packets are delivered with the minimum priority by |
70 | 70 | Only available on Unix systems. Ignored on Windows. |
71 | 71 | |
72 | 72 | ''' |
73 | __slots__ = '_destination', '_id', '_sequence', '_payload', \ | |
74 | '_payload_size', '_ttl', '_traffic_class', '_time' | |
75 | ||
73 | 76 | 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 | |
77 | payload_size=56, ttl=64, traffic_class=0): | |
78 | 78 | |
79 | 79 | if payload: |
80 | 80 | payload_size = len(payload) |
81 | 81 | |
82 | 82 | self._destination = destination |
83 | self._id = id | |
84 | self._sequence = sequence | |
83 | self._id = id & 0xffff | |
84 | self._sequence = sequence & 0xffff | |
85 | 85 | self._payload = payload |
86 | 86 | self._payload_size = payload_size |
87 | self._timeout = timeout | |
88 | 87 | self._ttl = ttl |
89 | 88 | self._traffic_class = traffic_class |
90 | 89 | self._time = 0 |
95 | 94 | @property |
96 | 95 | def destination(self): |
97 | 96 | ''' |
98 | The IP address of the gateway or host to which the message | |
99 | should be sent. | |
97 | The IP address of the host to which the message should be sent. | |
100 | 98 | |
101 | 99 | ''' |
102 | 100 | return self._destination |
137 | 135 | return self._payload_size |
138 | 136 | |
139 | 137 | @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 | |
148 | 138 | def ttl(self): |
149 | 139 | ''' |
150 | The time to live of the packet in seconds. | |
140 | The time to live of the packet in terms of hops. | |
151 | 141 | |
152 | 142 | ''' |
153 | 143 | return self._ttl |
164 | 154 | def time(self): |
165 | 155 | ''' |
166 | 156 | The timestamp of the ICMP request. |
157 | ||
167 | 158 | Initialized to zero when creating the request and replaced by |
168 | `ICMPv4Socket` or `ICMPv6Socket` with the time of sending. | |
159 | the `send` method of an ICMP socket with the time of sending. | |
169 | 160 | |
170 | 161 | ''' |
171 | 162 | return self._time |
173 | 164 | |
174 | 165 | class ICMPReply: |
175 | 166 | ''' |
176 | A class that represents an ICMP reply. Generated from an | |
177 | `ICMPSocket` object (`ICMPv4Socket` or `ICMPv6Socket`). | |
167 | A class that represents an ICMP reply. Generated from an ICMP socket. | |
178 | 168 | |
179 | 169 | :type source: str |
180 | :param source: The IP address of the gateway or host that composes | |
181 | the ICMP message. | |
170 | :param source: The IP address of the host that composes the ICMP | |
171 | message. | |
172 | ||
173 | :type family: int | |
174 | :param family: The address family. Can be set to `4` for IPv4 or `6` | |
175 | for IPv6 addresses. | |
182 | 176 | |
183 | 177 | :type id: int |
184 | :param id: The identifier of the reply. Used to match the reply | |
185 | with the request. | |
178 | :param id: The identifier of the reply. Used to match the reply with | |
179 | the request. | |
186 | 180 | |
187 | 181 | :type sequence: int |
188 | 182 | :param sequence: The sequence number. Used to match the reply with |
189 | 183 | the request. |
190 | 184 | |
191 | 185 | :type type: int |
192 | :param type: The type of message. | |
186 | :param type: The type of ICMP message. | |
193 | 187 | |
194 | 188 | :type code: int |
195 | :param code: The error code. | |
189 | :param code: The ICMP error code. | |
196 | 190 | |
197 | 191 | :type bytes_received: int |
198 | 192 | :param bytes_received: The number of bytes received. |
201 | 195 | :param time: The timestamp of the ICMP reply. |
202 | 196 | |
203 | 197 | ''' |
204 | def __init__(self, source, id, sequence, type, code, | |
198 | __slots__ = '_source', '_family', '_id', '_sequence', '_type', \ | |
199 | '_code', '_bytes_received', '_time' | |
200 | ||
201 | def __init__(self, source, family, id, sequence, type, code, | |
205 | 202 | bytes_received, time): |
206 | 203 | |
207 | 204 | self._source = source |
205 | self._family = family | |
208 | 206 | self._id = id |
209 | 207 | self._sequence = sequence |
210 | 208 | self._type = type |
217 | 215 | |
218 | 216 | def raise_for_status(self): |
219 | 217 | ''' |
220 | Throw an exception if the reply is not an ICMP ECHO_REPLY. | |
218 | Throw an exception if the reply is not an ICMP Echo Reply. | |
221 | 219 | Otherwise, do nothing. |
222 | 220 | |
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): | |
234 | echo_reply_type = 129 | |
235 | errors = { | |
236 | 1: ICMPv6DestinationUnreachable, | |
237 | 3: ICMPv6TimeExceeded | |
238 | } | |
239 | ||
221 | :raises DestinationUnreachable: If the destination is | |
222 | unreachable for some reason. | |
223 | :raises TimeExceeded: If the time to live field of the ICMP | |
224 | request has reached zero. | |
225 | :raises ICMPError: Raised for any other type and ICMP error | |
226 | code, except ICMP Echo Reply messages. | |
227 | ||
228 | ''' | |
229 | if self._family == 6: | |
230 | if self._type == 1: | |
231 | raise ICMPv6DestinationUnreachable(self) | |
232 | ||
233 | if self._type == 3: | |
234 | raise ICMPv6TimeExceeded(self) | |
240 | 235 | else: |
241 | echo_reply_type = 0 | |
242 | errors = { | |
243 | 3: ICMPv4DestinationUnreachable, | |
244 | 11: ICMPv4TimeExceeded | |
245 | } | |
246 | ||
247 | if self._type in errors: | |
248 | raise errors[self._type](self) | |
249 | ||
250 | if self._type != echo_reply_type: | |
251 | message = f'Error type: {self._type}, ' \ | |
252 | f'code: {self._code}' | |
253 | ||
236 | if self._type == 3: | |
237 | raise ICMPv4DestinationUnreachable(self) | |
238 | ||
239 | if self._type == 11: | |
240 | raise ICMPv4TimeExceeded(self) | |
241 | ||
242 | if (self._family == 4 and self._type != 0 or | |
243 | self._family == 6 and self._type != 129): | |
244 | message = f'Error type: {self._type}, code: {self._code}' | |
254 | 245 | raise ICMPError(message, self) |
255 | 246 | |
256 | 247 | @property |
257 | 248 | def source(self): |
258 | 249 | ''' |
259 | The IP address of the gateway or host that composes the ICMP | |
260 | message. | |
250 | The IP address of the host that composes the ICMP message. | |
261 | 251 | |
262 | 252 | ''' |
263 | 253 | return self._source |
265 | 255 | @property |
266 | 256 | def id(self): |
267 | 257 | ''' |
268 | The identifier of the request. | |
258 | The identifier of the reply. | |
269 | 259 | Used to match the reply with the request. |
270 | 260 | |
271 | 261 | ''' |
283 | 273 | @property |
284 | 274 | def type(self): |
285 | 275 | ''' |
286 | The type of message. | |
276 | The type of ICMP message. | |
287 | 277 | |
288 | 278 | ''' |
289 | 279 | return self._type |
291 | 281 | @property |
292 | 282 | def code(self): |
293 | 283 | ''' |
294 | The error code. | |
284 | The ICMP error code. | |
295 | 285 | |
296 | 286 | ''' |
297 | 287 | return self._code |
305 | 295 | return self._bytes_received |
306 | 296 | |
307 | 297 | @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 | |
320 | 298 | def time(self): |
321 | 299 | ''' |
322 | 300 | The timestamp of the ICMP reply. |
327 | 305 | |
328 | 306 | class Host: |
329 | 307 | ''' |
330 | A class that represents a host. Simplifies the exploitation of | |
331 | results from `ping` and `traceroute` functions. | |
308 | A class that represents a host. It simplifies the use of the results | |
309 | from the `ping`, `multiping` and `traceroute` functions. | |
332 | 310 | |
333 | 311 | :type address: str |
334 | :param address: The IP address of the gateway or host that | |
335 | responded to the request. | |
336 | ||
337 | :type min_rtt: float | |
338 | :param min_rtt: The minimum round-trip time. | |
339 | ||
340 | :type avg_rtt: float | |
341 | :param avg_rtt: The average round-trip time. | |
342 | ||
343 | :type max_rtt: float | |
344 | :param max_rtt: The maximum round-trip time. | |
312 | :param address: The IP address of the host that responded to the | |
313 | request. | |
345 | 314 | |
346 | 315 | :type packets_sent: int |
347 | 316 | :param packets_sent: The number of packets transmitted to the |
348 | 317 | destination host. |
349 | 318 | |
350 | :type packets_received: int | |
351 | :param packets_received: The number of packets sent by the remote | |
352 | host and received by the current host. | |
353 | ||
354 | ''' | |
355 | def __init__(self, address, min_rtt, avg_rtt, max_rtt, | |
356 | packets_sent, packets_received): | |
357 | ||
319 | :type rtts: list[float] | |
320 | :param rtts: The list of round-trip times expressed in milliseconds. | |
321 | ||
322 | ''' | |
323 | __slots__ = '_address', '_packets_sent', '_rtts' | |
324 | ||
325 | def __init__(self, address, packets_sent, rtts): | |
358 | 326 | self._address = address |
359 | self._min_rtt = round(min_rtt, 3) | |
360 | self._avg_rtt = round(avg_rtt, 3) | |
361 | self._max_rtt = round(max_rtt, 3) | |
362 | 327 | self._packets_sent = packets_sent |
363 | self._packets_received = packets_received | |
328 | self._rtts = rtts | |
364 | 329 | |
365 | 330 | def __repr__(self): |
366 | 331 | return f'<Host [{self._address}]>' |
367 | 332 | |
333 | def __str__(self): | |
334 | return f' {self._address}\n' + '-' * 60 + '\n' \ | |
335 | f' Packets sent: {self._packets_sent}\n' \ | |
336 | f' Packets received: {self.packets_received}\n' \ | |
337 | f' Packet loss: {self.packet_loss * 100}%\n' \ | |
338 | f' Round-trip times: {self.min_rtt} ms / ' \ | |
339 | f'{self.avg_rtt} ms / {self.max_rtt} ms\n' \ | |
340 | f' Jitter: {self.jitter} ms\n' + '-' * 60 | |
341 | ||
368 | 342 | @property |
369 | 343 | def address(self): |
370 | 344 | ''' |
371 | The IP address of the gateway or host that responded to the | |
372 | request. | |
345 | The IP address of the host that responded to the request. | |
373 | 346 | |
374 | 347 | ''' |
375 | 348 | return self._address |
377 | 350 | @property |
378 | 351 | def min_rtt(self): |
379 | 352 | ''' |
380 | The minimum round-trip time. | |
381 | ||
382 | ''' | |
383 | return self._min_rtt | |
353 | The minimum round-trip time in milliseconds. | |
354 | ||
355 | ''' | |
356 | if not self._rtts: | |
357 | return 0.0 | |
358 | ||
359 | return round(min(self._rtts), 3) | |
384 | 360 | |
385 | 361 | @property |
386 | 362 | def avg_rtt(self): |
387 | 363 | ''' |
388 | The average round-trip time. | |
389 | ||
390 | ''' | |
391 | return self._avg_rtt | |
364 | The average round-trip time in milliseconds. | |
365 | ||
366 | ''' | |
367 | if not self._rtts: | |
368 | return 0.0 | |
369 | ||
370 | return round(sum(self._rtts) / len(self._rtts), 3) | |
392 | 371 | |
393 | 372 | @property |
394 | 373 | def max_rtt(self): |
395 | 374 | ''' |
396 | The maximum round-trip time. | |
397 | ||
398 | ''' | |
399 | return self._max_rtt | |
375 | The maximum round-trip time in milliseconds. | |
376 | ||
377 | ''' | |
378 | if not self._rtts: | |
379 | return 0.0 | |
380 | ||
381 | return round(max(self._rtts), 3) | |
382 | ||
383 | @property | |
384 | def rtts(self): | |
385 | ''' | |
386 | The list of round-trip times expressed in milliseconds. | |
387 | ||
388 | ''' | |
389 | return self._rtts | |
400 | 390 | |
401 | 391 | @property |
402 | 392 | def packets_sent(self): |
404 | 394 | The number of packets transmitted to the destination host. |
405 | 395 | |
406 | 396 | ''' |
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 | ||
419 | 397 | return self._packets_sent |
420 | 398 | |
421 | 399 | @property |
425 | 403 | the current host. |
426 | 404 | |
427 | 405 | ''' |
428 | return self._packets_received | |
429 | ||
430 | @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 | |
406 | return len(self._rtts) | |
441 | 407 | |
442 | 408 | @property |
443 | 409 | def packet_loss(self): |
449 | 415 | if not self._packets_sent: |
450 | 416 | return 0.0 |
451 | 417 | |
452 | return 1 - self._packets_received / self._packets_sent | |
418 | return round(1 - len(self._rtts) / self._packets_sent, 2) | |
419 | ||
420 | @property | |
421 | def jitter(self): | |
422 | ''' | |
423 | The jitter in milliseconds, defined as the variance of the | |
424 | latency of packets flowing through the network. | |
425 | ||
426 | At least two ICMP responses are required to calculate the | |
427 | jitter. | |
428 | ||
429 | ''' | |
430 | sum_deltas = 0.0 | |
431 | num_deltas = len(self._rtts) - 1 | |
432 | ||
433 | if num_deltas < 1: | |
434 | return 0.0 | |
435 | ||
436 | for i in range(num_deltas): | |
437 | sum_deltas += abs(self._rtts[i] - self._rtts[i + 1]) | |
438 | ||
439 | return round(sum_deltas / num_deltas, 3) | |
453 | 440 | |
454 | 441 | @property |
455 | 442 | def is_alive(self): |
456 | 443 | ''' |
457 | Indicate whether the host is reachable. Return a `boolean`. | |
458 | ||
459 | ''' | |
460 | return self._packets_received > 0 | |
444 | Indicate whether the host is reachable. | |
445 | Return a `boolean`. | |
446 | ||
447 | ''' | |
448 | return len(self._rtts) > 0 | |
461 | 449 | |
462 | 450 | |
463 | 451 | class Hop(Host): |
466 | 454 | some features for the `traceroute` function. |
467 | 455 | |
468 | 456 | :type address: str |
469 | :param address: The IP address of the gateway or host that | |
470 | responded to the request. | |
471 | ||
472 | :type min_rtt: float | |
473 | :param min_rtt: The minimum round-trip time. | |
474 | ||
475 | :type avg_rtt: float | |
476 | :param avg_rtt: The average round-trip time. | |
477 | ||
478 | :type max_rtt: float | |
479 | :param max_rtt: The maximum round-trip time. | |
457 | :param address: The IP address of the gateway or host that responded | |
458 | to the request. | |
480 | 459 | |
481 | 460 | :type packets_sent: int |
482 | 461 | :param packets_sent: The number of packets transmitted to the |
483 | 462 | destination host. |
484 | 463 | |
485 | :type packets_received: int | |
486 | :param packets_received: The number of packets sent by the remote | |
487 | host and received by the current host. | |
464 | :type rtts: list[float] | |
465 | :param rtts: The list of round-trip times expressed in milliseconds. | |
488 | 466 | |
489 | 467 | :type distance: int |
490 | :param distance: The distance (in terms of hops) that separates the | |
468 | :param distance: The distance, in terms of hops, that separates the | |
491 | 469 | remote host from the current machine. |
492 | 470 | |
493 | 471 | ''' |
494 | def __init__(self, address, min_rtt, avg_rtt, max_rtt, | |
495 | packets_sent, packets_received, distance): | |
496 | ||
497 | super().__init__(address, min_rtt, avg_rtt, max_rtt, | |
498 | packets_sent, packets_received) | |
499 | ||
472 | __slots__ = '_address', '_packets_sent', '_rtts', '_distance' | |
473 | ||
474 | def __init__(self, address, packets_sent, rtts, distance): | |
475 | super().__init__(address, packets_sent, rtts) | |
500 | 476 | self._distance = distance |
501 | 477 | |
502 | 478 | def __repr__(self): |
503 | 479 | return f'<Hop {self._distance} [{self._address}]>' |
504 | 480 | |
481 | def __str__(self): | |
482 | return f' #{self._distance:<2} ' + super().__str__()[2:] | |
483 | ||
505 | 484 | @property |
506 | 485 | def distance(self): |
507 | 486 | ''' |
508 | The distance (in terms of hops) that separates the remote host | |
487 | The distance, in terms of hops, that separates the remote host | |
509 | 488 | from the current machine. |
510 | 489 | |
511 | 490 | ''' |
0 | ''' | |
1 | icmplib | |
2 | ~~~~~~~ | |
3 | ||
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
7 | https://github.com/ValentinBELYN/icmplib | |
8 | ||
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
10 | :license: GNU LGPLv3, see the LICENSE for details. | |
11 | ||
12 | ~~~~~~~ | |
13 | ||
14 | This program is free software: you can redistribute it and/or | |
15 | modify it under the terms of the GNU Lesser General Public License | |
16 | as published by the Free Software Foundation, either version 3 of | |
17 | the License, or (at your option) any later version. | |
18 | ||
19 | This program is distributed in the hope that it will be useful, | |
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | GNU Lesser General Public License for more details. | |
23 | ||
24 | You should have received a copy of the GNU Lesser General Public | |
25 | License along with this program. If not, see | |
26 | <https://www.gnu.org/licenses/>. | |
27 | ''' | |
28 | ||
29 | import asyncio | |
30 | ||
31 | from .ping import async_ping | |
32 | ||
33 | ||
34 | async def async_multiping(addresses, count=2, interval=0.5, timeout=2, | |
35 | concurrent_tasks=50, source=None, family=None, privileged=True, | |
36 | **kwargs): | |
37 | ''' | |
38 | Send ICMP Echo Request packets to several network hosts. | |
39 | ||
40 | This function is non-blocking. | |
41 | ||
42 | :type addresses: str | |
43 | :param addresses: The IP addresses of the hosts to which messages | |
44 | should be sent. Hostnames and FQDNs are allowed but not | |
45 | recommended. You can easily retrieve their IP address by calling | |
46 | the built-in `resolve` function. | |
47 | ||
48 | :type count: int, optional | |
49 | :param count: The number of ping to perform per address. | |
50 | Default to 2. | |
51 | ||
52 | :type interval: int or float, optional | |
53 | :param interval: The interval in seconds between sending each | |
54 | packet. Default to 0.5. | |
55 | ||
56 | :type timeout: int or float, optional | |
57 | :param timeout: The maximum waiting time for receiving a reply in | |
58 | seconds. Default to 2. | |
59 | ||
60 | :type concurrent_tasks: int, optional | |
61 | :param concurrent_tasks: The maximum number of concurrent tasks to | |
62 | speed up processing. This value cannot exceed the maximum number | |
63 | of file descriptors configured on the operating system. | |
64 | Default to 50. | |
65 | ||
66 | :type source: str, optional | |
67 | :param source: The IP address from which you want to send packets. | |
68 | By default, the interface is automatically chosen according to | |
69 | the specified destinations. This parameter should not be used if | |
70 | you are passing both IPv4 and IPv6 addresses to this function. | |
71 | ||
72 | :type family: int, optional | |
73 | :param family: The address family if a hostname or FQDN is specified. | |
74 | Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, | |
75 | this function searches for IPv4 addresses first before searching | |
76 | for IPv6 addresses. | |
77 | ||
78 | :type privileged: bool, optional | |
79 | :param privileged: When this option is enabled, this library fully | |
80 | manages the exchanges and the structure of ICMP packets. | |
81 | Disable this option if you want to use this function without | |
82 | root privileges and let the kernel handle ICMP headers. | |
83 | Default to True. | |
84 | Only available on Unix systems. Ignored on Windows. | |
85 | ||
86 | Advanced (**kwags): | |
87 | ||
88 | :type payload: bytes, optional | |
89 | :param payload: The payload content in bytes. A random payload is | |
90 | used by default. | |
91 | ||
92 | :type payload_size: int, optional | |
93 | :param payload_size: The payload size. Ignored when the `payload` | |
94 | parameter is set. Default to 56. | |
95 | ||
96 | :type traffic_class: int, optional | |
97 | :param traffic_class: The traffic class of ICMP packets. | |
98 | Provides a defined level of service to packets by setting the | |
99 | DS Field (formerly TOS) or the Traffic Class field of IP | |
100 | headers. Packets are delivered with the minimum priority by | |
101 | default (Best-effort delivery). | |
102 | Intermediate routers must be able to support this feature. | |
103 | Only available on Unix systems. Ignored on Windows. | |
104 | ||
105 | :rtype: list[Host] | |
106 | :returns: A list of `Host` objects containing statistics about the | |
107 | desired destinations. The list is sorted in the same order as | |
108 | the addresses passed in parameters. | |
109 | ||
110 | :raises NameLookupError: If you pass a hostname or FQDN in | |
111 | parameters and it does not exist or cannot be resolved. | |
112 | :raises SocketPermissionError: If the privileges are insufficient to | |
113 | create the socket. | |
114 | :raises SocketAddressError: If the source address cannot be assigned | |
115 | to the socket. | |
116 | :raises ICMPSocketError: If another error occurs. See the | |
117 | `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
118 | ||
119 | Usage:: | |
120 | ||
121 | >>> import asyncio | |
122 | >>> from icmplib import async_multiping | |
123 | >>> hosts = asyncio.run(async_multiping(['10.0.0.5', '::1'])) | |
124 | ||
125 | >>> for host in hosts: | |
126 | ... if host.is_alive: | |
127 | ... print(f'{host.address} is up!') | |
128 | ... else: | |
129 | ... print(f'{host.address} is down!') | |
130 | ||
131 | 10.0.0.5 is down! | |
132 | ::1 is up! | |
133 | ||
134 | See the `Host` class for details. | |
135 | ||
136 | ''' | |
137 | loop = asyncio.get_running_loop() | |
138 | tasks = [] | |
139 | tasks_pending = set() | |
140 | ||
141 | for address in addresses: | |
142 | if len(tasks_pending) >= concurrent_tasks: | |
143 | _, tasks_pending = await asyncio.wait( | |
144 | tasks_pending, | |
145 | return_when=asyncio.FIRST_COMPLETED) | |
146 | ||
147 | task = loop.create_task( | |
148 | async_ping( | |
149 | address=address, | |
150 | count=count, | |
151 | interval=interval, | |
152 | timeout=timeout, | |
153 | source=source, | |
154 | family=family, | |
155 | privileged=privileged, | |
156 | **kwargs)) | |
157 | ||
158 | tasks.append(task) | |
159 | tasks_pending.add(task) | |
160 | ||
161 | await asyncio.wait(tasks_pending) | |
162 | ||
163 | return [task.result() for task in tasks] | |
164 | ||
165 | ||
166 | def multiping(addresses, count=2, interval=0.5, timeout=2, | |
167 | concurrent_tasks=50, source=None, family=None, privileged=True, | |
168 | **kwargs): | |
169 | ''' | |
170 | Send ICMP Echo Request packets to several network hosts. | |
171 | ||
172 | :type addresses: str | |
173 | :param addresses: The IP addresses of the hosts to which messages | |
174 | should be sent. Hostnames and FQDNs are allowed but not | |
175 | recommended. You can easily retrieve their IP address by calling | |
176 | the built-in `resolve` function. | |
177 | ||
178 | :type count: int, optional | |
179 | :param count: The number of ping to perform per address. | |
180 | Default to 2. | |
181 | ||
182 | :type interval: int or float, optional | |
183 | :param interval: The interval in seconds between sending each | |
184 | packet. Default to 0.5. | |
185 | ||
186 | :type timeout: int or float, optional | |
187 | :param timeout: The maximum waiting time for receiving a reply in | |
188 | seconds. Default to 2. | |
189 | ||
190 | :type concurrent_tasks: int, optional | |
191 | :param concurrent_tasks: The maximum number of concurrent tasks to | |
192 | speed up processing. This value cannot exceed the maximum number | |
193 | of file descriptors configured on the operating system. | |
194 | Default to 50. | |
195 | ||
196 | :type source: str, optional | |
197 | :param source: The IP address from which you want to send packets. | |
198 | By default, the interface is automatically chosen according to | |
199 | the specified destinations. This parameter should not be used if | |
200 | you are passing both IPv4 and IPv6 addresses to this function. | |
201 | ||
202 | :type family: int, optional | |
203 | :param family: The address family if a hostname or FQDN is specified. | |
204 | Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, | |
205 | this function searches for IPv4 addresses first before searching | |
206 | for IPv6 addresses. | |
207 | ||
208 | :type privileged: bool, optional | |
209 | :param privileged: When this option is enabled, this library fully | |
210 | manages the exchanges and the structure of ICMP packets. | |
211 | Disable this option if you want to use this function without | |
212 | root privileges and let the kernel handle ICMP headers. | |
213 | Default to True. | |
214 | Only available on Unix systems. Ignored on Windows. | |
215 | ||
216 | Advanced (**kwags): | |
217 | ||
218 | :type payload: bytes, optional | |
219 | :param payload: The payload content in bytes. A random payload is | |
220 | used by default. | |
221 | ||
222 | :type payload_size: int, optional | |
223 | :param payload_size: The payload size. Ignored when the `payload` | |
224 | parameter is set. Default to 56. | |
225 | ||
226 | :type traffic_class: int, optional | |
227 | :param traffic_class: The traffic class of ICMP packets. | |
228 | Provides a defined level of service to packets by setting the | |
229 | DS Field (formerly TOS) or the Traffic Class field of IP | |
230 | headers. Packets are delivered with the minimum priority by | |
231 | default (Best-effort delivery). | |
232 | Intermediate routers must be able to support this feature. | |
233 | Only available on Unix systems. Ignored on Windows. | |
234 | ||
235 | :rtype: list[Host] | |
236 | :returns: A list of `Host` objects containing statistics about the | |
237 | desired destinations. The list is sorted in the same order as | |
238 | the addresses passed in parameters. | |
239 | ||
240 | :raises NameLookupError: If you pass a hostname or FQDN in | |
241 | parameters and it does not exist or cannot be resolved. | |
242 | :raises SocketPermissionError: If the privileges are insufficient to | |
243 | create the socket. | |
244 | :raises SocketAddressError: If the source address cannot be assigned | |
245 | to the socket. | |
246 | :raises ICMPSocketError: If another error occurs. See the | |
247 | `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
248 | ||
249 | Usage:: | |
250 | ||
251 | >>> from icmplib import multiping | |
252 | >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1']) | |
253 | ||
254 | >>> for host in hosts: | |
255 | ... if host.is_alive: | |
256 | ... print(f'{host.address} is up!') | |
257 | ... else: | |
258 | ... print(f'{host.address} is down!') | |
259 | ||
260 | 10.0.0.5 is down! | |
261 | 127.0.0.1 is up! | |
262 | ::1 is up! | |
263 | ||
264 | See the `Host` class for details. | |
265 | ||
266 | ''' | |
267 | return asyncio.run( | |
268 | async_multiping( | |
269 | addresses=addresses, | |
270 | count=count, | |
271 | interval=interval, | |
272 | timeout=timeout, | |
273 | concurrent_tasks=concurrent_tasks, | |
274 | source=source, | |
275 | family=family, | |
276 | privileged=privileged, | |
277 | **kwargs)) |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |
23 | 26 | <https://www.gnu.org/licenses/>. |
24 | 27 | ''' |
25 | 28 | |
26 | from threading import Thread | |
29 | import asyncio | |
27 | 30 | from time import sleep |
28 | 31 | |
29 | from .sockets import ICMPv4Socket, ICMPv6Socket | |
32 | from .sockets import ICMPv4Socket, ICMPv6Socket, AsyncSocket | |
30 | 33 | from .models import ICMPRequest, Host |
31 | 34 | 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): | |
50 | ''' | |
51 | Send ICMP ECHO_REQUEST packets to a network host. | |
35 | from .utils import * | |
36 | ||
37 | ||
38 | def ping(address, count=4, interval=1, timeout=2, id=None, source=None, | |
39 | family=None, privileged=True, **kwargs): | |
40 | ''' | |
41 | Send ICMP Echo Request packets to a network host. | |
52 | 42 | |
53 | 43 | :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. | |
44 | :param address: The IP address, hostname or FQDN of the host to | |
45 | which messages should be sent. For deterministic behavior, | |
46 | prefer to use an IP address. | |
47 | ||
48 | :type count: int, optional | |
49 | :param count: The number of ping to perform. Default to 4. | |
50 | ||
51 | :type interval: int or float, optional | |
52 | :param interval: The interval in seconds between sending each | |
53 | packet. Default to 1. | |
54 | ||
55 | :type timeout: int or float, optional | |
56 | :param timeout: The maximum waiting time for receiving a reply in | |
57 | seconds. Default to 2. | |
58 | ||
59 | :type id: int, optional | |
60 | :param id: The identifier of ICMP requests. Used to match the | |
61 | responses with requests. In practice, a unique identifier should | |
62 | be used for every ping process. On Linux, this identifier is | |
63 | ignored when the `privileged` parameter is disabled. The library | |
64 | handles this identifier itself by default. | |
65 | ||
66 | :type source: str, optional | |
67 | :param source: The IP address from which you want to send packets. | |
68 | By default, the interface is automatically chosen according to | |
69 | the specified destination. | |
70 | ||
71 | :type family: int, optional | |
72 | :param family: The address family if a hostname or FQDN is specified. | |
73 | Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, | |
74 | this function searches for IPv4 addresses first before searching | |
75 | for IPv6 addresses. | |
76 | ||
77 | :type privileged: bool, optional | |
78 | :param privileged: When this option is enabled, this library fully | |
79 | manages the exchanges and the structure of ICMP packets. | |
80 | Disable this option if you want to use this function without | |
81 | root privileges and let the kernel handle ICMP headers. | |
82 | Default to True. | |
83 | Only available on Unix systems. Ignored on Windows. | |
84 | ||
85 | Advanced (**kwags): | |
86 | ||
87 | :type payload: bytes, optional | |
88 | :param payload: The payload content in bytes. A random payload is | |
89 | used by default. | |
90 | ||
91 | :type payload_size: int, optional | |
92 | :param payload_size: The payload size. Ignored when the `payload` | |
93 | parameter is set. Default to 56. | |
94 | ||
95 | :type traffic_class: int, optional | |
96 | :param traffic_class: The traffic class of ICMP packets. | |
97 | Provides a defined level of service to packets by setting the | |
98 | DS Field (formerly TOS) or the Traffic Class field of IP | |
99 | headers. Packets are delivered with the minimum priority by | |
100 | default (Best-effort delivery). | |
101 | Intermediate routers must be able to support this feature. | |
102 | Only available on Unix systems. Ignored on Windows. | |
75 | 103 | |
76 | 104 | :rtype: Host |
77 | 105 | :returns: A `Host` object containing statistics about the desired |
78 | 106 | destination. |
79 | 107 | |
80 | :raises SocketPermissionError: If the permissions are insufficient | |
81 | to create a socket. | |
108 | :raises NameLookupError: If you pass a hostname or FQDN in | |
109 | parameters and it does not exist or cannot be resolved. | |
110 | :raises SocketPermissionError: If the privileges are insufficient to | |
111 | create the socket. | |
112 | :raises SocketAddressError: If the source address cannot be assigned | |
113 | to the socket. | |
114 | :raises ICMPSocketError: If another error occurs. See the | |
115 | `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
82 | 116 | |
83 | 117 | Usage:: |
84 | 118 | |
85 | 119 | >>> from icmplib import ping |
86 | 120 | >>> host = ping('1.1.1.1') |
87 | ||
88 | 121 | >>> host.avg_rtt |
89 | 122 | 13.2 |
90 | ||
91 | 123 | >>> host.is_alive |
92 | 124 | True |
93 | 125 | |
94 | 126 | See the `Host` class for details. |
95 | 127 | |
96 | 128 | ''' |
97 | address = resolve(address) | |
129 | if is_hostname(address): | |
130 | address = resolve(address, family)[0] | |
98 | 131 | |
99 | 132 | if is_ipv6_address(address): |
100 | socket = ICMPv6Socket() | |
101 | ||
133 | _Socket = ICMPv6Socket | |
102 | 134 | else: |
103 | socket = ICMPv4Socket() | |
104 | ||
135 | _Socket = ICMPv4Socket | |
136 | ||
137 | id = id or unique_identifier() | |
105 | 138 | packets_sent = 0 |
106 | packets_received = 0 | |
107 | ||
108 | min_rtt = float('inf') | |
109 | avg_rtt = 0.0 | |
110 | max_rtt = 0.0 | |
111 | ||
112 | for sequence in range(count): | |
113 | request = ICMPRequest( | |
114 | destination=address, | |
115 | id=id, | |
116 | sequence=sequence, | |
117 | timeout=timeout, | |
118 | **kwargs) | |
119 | ||
120 | try: | |
121 | socket.send(request) | |
122 | packets_sent += 1 | |
123 | ||
124 | reply = socket.receive() | |
125 | reply.raise_for_status() | |
126 | packets_received += 1 | |
127 | ||
128 | round_trip_time = (reply.time - request.time) * 1000 | |
129 | avg_rtt += round_trip_time | |
130 | min_rtt = min(round_trip_time, min_rtt) | |
131 | max_rtt = max(round_trip_time, max_rtt) | |
132 | ||
133 | if sequence < count - 1: | |
139 | rtts = [] | |
140 | ||
141 | with _Socket(source, privileged) as sock: | |
142 | for sequence in range(count): | |
143 | if sequence > 0: | |
134 | 144 | sleep(interval) |
135 | 145 | |
136 | except ICMPLibError: | |
137 | pass | |
138 | ||
139 | if packets_received: | |
140 | avg_rtt /= packets_received | |
141 | ||
146 | request = ICMPRequest( | |
147 | destination=address, | |
148 | id=id, | |
149 | sequence=sequence, | |
150 | **kwargs) | |
151 | ||
152 | try: | |
153 | sock.send(request) | |
154 | packets_sent += 1 | |
155 | ||
156 | reply = sock.receive(request, timeout) | |
157 | reply.raise_for_status() | |
158 | ||
159 | rtt = (reply.time - request.time) * 1000 | |
160 | rtts.append(rtt) | |
161 | ||
162 | except ICMPLibError: | |
163 | pass | |
164 | ||
165 | return Host(address, packets_sent, rtts) | |
166 | ||
167 | ||
168 | async def async_ping(address, count=4, interval=1, timeout=2, id=None, | |
169 | source=None, family=None, privileged=True, **kwargs): | |
170 | ''' | |
171 | Send ICMP Echo Request packets to a network host. | |
172 | ||
173 | This function is non-blocking. | |
174 | ||
175 | :type address: str | |
176 | :param address: The IP address, hostname or FQDN of the host to | |
177 | which messages should be sent. For deterministic behavior, | |
178 | prefer to use an IP address. | |
179 | ||
180 | :type count: int, optional | |
181 | :param count: The number of ping to perform. Default to 4. | |
182 | ||
183 | :type interval: int or float, optional | |
184 | :param interval: The interval in seconds between sending each | |
185 | packet. Default to 1. | |
186 | ||
187 | :type timeout: int or float, optional | |
188 | :param timeout: The maximum waiting time for receiving a reply in | |
189 | seconds. Default to 2. | |
190 | ||
191 | :type id: int, optional | |
192 | :param id: The identifier of ICMP requests. Used to match the | |
193 | responses with requests. In practice, a unique identifier should | |
194 | be used for every ping process. On Linux, this identifier is | |
195 | ignored when the `privileged` parameter is disabled. The library | |
196 | handles this identifier itself by default. | |
197 | ||
198 | :type source: str, optional | |
199 | :param source: The IP address from which you want to send packets. | |
200 | By default, the interface is automatically chosen according to | |
201 | the specified destination. | |
202 | ||
203 | :type family: int, optional | |
204 | :param family: The address family if a hostname or FQDN is specified. | |
205 | Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, | |
206 | this function searches for IPv4 addresses first before searching | |
207 | for IPv6 addresses. | |
208 | ||
209 | :type privileged: bool, optional | |
210 | :param privileged: When this option is enabled, this library fully | |
211 | manages the exchanges and the structure of ICMP packets. | |
212 | Disable this option if you want to use this function without | |
213 | root privileges and let the kernel handle ICMP headers. | |
214 | Default to True. | |
215 | Only available on Unix systems. Ignored on Windows. | |
216 | ||
217 | Advanced (**kwags): | |
218 | ||
219 | :type payload: bytes, optional | |
220 | :param payload: The payload content in bytes. A random payload is | |
221 | used by default. | |
222 | ||
223 | :type payload_size: int, optional | |
224 | :param payload_size: The payload size. Ignored when the `payload` | |
225 | parameter is set. Default to 56. | |
226 | ||
227 | :type traffic_class: int, optional | |
228 | :param traffic_class: The traffic class of ICMP packets. | |
229 | Provides a defined level of service to packets by setting the | |
230 | DS Field (formerly TOS) or the Traffic Class field of IP | |
231 | headers. Packets are delivered with the minimum priority by | |
232 | default (Best-effort delivery). | |
233 | Intermediate routers must be able to support this feature. | |
234 | Only available on Unix systems. Ignored on Windows. | |
235 | ||
236 | :rtype: Host | |
237 | :returns: A `Host` object containing statistics about the desired | |
238 | destination. | |
239 | ||
240 | :raises NameLookupError: If you pass a hostname or FQDN in | |
241 | parameters and it does not exist or cannot be resolved. | |
242 | :raises SocketPermissionError: If the privileges are insufficient to | |
243 | create the socket. | |
244 | :raises SocketAddressError: If the source address cannot be assigned | |
245 | to the socket. | |
246 | :raises ICMPSocketError: If another error occurs. See the | |
247 | `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
248 | ||
249 | Usage:: | |
250 | ||
251 | >>> import asyncio | |
252 | >>> from icmplib import async_ping | |
253 | >>> host = asyncio.run(async_ping('1.1.1.1')) | |
254 | >>> host.avg_rtt | |
255 | 13.2 | |
256 | >>> host.is_alive | |
257 | True | |
258 | ||
259 | See the `Host` class for details. | |
260 | ||
261 | ''' | |
262 | if is_hostname(address): | |
263 | address = (await async_resolve(address, family))[0] | |
264 | ||
265 | if is_ipv6_address(address): | |
266 | _Socket = ICMPv6Socket | |
142 | 267 | else: |
143 | min_rtt = 0.0 | |
144 | ||
145 | host = Host( | |
146 | address=address, | |
147 | min_rtt=min_rtt, | |
148 | avg_rtt=avg_rtt, | |
149 | max_rtt=max_rtt, | |
150 | packets_sent=packets_sent, | |
151 | packets_received=packets_received) | |
152 | ||
153 | socket.close() | |
154 | ||
155 | return host | |
156 | ||
157 | ||
158 | def multiping(addresses, count=2, interval=1, timeout=2, id=PID, | |
159 | max_threads=10, **kwargs): | |
160 | ''' | |
161 | Send ICMP ECHO_REQUEST packets to multiple network hosts. | |
162 | ||
163 | :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. | |
188 | ||
189 | :rtype: list of Host | |
190 | :returns: A list of `Host` objects containing statistics about the | |
191 | desired destinations. The list is sorted in the same order as | |
192 | the addresses passed in parameters. | |
193 | ||
194 | :raises SocketPermissionError: If the permissions are insufficient | |
195 | to create a socket. | |
196 | ||
197 | Usage:: | |
198 | ||
199 | >>> from icmplib import multiping | |
200 | >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1']) | |
201 | ||
202 | >>> for host in hosts: | |
203 | ... if host.is_alive: | |
204 | ... print(f'{host.address} is alive!') | |
205 | ... | |
206 | ... else: | |
207 | ... print(f'{host.address} is dead!') | |
208 | ... | |
209 | 10.0.0.5 is dead! | |
210 | 127.0.0.1 is alive! | |
211 | ::1 is alive! | |
212 | ||
213 | See the `Host` class for details. | |
214 | ||
215 | ''' | |
216 | hosts = [] | |
217 | inactive_threads = [] | |
218 | active_threads = [] | |
219 | ||
220 | for i, address in enumerate(addresses): | |
221 | thread = PingThread( | |
222 | 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) | |
245 | ||
246 | return hosts | |
268 | _Socket = ICMPv4Socket | |
269 | ||
270 | id = id or unique_identifier() | |
271 | packets_sent = 0 | |
272 | rtts = [] | |
273 | ||
274 | with AsyncSocket(_Socket(source, privileged)) as sock: | |
275 | for sequence in range(count): | |
276 | if sequence > 0: | |
277 | await asyncio.sleep(interval) | |
278 | ||
279 | request = ICMPRequest( | |
280 | destination=address, | |
281 | id=id, | |
282 | sequence=sequence, | |
283 | **kwargs) | |
284 | ||
285 | try: | |
286 | sock.send(request) | |
287 | packets_sent += 1 | |
288 | ||
289 | reply = await sock.receive(request, timeout) | |
290 | reply.raise_for_status() | |
291 | ||
292 | rtt = (reply.time - request.time) * 1000 | |
293 | rtts.append(rtt) | |
294 | ||
295 | except ICMPLibError: | |
296 | pass | |
297 | ||
298 | return Host(address, packets_sent, rtts) |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |
23 | 26 | <https://www.gnu.org/licenses/>. |
24 | 27 | ''' |
25 | 28 | |
26 | import socket | |
29 | import socket, asyncio | |
27 | 30 | from struct import pack, unpack |
28 | 31 | from time import time |
29 | 32 | |
30 | from .ip import IPv4Socket, IPv6Socket | |
31 | from .models import ICMPRequest, ICMPReply | |
33 | from .models import ICMPReply | |
32 | 34 | 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 | |
35 | from .utils import * | |
116 | 36 | |
117 | 37 | |
118 | 38 | class ICMPSocket: |
119 | 39 | ''' |
120 | 40 | Base class for ICMP sockets. |
121 | 41 | |
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 | |
127 | to create the socket. | |
42 | :type address: str, optional | |
43 | :param address: The IP address from which you want to listen and | |
44 | send packets. By default, the socket listens on all interfaces. | |
45 | ||
46 | :type privileged: bool, optional | |
47 | :param privileged: When this option is enabled, the socket fully | |
48 | manages the exchanges and the structure of the ICMP packets. | |
49 | Disable this option if you want to instantiate and use the | |
50 | socket without root privileges and let the kernel handle ICMP | |
51 | headers. Default to True. | |
52 | Only available on Unix systems. Ignored on Windows. | |
53 | ||
54 | :raises SocketPermissionError: If the privileges are insufficient to | |
55 | create the socket. | |
56 | :raises SocketAddressError: If the requested address cannot be | |
57 | assigned to the socket. | |
58 | :raises ICMPSocketError: If another error occurs while creating the | |
59 | socket. | |
128 | 60 | |
129 | 61 | ''' |
130 | def __init__(self, config): | |
131 | self._socket = None | |
132 | self._config = config | |
133 | self._last_request = None | |
62 | __slots__ = '_sock', '_address', '_privileged' | |
63 | ||
64 | _IP_VERSION = -1 | |
65 | _ICMP_HEADER_OFFSET = -1 | |
66 | _ICMP_HEADER_REAL_OFFSET = -1 | |
67 | ||
68 | _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1 | |
69 | _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2 | |
70 | _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4 | |
71 | _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6 | |
72 | _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8 | |
73 | ||
74 | _ICMP_ECHO_REQUEST = -1 | |
75 | _ICMP_ECHO_REPLY = -1 | |
76 | ||
77 | def __init__(self, address=None, privileged=True): | |
78 | self._sock = None | |
79 | self._address = address | |
80 | ||
81 | # The Linux kernel allows unprivileged users to use datagram | |
82 | # sockets (SOCK_DGRAM) to send ICMP requests. This feature is | |
83 | # now supported by the majority of Unix systems. | |
84 | # Windows is not compatible. | |
85 | self._privileged = privileged or PLATFORM_WINDOWS | |
134 | 86 | |
135 | 87 | try: |
136 | self._socket = config.IP_SOCKET( | |
137 | config.IP_PROTOCOL) | |
138 | ||
139 | except OSError: | |
140 | raise SocketPermissionError | |
88 | self._sock = self._create_socket( | |
89 | socket.SOCK_RAW if self._privileged else | |
90 | socket.SOCK_DGRAM) | |
91 | ||
92 | if address: | |
93 | self._sock.bind((address, 0)) | |
94 | ||
95 | except OSError as err: | |
96 | if err.errno in (1, 13, 10013): | |
97 | raise SocketPermissionError | |
98 | ||
99 | if err.errno in (-9, 49, 99, 10049, 11001): | |
100 | raise SocketAddressError(address) | |
101 | ||
102 | raise ICMPSocketError(str(err)) | |
103 | ||
104 | def __enter__(self): | |
105 | ''' | |
106 | Return this object. | |
107 | ||
108 | ''' | |
109 | return self | |
110 | ||
111 | def __exit__(self, type, value, traceback): | |
112 | ''' | |
113 | Call the `close` method. | |
114 | ||
115 | ''' | |
116 | self.close() | |
141 | 117 | |
142 | 118 | def __del__(self): |
143 | 119 | ''' |
146 | 122 | ''' |
147 | 123 | self.close() |
148 | 124 | |
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) | |
125 | def _create_socket(self, type): | |
126 | ''' | |
127 | Create and return a new socket. Must be overridden. | |
128 | ||
129 | ''' | |
130 | raise NotImplementedError | |
131 | ||
132 | def _set_ttl(self, ttl): | |
133 | ''' | |
134 | Set the time to live of every IP packet originating from this | |
135 | socket. Must be overridden. | |
136 | ||
137 | ''' | |
138 | raise NotImplementedError | |
139 | ||
140 | def _set_traffic_class(self, traffic_class): | |
141 | ''' | |
142 | Set the DS Field (formerly TOS) or the Traffic Class field of | |
143 | every IP packet originating from this socket. Must be | |
144 | overridden. | |
145 | ||
146 | ''' | |
147 | raise NotImplementedError | |
157 | 148 | |
158 | 149 | def _checksum(self, data): |
159 | 150 | ''' |
160 | Calculate the checksum of a packet. | |
161 | ||
162 | ''' | |
151 | Compute the checksum of an ICMP packet. Checksums are used to | |
152 | verify the integrity of packets. | |
153 | ||
154 | ''' | |
155 | sum = 0 | |
163 | 156 | data += b'\x00' |
164 | end = len(data) - 1 | |
165 | sum = 0 | |
166 | ||
167 | for i in range(0, end, 2): | |
157 | ||
158 | for i in range(0, len(data) - 1, 2): | |
168 | 159 | sum += (data[i] << 8) + data[i + 1] |
169 | sum = (sum >> 16) + (sum & 0xffff) | |
160 | sum = (sum & 0xffff) + (sum >> 16) | |
170 | 161 | |
171 | 162 | sum = ~sum & 0xffff |
172 | 163 | |
174 | 165 | |
175 | 166 | def _create_packet(self, id, sequence, payload): |
176 | 167 | ''' |
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) | |
168 | Build an ICMP packet from an identifier, a sequence number and | |
169 | a payload. | |
170 | ||
171 | This method returns the newly created ICMP header concatenated | |
172 | to the payload passed in parameters. | |
173 | ||
174 | ''' | |
175 | checksum = 0 | |
176 | ||
177 | # Temporary ICMP header to compute the checksum | |
178 | header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum, | |
179 | id, sequence) | |
180 | ||
184 | 181 | checksum = self._checksum(header + payload) |
185 | 182 | |
186 | # Definitive header | |
187 | header = self._create_header(type, 0, checksum, id, sequence) | |
183 | # Definitive ICMP header | |
184 | header = pack('!2B3H', self._ICMP_ECHO_REQUEST, 0, checksum, | |
185 | id, sequence) | |
188 | 186 | |
189 | 187 | return header + payload |
190 | 188 | |
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: | |
189 | def _parse_reply(self, packet, source, current_time): | |
190 | ''' | |
191 | Parse an ICMP reply from bytes. | |
192 | ||
193 | This method returns an `ICMPReply` object or `None` if the reply | |
194 | cannot be parsed. | |
195 | ||
196 | ''' | |
197 | # On Linux, the IP header is missing when a datagram socket is | |
198 | # used (SOCK_DGRAM). To keep the same behavior on all operating | |
199 | # systems including macOS which has this header, we add a | |
200 | # padding of the size of the missing IP header. | |
201 | if not self._privileged and PLATFORM_LINUX: | |
202 | packet = b'\x00' * self._ICMP_HEADER_OFFSET + packet | |
203 | ||
204 | bytes_received = len(packet) - self._ICMP_HEADER_OFFSET | |
205 | ||
206 | if len(packet) < self._ICMP_CHECKSUM_OFFSET: | |
202 | 207 | return None |
203 | 208 | |
204 | 209 | 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: | |
210 | self._ICMP_HEADER_OFFSET: | |
211 | self._ICMP_CHECKSUM_OFFSET]) | |
212 | ||
213 | if type != self._ICMP_ECHO_REPLY: | |
214 | packet = packet[ | |
215 | self._ICMP_PAYLOAD_OFFSET | |
216 | - self._ICMP_HEADER_OFFSET | |
217 | + self._ICMP_HEADER_REAL_OFFSET:] | |
218 | ||
219 | if len(packet) < self._ICMP_PAYLOAD_OFFSET: | |
212 | 220 | return None |
213 | 221 | |
214 | 222 | id, sequence = unpack('!2H', packet[ |
215 | self._config.ICMP_ID_OFFSET: | |
216 | self._config.ICMP_PAYLOAD_OFFSET | |
217 | ]) | |
218 | ||
219 | reply = ICMPReply( | |
223 | self._ICMP_ID_OFFSET: | |
224 | self._ICMP_PAYLOAD_OFFSET]) | |
225 | ||
226 | return ICMPReply( | |
220 | 227 | source=source, |
228 | family=self._IP_VERSION, | |
221 | 229 | id=id, |
222 | 230 | sequence=sequence, |
223 | 231 | type=type, |
224 | 232 | code=code, |
225 | 233 | bytes_received=bytes_received, |
226 | time=reply_time) | |
227 | ||
228 | return reply | |
234 | time=current_time) | |
229 | 235 | |
230 | 236 | def send(self, request): |
231 | 237 | ''' |
232 | Send a request to a host. | |
238 | Send an ICMP request message over the network to a remote host. | |
233 | 239 | |
234 | 240 | This operation is non-blocking. Use the `receive` method to get |
235 | 241 | the reply. |
236 | 242 | |
237 | 243 | :type request: ICMPRequest |
238 | :param request: The ICMP request you have created. | |
239 | ||
240 | :raises SocketBroadcastError: If a broadcast address is used | |
241 | and the corresponding option is not enabled on the socket | |
244 | :param request: The ICMP request you have created. If the socket | |
245 | is used in non-privileged mode on a Linux system, the | |
246 | identifier defined in the request will be replaced by the | |
247 | kernel. | |
248 | ||
249 | :raises SocketBroadcastError: If a broadcast address is used and | |
250 | the corresponding option is not enabled on the socket | |
242 | 251 | (ICMPv4 only). |
243 | 252 | :raises SocketUnavailableError: If the socket is closed. |
244 | 253 | :raises ICMPSocketError: If another error occurs while sending. |
245 | 254 | |
246 | 255 | ''' |
247 | if not self._socket: | |
256 | if not self._sock: | |
248 | 257 | raise SocketUnavailableError |
249 | 258 | |
250 | payload = request.payload | |
251 | ||
252 | if not payload: | |
253 | payload = random_byte_message( | |
254 | size=request.payload_size) | |
259 | payload = request.payload or \ | |
260 | random_byte_message(request.payload_size) | |
255 | 261 | |
256 | 262 | packet = self._create_packet( |
257 | 263 | id=request.id, |
258 | 264 | sequence=request.sequence, |
259 | 265 | payload=payload) |
260 | 266 | |
261 | self._socket.ttl = request.ttl | |
262 | self._socket.traffic_class = request.traffic_class | |
263 | ||
264 | request._time = time() | |
265 | ||
266 | 267 | 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 | |
268 | self._set_ttl(request.ttl) | |
269 | self._set_traffic_class(request.traffic_class) | |
270 | ||
271 | request._time = time() | |
272 | self._sock.sendto(packet, (request.destination, 0)) | |
273 | ||
274 | # On Linux, the ICMP request identifier is replaced by the | |
275 | # kernel with a random port number when a datagram socket is | |
276 | # used (SOCK_DGRAM). So, we update the request created by | |
277 | # the user to take this new identifier into account. | |
278 | if not self._privileged and PLATFORM_LINUX: | |
279 | request._id = self._sock.getsockname()[1] | |
276 | 280 | |
277 | 281 | except PermissionError: |
278 | 282 | raise SocketBroadcastError |
280 | 284 | except OSError as err: |
281 | 285 | raise ICMPSocketError(str(err)) |
282 | 286 | |
283 | def receive(self): | |
284 | ''' | |
285 | Receive a reply from a host. | |
287 | def receive(self, request=None, timeout=2): | |
288 | ''' | |
289 | Receive an ICMP reply message from the socket. | |
286 | 290 | |
287 | 291 | This method can be called multiple times if you expect several |
288 | responses (as with a broadcast address). | |
292 | responses as with a broadcast address. | |
293 | ||
294 | :type request: ICMPRequest, optional | |
295 | :param request: The ICMP request to use to match the response. | |
296 | By default, all ICMP packets arriving on the socket are | |
297 | returned. | |
298 | ||
299 | :type timeout: int or float, optional | |
300 | :param timeout: The maximum waiting time for receiving the | |
301 | response in seconds. Default to 2. | |
302 | ||
303 | :rtype: ICMPReply | |
304 | :returns: An `ICMPReply` object representing the response of the | |
305 | desired destination or an upstream gateway. See the | |
306 | `ICMPReply` class for details. | |
289 | 307 | |
290 | 308 | :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. | |
309 | timeout specified in parameters. | |
294 | 310 | :raises SocketUnavailableError: If the socket is closed. |
295 | :raises ICMPSocketError: If another error occurs while | |
296 | receiving. | |
297 | ||
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: | |
311 | :raises ICMPSocketError: If another error occurs while receiving. | |
312 | ||
313 | ''' | |
314 | if not self._sock: | |
306 | 315 | raise SocketUnavailableError |
307 | 316 | |
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 | |
317 | self._sock.settimeout(timeout) | |
318 | time_limit = time() + timeout | |
316 | 319 | |
317 | 320 | try: |
318 | 321 | while True: |
319 | packet, address, port = self._socket.receive() | |
320 | reply_time = time() | |
321 | ||
322 | if reply_time > timeout: | |
322 | response = self._sock.recvfrom(1024) | |
323 | current_time = time() | |
324 | ||
325 | packet = response[0] | |
326 | source = response[1][0] | |
327 | ||
328 | if current_time > time_limit: | |
323 | 329 | raise socket.timeout |
324 | 330 | |
325 | reply = self._read_reply( | |
331 | reply = self._parse_reply( | |
326 | 332 | packet=packet, |
327 | source=address, | |
328 | reply_time=reply_time) | |
329 | ||
330 | if (reply and request.id == reply.id and | |
333 | source=source, | |
334 | current_time=current_time) | |
335 | ||
336 | if (reply and not request or | |
337 | reply and request.id == reply.id and | |
331 | 338 | request.sequence == reply.sequence): |
332 | 339 | return reply |
333 | 340 | |
334 | 341 | except socket.timeout: |
335 | raise TimeoutExceeded(request.timeout) | |
342 | raise TimeoutExceeded(timeout) | |
336 | 343 | |
337 | 344 | except OSError as err: |
338 | 345 | raise ICMPSocketError(str(err)) |
342 | 349 | Close the socket. It cannot be used after this call. |
343 | 350 | |
344 | 351 | ''' |
345 | if self._socket: | |
346 | self._socket.close() | |
347 | self._socket = None | |
352 | if self._sock: | |
353 | self._sock.close() | |
354 | self._sock = None | |
355 | ||
356 | @property | |
357 | def blocking(self): | |
358 | ''' | |
359 | Indicate whether the socket is in blocking mode. | |
360 | Return a `boolean`. | |
361 | ||
362 | ''' | |
363 | return self._sock.getblocking() | |
364 | ||
365 | @blocking.setter | |
366 | def blocking(self, enable): | |
367 | return self._sock.setblocking(enable) | |
368 | ||
369 | @property | |
370 | def address(self): | |
371 | ''' | |
372 | The IP address from which the socket listens and sends packets. | |
373 | Return `None` if the socket listens on all interfaces. | |
374 | ||
375 | ''' | |
376 | return self._address | |
377 | ||
378 | @property | |
379 | def is_privileged(self): | |
380 | ''' | |
381 | Indicate whether the socket is running in privileged mode. | |
382 | Return a `boolean`. | |
383 | ||
384 | ''' | |
385 | return self._privileged | |
348 | 386 | |
349 | 387 | @property |
350 | 388 | def is_closed(self): |
351 | 389 | ''' |
352 | Indicate whether the socket is closed. Return a `boolean`. | |
353 | ||
354 | ''' | |
355 | return self._socket is None | |
390 | Indicate whether the socket is closed. | |
391 | Return a `boolean`. | |
392 | ||
393 | ''' | |
394 | return self._sock is None | |
395 | ||
396 | ||
397 | # Echo Request and Echo Reply messages RFC 792 | |
398 | # | |
399 | # 0 1 2 3 | |
400 | # 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 | |
401 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
402 | # | Type | Code | Checksum | | |
403 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
404 | # | Identifier | Sequence Number | | |
405 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
406 | # | Data ... | |
407 | # +-+-+-+-+- | |
408 | # | |
409 | # ICMPv4 Error message RFC 792 | |
410 | # | |
411 | # 0 1 2 3 | |
412 | # 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 | |
413 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
414 | # | Type | Code | Checksum | | |
415 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
416 | # | Unused / Depends on the error | | |
417 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
418 | # | Internet Header + 64 bits of Original Data Datagram | | |
419 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
356 | 420 | |
357 | 421 | |
358 | 422 | class ICMPv4Socket(ICMPSocket): |
359 | 423 | ''' |
360 | Socket for sending and receiving ICMPv4 packets. | |
361 | ||
362 | :raises SocketPermissionError: If the permissions are insufficient | |
363 | to create the socket. | |
424 | Class for sending and receiving ICMPv4 packets. | |
425 | ||
426 | :type address: str, optional | |
427 | :param address: The IP address from which you want to listen and | |
428 | send packets. By default, the socket listens on all interfaces. | |
429 | ||
430 | :type privileged: bool, optional | |
431 | :param privileged: When this option is enabled, the socket fully | |
432 | manages the exchanges and the structure of the ICMP packets. | |
433 | Disable this option if you want to instantiate and use the | |
434 | socket without root privileges and let the kernel handle ICMP | |
435 | headers. Default to True. | |
436 | Only available on Unix systems. Ignored on Windows. | |
437 | ||
438 | :raises SocketPermissionError: If the privileges are insufficient to | |
439 | create the socket. | |
440 | :raises SocketAddressError: If the requested address cannot be | |
441 | assigned to the socket. | |
442 | :raises ICMPSocketError: If another error occurs while creating the | |
443 | socket. | |
364 | 444 | |
365 | 445 | ''' |
366 | def __init__(self): | |
367 | super().__init__(ICMPv4Config) | |
446 | __slots__ = '_sock', '_address', '_privileged' | |
447 | ||
448 | _IP_VERSION = 4 | |
449 | _ICMP_HEADER_OFFSET = 20 | |
450 | _ICMP_HEADER_REAL_OFFSET = 20 | |
451 | ||
452 | _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1 | |
453 | _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2 | |
454 | _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4 | |
455 | _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6 | |
456 | _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8 | |
457 | ||
458 | _ICMP_ECHO_REQUEST = 8 | |
459 | _ICMP_ECHO_REPLY = 0 | |
460 | ||
461 | def _create_socket(self, type): | |
462 | ''' | |
463 | Create and return a new socket. | |
464 | ||
465 | ''' | |
466 | return socket.socket( | |
467 | family=socket.AF_INET, | |
468 | type=type, | |
469 | proto=socket.IPPROTO_ICMP) | |
470 | ||
471 | def _set_ttl(self, ttl): | |
472 | ''' | |
473 | Set the time to live of every IP packet originating from this | |
474 | socket. | |
475 | ||
476 | ''' | |
477 | self._sock.setsockopt( | |
478 | socket.IPPROTO_IP, | |
479 | socket.IP_TTL, | |
480 | ttl) | |
481 | ||
482 | def _set_traffic_class(self, traffic_class): | |
483 | ''' | |
484 | Set the DS Field (formerly TOS) of every IP packet originating | |
485 | from this socket. | |
486 | ||
487 | Only available on Unix systems. Ignored on Windows. | |
488 | ||
489 | ''' | |
490 | if PLATFORM_WINDOWS: | |
491 | return | |
492 | ||
493 | self._sock.setsockopt( | |
494 | socket.IPPROTO_IP, | |
495 | socket.IP_TOS, | |
496 | traffic_class) | |
368 | 497 | |
369 | 498 | @property |
370 | 499 | def broadcast(self): |
377 | 506 | icmp_socket.broadcast = True |
378 | 507 | |
379 | 508 | ''' |
380 | return self._socket.broadcast | |
509 | return self._sock.getsockopt( | |
510 | socket.SOL_SOCKET, | |
511 | socket.SO_BROADCAST) > 0 | |
381 | 512 | |
382 | 513 | @broadcast.setter |
383 | 514 | def broadcast(self, allow): |
384 | self._socket.broadcast = allow | |
515 | self._sock.setsockopt( | |
516 | socket.SOL_SOCKET, | |
517 | socket.SO_BROADCAST, | |
518 | allow) | |
519 | ||
520 | ||
521 | # Echo Request and Echo Reply messages RFC 4443 | |
522 | # | |
523 | # 0 1 2 3 | |
524 | # 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 | |
525 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
526 | # | Type | Code | Checksum | | |
527 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
528 | # | Identifier | Sequence Number | | |
529 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
530 | # | Data ... | |
531 | # +-+-+-+-+- | |
532 | # | |
533 | # ICMPv6 Error message RFC 4443 | |
534 | # | |
535 | # 0 1 2 3 | |
536 | # 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 | |
537 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
538 | # | Type | Code | Checksum | | |
539 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
540 | # | Unused / Depends on the error | | |
541 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
542 | # | Original packet without exceed the minimum IPv6 MTU | | |
543 | # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
544 | ||
545 | # Windows IPv6 compatibility | |
546 | if PLATFORM_WINDOWS: socket.IPPROTO_IPV6 = 41 | |
385 | 547 | |
386 | 548 | |
387 | 549 | class ICMPv6Socket(ICMPSocket): |
388 | 550 | ''' |
389 | Socket for sending and receiving ICMPv6 packets. | |
390 | ||
391 | :raises SocketPermissionError: If the permissions are insufficient | |
392 | to create the socket. | |
551 | Class for sending and receiving ICMPv6 packets. | |
552 | ||
553 | :type address: str, optional | |
554 | :param address: The IP address from which you want to listen and | |
555 | send packets. By default, the socket listens on all interfaces. | |
556 | ||
557 | :type privileged: bool, optional | |
558 | :param privileged: When this option is enabled, the socket fully | |
559 | manages the exchanges and the structure of the ICMP packets. | |
560 | Disable this option if you want to instantiate and use the | |
561 | socket without root privileges and let the kernel handle ICMP | |
562 | headers. Default to True. | |
563 | Only available on Unix systems. Ignored on Windows. | |
564 | ||
565 | :raises SocketPermissionError: If the privileges are insufficient to | |
566 | create the socket. | |
567 | :raises SocketAddressError: If the requested address cannot be | |
568 | assigned to the socket. | |
569 | :raises ICMPSocketError: If another error occurs while creating the | |
570 | socket. | |
393 | 571 | |
394 | 572 | ''' |
395 | def __init__(self): | |
396 | super().__init__(ICMPv6Config) | |
573 | __slots__ = '_sock', '_address', '_privileged' | |
574 | ||
575 | _IP_VERSION = 6 | |
576 | _ICMP_HEADER_OFFSET = 0 | |
577 | _ICMP_HEADER_REAL_OFFSET = 40 | |
578 | ||
579 | _ICMP_CODE_OFFSET = _ICMP_HEADER_OFFSET + 1 | |
580 | _ICMP_CHECKSUM_OFFSET = _ICMP_HEADER_OFFSET + 2 | |
581 | _ICMP_ID_OFFSET = _ICMP_HEADER_OFFSET + 4 | |
582 | _ICMP_SEQUENCE_OFFSET = _ICMP_HEADER_OFFSET + 6 | |
583 | _ICMP_PAYLOAD_OFFSET = _ICMP_HEADER_OFFSET + 8 | |
584 | ||
585 | _ICMP_ECHO_REQUEST = 128 | |
586 | _ICMP_ECHO_REPLY = 129 | |
587 | ||
588 | def _create_socket(self, type): | |
589 | ''' | |
590 | Create and return a new socket. | |
591 | ||
592 | ''' | |
593 | return socket.socket( | |
594 | family=socket.AF_INET6, | |
595 | type=type, | |
596 | proto=socket.IPPROTO_ICMPV6) | |
597 | ||
598 | def _set_ttl(self, ttl): | |
599 | ''' | |
600 | Set the time to live of every IP packet originating from this | |
601 | socket. | |
602 | ||
603 | ''' | |
604 | self._sock.setsockopt( | |
605 | socket.IPPROTO_IPV6, | |
606 | socket.IPV6_UNICAST_HOPS, | |
607 | ttl) | |
608 | ||
609 | def _set_traffic_class(self, traffic_class): | |
610 | ''' | |
611 | Set the Traffic Class field of every IP packet originating from | |
612 | this socket. | |
613 | ||
614 | Only available on Unix systems. Ignored on Windows. | |
615 | ||
616 | ''' | |
617 | if PLATFORM_WINDOWS: | |
618 | return | |
619 | ||
620 | self._sock.setsockopt( | |
621 | socket.IPPROTO_IPV6, | |
622 | socket.IPV6_TCLASS, | |
623 | traffic_class) | |
624 | ||
625 | ||
626 | class AsyncSocket: | |
627 | ''' | |
628 | A wrapper for ICMP sockets which makes them asynchronous. | |
629 | ||
630 | :type icmp_sock: ICMPSocket | |
631 | :param icmp_sock: An ICMP socket. Once the wrapper is instantiated, | |
632 | this socket should no longer be used directly. | |
633 | ||
634 | ''' | |
635 | __slots__ = '_icmp_sock' | |
636 | ||
637 | def __init__(self, icmp_sock): | |
638 | self._icmp_sock = icmp_sock | |
639 | self._icmp_sock.blocking = False | |
640 | ||
641 | def __getattr__(self, name): | |
642 | if not self._icmp_sock: | |
643 | raise SocketUnavailableError | |
644 | ||
645 | return getattr(self._icmp_sock, name) | |
646 | ||
647 | def __enter__(self): | |
648 | ''' | |
649 | Return this object. | |
650 | ||
651 | ''' | |
652 | return self | |
653 | ||
654 | def __exit__(self, type, value, traceback): | |
655 | ''' | |
656 | Call the `close` method. | |
657 | ||
658 | ''' | |
659 | self.close() | |
660 | ||
661 | def __del__(self): | |
662 | ''' | |
663 | Call the `close` method. | |
664 | ||
665 | ''' | |
666 | self.close() | |
667 | ||
668 | async def receive(self, request=None, timeout=2): | |
669 | ''' | |
670 | Receive an ICMP reply message from the socket. | |
671 | ||
672 | This method can be called multiple times if you expect several | |
673 | responses as with a broadcast address. | |
674 | ||
675 | This method is non-blocking. | |
676 | ||
677 | :type request: ICMPRequest, optional | |
678 | :param request: The ICMP request to use to match the response. | |
679 | By default, all ICMP packets arriving on the socket are | |
680 | returned. | |
681 | ||
682 | :type timeout: int or float, optional | |
683 | :param timeout: The maximum waiting time for receiving the | |
684 | response in seconds. Default to 2. | |
685 | ||
686 | :rtype: ICMPReply | |
687 | :returns: An `ICMPReply` object representing the response of the | |
688 | desired destination or an upstream gateway. See the | |
689 | `ICMPReply` class for details. | |
690 | Unlike the `reveive` method of synchronous ICMP sockets, the | |
691 | returned `ICMPReply` object does not specify the IP address | |
692 | of the host that replied to the request message. | |
693 | ||
694 | :raises TimeoutExceeded: If no response is received before the | |
695 | timeout specified in parameters. | |
696 | :raises SocketUnavailableError: If the socket is closed. | |
697 | :raises ICMPSocketError: If another error occurs while receiving. | |
698 | ||
699 | ''' | |
700 | if not self._icmp_sock or not self._icmp_sock._sock: | |
701 | raise SocketUnavailableError | |
702 | ||
703 | loop = asyncio.get_running_loop() | |
704 | time_limit = time() + timeout | |
705 | remaining_time = timeout | |
706 | ||
707 | try: | |
708 | while True: | |
709 | packet = await asyncio.wait_for( | |
710 | loop.sock_recv(self._icmp_sock._sock, 1024), | |
711 | remaining_time) | |
712 | ||
713 | current_time = time() | |
714 | ||
715 | if current_time > time_limit: | |
716 | raise asyncio.TimeoutError | |
717 | ||
718 | reply = self._parse_reply( | |
719 | packet=packet, | |
720 | source=None, | |
721 | current_time=current_time) | |
722 | ||
723 | if (reply and not request or | |
724 | reply and request.id == reply.id and | |
725 | request.sequence == reply.sequence): | |
726 | return reply | |
727 | ||
728 | remaining_time = time_limit - current_time | |
729 | ||
730 | except asyncio.TimeoutError: | |
731 | raise TimeoutExceeded(timeout) | |
732 | ||
733 | except OSError as err: | |
734 | raise ICMPSocketError(str(err)) | |
735 | ||
736 | def detach(self): | |
737 | ''' | |
738 | Detach the socket from the wrapper and return it. The wrapper | |
739 | cannot be used after this call but the socket can be reused for | |
740 | other purposes. | |
741 | ||
742 | ''' | |
743 | icmp_sock = self._icmp_sock | |
744 | ||
745 | if self._icmp_sock: | |
746 | self._icmp_sock = None | |
747 | ||
748 | return icmp_sock | |
749 | ||
750 | def close(self): | |
751 | ''' | |
752 | Detach the underlying socket from the wrapper and close it. Both | |
753 | cannot be used after this call. | |
754 | ||
755 | ''' | |
756 | if self._icmp_sock: | |
757 | self.detach().close() | |
758 | ||
759 | @property | |
760 | def is_closed(self): | |
761 | ''' | |
762 | Indicate whether the underlying socket is closed or detached | |
763 | from this wrapper. Return a `boolean`. | |
764 | ||
765 | ''' | |
766 | return self._icmp_sock is None |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |
28 | 31 | from .sockets import ICMPv4Socket, ICMPv6Socket |
29 | 32 | from .models import ICMPRequest, Hop |
30 | 33 | from .exceptions import * |
31 | from .utils import PID, resolve, is_ipv6_address | |
32 | ||
33 | ||
34 | def traceroute(address, count=3, interval=0.05, timeout=2, id=PID, | |
35 | traffic_class=0, max_hops=30, fast_mode=False, **kwargs): | |
34 | from .utils import * | |
35 | ||
36 | ||
37 | def traceroute(address, count=2, interval=0.05, timeout=2, first_hop=1, | |
38 | max_hops=30, fast=False, id=None, source=None, family=None, | |
39 | **kwargs): | |
36 | 40 | ''' |
37 | 41 | Determine the route to a destination host. |
38 | 42 | |
39 | 43 | The Internet is a large and complex aggregation of network hardware, |
40 | 44 | 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. | |
45 | follow can be difficult. This function uses the IP protocol time to | |
46 | live field and attempts to elicit an ICMP Time Exceeded response | |
47 | from each gateway along the path to some host. | |
48 | ||
49 | This function requires root privileges to run. | |
44 | 50 | |
45 | 51 | :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. | |
52 | :param address: The IP address, hostname or FQDN of the host to | |
53 | reach. For deterministic behavior, prefer to use an IP address. | |
54 | ||
55 | :type count: int, optional | |
56 | :param count: The number of ping to perform per hop. Default to 2. | |
57 | ||
58 | :type interval: int or float, optional | |
59 | :param interval: The interval in seconds between sending each | |
60 | packet. Default to 0.05. | |
61 | ||
62 | :type timeout: int or float, optional | |
63 | :param timeout: The maximum waiting time for receiving a reply in | |
64 | seconds. Default to 2. | |
65 | ||
66 | :type first_hop: int, optional | |
67 | :param first_hop: The initial time to live value used in outgoing | |
68 | probe packets. Default to 1. | |
69 | ||
70 | :type max_hops: int, optional | |
71 | :param max_hops: The maximum time to live (max number of hops) used | |
72 | in outgoing probe packets. Default to 30. | |
73 | ||
74 | :type fast: bool, optional | |
75 | :param fast: When this option is enabled and an intermediate router | |
76 | has been reached, skip to the next hop rather than perform | |
77 | additional requests. The `count` parameter then becomes the | |
78 | maximum number of requests in the event of no response. | |
79 | Default to False. | |
80 | ||
81 | :type id: int, optional | |
82 | :param id: The identifier of ICMP requests. Used to match the | |
83 | responses with requests. In practice, a unique identifier should | |
84 | be used for every traceroute process. The library handles this | |
85 | identifier itself by default. | |
86 | ||
87 | :type source: str, optional | |
88 | :param source: The IP address from which you want to send packets. | |
89 | By default, the interface is automatically chosen according to | |
90 | the specified destination. | |
91 | ||
92 | :type family: int, optional | |
93 | :param family: The address family if a hostname or FQDN is specified. | |
94 | Can be set to `4` for IPv4 or `6` for IPv6 addresses. By default, | |
95 | this function searches for IPv4 addresses first before searching | |
96 | for IPv6 addresses. | |
97 | ||
98 | Advanced (**kwags): | |
99 | ||
100 | :type payload: bytes, optional | |
101 | :param payload: The payload content in bytes. A random payload is | |
102 | used by default. | |
103 | ||
104 | :type payload_size: int, optional | |
105 | :param payload_size: The payload size. Ignored when the `payload` | |
106 | parameter is set. Default to 56. | |
107 | ||
108 | :type traffic_class: int, optional | |
109 | :param traffic_class: The traffic class of ICMP packets. | |
66 | 110 | Provides a defined level of service to packets by setting the |
67 | 111 | DS Field (formerly TOS) or the Traffic Class field of IP |
68 | 112 | headers. Packets are delivered with the minimum priority by |
70 | 114 | Intermediate routers must be able to support this feature. |
71 | 115 | Only available on Unix systems. Ignored on Windows. |
72 | 116 | |
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 | ||
87 | :rtype: list of Hop | |
117 | :rtype: list[Hop] | |
88 | 118 | :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. | |
119 | desired destination. The list is sorted in ascending order | |
120 | according to the distance, in terms of hops, that separates the | |
121 | remote host from the current machine. Gateways that do not | |
122 | respond to requests are not added to this list. | |
123 | ||
124 | :raises NameLookupError: If you pass a hostname or FQDN in | |
125 | parameters and it does not exist or cannot be resolved. | |
126 | :raises SocketPermissionError: If the privileges are insufficient to | |
127 | create the socket. | |
128 | :raises SocketAddressError: If the source address cannot be assigned | |
129 | to the socket. | |
130 | :raises ICMPSocketError: If another error occurs. See the | |
131 | `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
95 | 132 | |
96 | 133 | Usage:: |
97 | 134 | |
101 | 138 | |
102 | 139 | >>> for hop in hops: |
103 | 140 | ... if last_distance + 1 != hop.distance: |
104 | ... print('Some routers are not responding') | |
141 | ... print('Some gateways are not responding') | |
105 | 142 | ... |
106 | 143 | ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms') |
107 | 144 | ... |
108 | 145 | ... last_distance = hop.distance |
109 | ... | |
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 | |
146 | ||
147 | 1 10.0.0.1 5.196 ms | |
148 | 2 194.149.169.49 7.552 ms | |
149 | 3 194.149.166.54 12.21 ms | |
150 | * Some gateways are not responding | |
151 | 5 212.73.205.22 22.15 ms | |
152 | 6 1.1.1.1 13.59 ms | |
116 | 153 | |
117 | 154 | See the `Hop` class for details. |
118 | 155 | |
119 | 156 | ''' |
120 | address = resolve(address) | |
157 | if is_hostname(address): | |
158 | address = resolve(address, family)[0] | |
121 | 159 | |
122 | 160 | if is_ipv6_address(address): |
123 | socket = ICMPv6Socket() | |
124 | ||
161 | _Socket = ICMPv6Socket | |
125 | 162 | else: |
126 | socket = ICMPv4Socket() | |
127 | ||
128 | ttl = 1 | |
163 | _Socket = ICMPv4Socket | |
164 | ||
165 | id = id or unique_identifier() | |
166 | ttl = first_hop | |
129 | 167 | host_reached = False |
130 | 168 | hops = [] |
131 | 169 | |
132 | while not host_reached and ttl <= max_hops: | |
133 | hop_address = None | |
134 | packets_sent = 0 | |
135 | packets_received = 0 | |
136 | ||
137 | min_rtt = float('inf') | |
138 | avg_rtt = 0.0 | |
139 | max_rtt = 0.0 | |
140 | ||
141 | for sequence in range(count): | |
142 | request = ICMPRequest( | |
143 | destination=address, | |
144 | id=id, | |
145 | sequence=sequence, | |
146 | timeout=timeout, | |
147 | ttl=ttl, | |
148 | traffic_class=traffic_class, | |
149 | **kwargs) | |
150 | ||
151 | try: | |
152 | socket.send(request) | |
153 | packets_sent += 1 | |
154 | ||
155 | reply = socket.receive() | |
156 | reply.raise_for_status() | |
157 | host_reached = True | |
158 | ||
159 | except TimeExceeded: | |
160 | sleep(interval) | |
161 | ||
162 | except ICMPLibError: | |
163 | continue | |
164 | ||
165 | hop_address = reply.source | |
166 | packets_received += 1 | |
167 | ||
168 | round_trip_time = (reply.time - request.time) * 1000 | |
169 | avg_rtt += round_trip_time | |
170 | min_rtt = min(round_trip_time, min_rtt) | |
171 | max_rtt = max(round_trip_time, max_rtt) | |
172 | ||
173 | if fast_mode: | |
174 | break | |
175 | ||
176 | if packets_received: | |
177 | avg_rtt /= packets_received | |
178 | ||
179 | hop = Hop( | |
180 | address=hop_address, | |
181 | min_rtt=min_rtt, | |
182 | avg_rtt=avg_rtt, | |
183 | max_rtt=max_rtt, | |
184 | packets_sent=packets_sent, | |
185 | packets_received=packets_received, | |
186 | distance=ttl) | |
187 | ||
188 | hops.append(hop) | |
189 | ||
190 | ttl += 1 | |
191 | ||
192 | socket.close() | |
170 | with _Socket(source) as sock: | |
171 | while not host_reached and ttl <= max_hops: | |
172 | reply = None | |
173 | packets_sent = 0 | |
174 | rtts = [] | |
175 | ||
176 | for sequence in range(count): | |
177 | request = ICMPRequest( | |
178 | destination=address, | |
179 | id=id, | |
180 | sequence=sequence, | |
181 | ttl=ttl, | |
182 | **kwargs) | |
183 | ||
184 | try: | |
185 | sock.send(request) | |
186 | packets_sent += 1 | |
187 | ||
188 | reply = sock.receive(request, timeout) | |
189 | rtt = (reply.time - request.time) * 1000 | |
190 | rtts.append(rtt) | |
191 | ||
192 | reply.raise_for_status() | |
193 | host_reached = True | |
194 | ||
195 | except TimeExceeded: | |
196 | sleep(interval) | |
197 | ||
198 | except ICMPLibError: | |
199 | break | |
200 | ||
201 | if reply: | |
202 | hop = Hop( | |
203 | address=reply.source, | |
204 | packets_sent=packets_sent, | |
205 | rtts=rtts, | |
206 | distance=ttl) | |
207 | ||
208 | hops.append(hop) | |
209 | ||
210 | ttl += 1 | |
193 | 211 | |
194 | 212 | return hops |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |
23 | 26 | <https://www.gnu.org/licenses/>. |
24 | 27 | ''' |
25 | 28 | |
26 | import socket | |
29 | import socket, asyncio | |
30 | ||
31 | from threading import Lock | |
27 | 32 | from sys import platform |
28 | 33 | from os import getpid |
29 | 34 | from re import match |
30 | 35 | from random import choices |
31 | 36 | |
37 | from .exceptions import NameLookupError | |
38 | ||
32 | 39 | |
33 | 40 | PID = getpid() |
41 | PLATFORM_LINUX = platform == 'linux' | |
42 | PLATFORM_MACOS = platform == 'darwin' | |
34 | 43 | PLATFORM_WINDOWS = platform == 'win32' |
35 | 44 | |
45 | _lock_id = Lock() | |
46 | _current_id = PID | |
47 | ||
36 | 48 | |
37 | 49 | def random_byte_message(size): |
38 | 50 | ''' |
39 | 51 | Generate a random byte sequence of the specified size. |
40 | 52 | |
41 | 53 | ''' |
42 | bytes_available = ( | |
54 | sequence = choices( | |
43 | 55 | b'abcdefghijklmnopqrstuvwxyz' |
44 | 56 | b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
45 | b'1234567890' | |
46 | ) | |
47 | ||
48 | return bytes( | |
49 | choices(bytes_available, k=size) | |
50 | ) | |
51 | ||
52 | ||
53 | def resolve(name): | |
54 | ''' | |
55 | Resolve a hostname or FQDN into an IP address. If several IP | |
56 | addresses are available, only the first one is returned. | |
57 | ||
58 | This function searches for IPv4 addresses first for compatibility | |
59 | reasons before searching for IPv6 addresses. | |
60 | ||
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. | |
64 | ||
65 | ''' | |
66 | if is_ipv4_address(name) or is_ipv6_address(name): | |
67 | return name | |
68 | ||
57 | b'1234567890', k=size) | |
58 | ||
59 | return bytes(sequence) | |
60 | ||
61 | ||
62 | def unique_identifier(): | |
63 | ''' | |
64 | Generate a unique identifier between 0 and 65535. | |
65 | The first number generated will be equal to the PID + 1. | |
66 | ||
67 | ''' | |
68 | global _current_id | |
69 | ||
70 | with _lock_id: | |
71 | _current_id += 1 | |
72 | _current_id &= 0xffff | |
73 | ||
74 | return _current_id | |
75 | ||
76 | ||
77 | def resolve(name, family=None): | |
78 | ''' | |
79 | Resolve a hostname or FQDN to an IP address. Depending on the name | |
80 | specified in parameters, several IP addresses may be returned. | |
81 | ||
82 | This function relies on the DNS name server configured on your | |
83 | operating system. | |
84 | ||
85 | :type name: str | |
86 | :param name: A hostname or a Fully Qualified Domain Name (FQDN). | |
87 | ||
88 | :type family: int, optional | |
89 | :param family: The address family. Can be set to `4` for IPv4 or `6` | |
90 | for IPv6 addresses. By default, this function searches for IPv4 | |
91 | addresses first for compatibility reasons (A DNS lookup) before | |
92 | searching for IPv6 addresses (AAAA DNS lookup). | |
93 | ||
94 | :rtype: list[str] | |
95 | :returns: A list of IP addresses associated with the name passed as | |
96 | a parameter. | |
97 | ||
98 | :raises NameLookupError: If the requested name does not exist or | |
99 | cannot be resolved. | |
100 | ||
101 | ''' | |
69 | 102 | try: |
70 | return socket.getaddrinfo( | |
103 | if family == 6: | |
104 | _family = socket.AF_INET6 | |
105 | else: | |
106 | _family = socket.AF_INET | |
107 | ||
108 | lookup = socket.getaddrinfo( | |
71 | 109 | host=name, |
72 | 110 | port=None, |
73 | family=socket.AF_INET, | |
74 | type=socket.SOCK_DGRAM | |
75 | )[0][4][0] | |
111 | family=_family, | |
112 | type=socket.SOCK_DGRAM) | |
113 | ||
114 | return [address[4][0] for address in lookup] | |
76 | 115 | |
77 | 116 | except OSError: |
78 | pass | |
79 | ||
117 | if not family: | |
118 | return resolve(name, 6) | |
119 | ||
120 | raise NameLookupError(name) | |
121 | ||
122 | ||
123 | async def async_resolve(name, family=None): | |
124 | ''' | |
125 | Resolve a hostname or FQDN to an IP address. Depending on the name | |
126 | specified in parameters, several IP addresses may be returned. | |
127 | ||
128 | This function relies on the DNS name server configured on your | |
129 | operating system. | |
130 | ||
131 | This function is non-blocking. | |
132 | ||
133 | :type name: str | |
134 | :param name: A hostname or a Fully Qualified Domain Name (FQDN). | |
135 | ||
136 | :type family: int, optional | |
137 | :param family: The address family. Can be set to `4` for IPv4 or `6` | |
138 | for IPv6 addresses. By default, this function searches for IPv4 | |
139 | addresses first for compatibility reasons (A DNS lookup) before | |
140 | searching for IPv6 addresses (AAAA DNS lookup). | |
141 | ||
142 | :rtype: list[str] | |
143 | :returns: A list of IP addresses associated with the name passed as | |
144 | a parameter. | |
145 | ||
146 | :raises NameLookupError: If the requested name does not exist or | |
147 | cannot be resolved. | |
148 | ||
149 | ''' | |
80 | 150 | try: |
81 | return socket.getaddrinfo( | |
151 | if family == 6: | |
152 | _family = socket.AF_INET6 | |
153 | else: | |
154 | _family = socket.AF_INET | |
155 | ||
156 | loop = asyncio.get_running_loop() | |
157 | ||
158 | lookup = await loop.getaddrinfo( | |
82 | 159 | host=name, |
83 | 160 | port=None, |
84 | family=socket.AF_INET6, | |
85 | type=socket.SOCK_DGRAM | |
86 | )[0][4][0] | |
161 | family=_family, | |
162 | type=socket.SOCK_DGRAM) | |
163 | ||
164 | return [address[4][0] for address in lookup] | |
87 | 165 | |
88 | 166 | except OSError: |
89 | return name | |
167 | if not family: | |
168 | return await async_resolve(name, 6) | |
169 | ||
170 | raise NameLookupError(name) | |
171 | ||
172 | ||
173 | def is_hostname(name): | |
174 | ''' | |
175 | Indicate whether the specified name is a hostname or an FQDN. | |
176 | Return a `boolean`. | |
177 | ||
178 | ''' | |
179 | pattern = r'(?i)^([a-z0-9-]+|([a-z0-9_-]+[.])+[a-z]+)$' | |
180 | return match(pattern, name) is not None | |
90 | 181 | |
91 | 182 | |
92 | 183 | def is_ipv4_address(address): |
95 | 186 | Return a `boolean`. |
96 | 187 | |
97 | 188 | ''' |
98 | return match( | |
99 | r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$', | |
100 | address | |
101 | ) is not None | |
189 | pattern = r'^([0-9]{1,3}[.]){3}[0-9]{1,3}$' | |
190 | return match(pattern, address) is not None | |
102 | 191 | |
103 | 192 | |
104 | 193 | def is_ipv6_address(address): |
0 | Metadata-Version: 2.1 | |
1 | Name: icmplib | |
2 | Version: 3.0 | |
3 | Summary: Easily forge ICMP packets and make your own ping and traceroute. | |
4 | Home-page: https://github.com/ValentinBELYN/icmplib | |
5 | Author: Valentin BELYN | |
6 | Author-email: [email protected] | |
7 | License: GNU Lesser General Public License v3.0 | |
8 | Description: <div align="center"> | |
9 | <br> | |
10 | <img src="media/icmplib-logo-2.0.png" height="190" width="100" alt="icmplib"> | |
11 | <br> | |
12 | <br> | |
13 | ||
14 | <br> | |
15 | <div> | |
16 | <a href="#features">Features</a> | |
17 | <a href="#installation">Installation</a> | |
18 | <a href="#built-in-functions">Built-in functions</a> | |
19 | <a href="#icmp-sockets">ICMP sockets</a> | |
20 | <a href="#faq">FAQ</a> | |
21 | <a href="#contributing">Contributing</a> | |
22 | <a href="#donate">Donate</a> | |
23 | <a href="#license">License</a> | |
24 | </div> | |
25 | <br> | |
26 | ||
27 | <pre>icmplib is a brand new and modern implementation of the ICMP protocol in Python. | |
28 | Use the built-in functions or build your own, you have the choice! | |
29 | ||
30 | <a href="#how-to-use-the-library-without-root-privileges">- You can now use this library without root privileges -</a></pre> | |
31 | <a href="https://pypi.org/project/icmplib"> | |
32 | <img src="https://img.shields.io/pypi/dm/icmplib.svg?style=flat-square&labelColor=0366d6&color=005cc5" alt="statistics"> | |
33 | </a> | |
34 | </div> | |
35 | ||
36 | <br> | |
37 | ||
38 | ## Features | |
39 | ||
40 | - :deciduous_tree: **Ready-to-use:** icmplib offers ready-to-use functions such as the most popular ones: `ping`, `multiping` and `traceroute`. | |
41 | - :gem: **Modern:** This library uses the latest technologies offered by Python 3.6+ and is fully object-oriented. | |
42 | - :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! | |
43 | - :zap: **Powerful:** Use the library without root privileges, set the traffic class of ICMP packets, customize their payload and more! | |
44 | - :nut_and_bolt: **Evolutive:** Easily build your own classes and functions with `ICMPv4` and `ICMPv6` sockets. | |
45 | - :fire: **Seamless integration of IPv6:** Use IPv6 the same way you use IPv4. Automatic detection is done without impacting performance. | |
46 | - :rainbow: **Broadcast support** (you must use the `ICMPv4Socket` class to enable it). | |
47 | - :beer: **Support of all operating systems.** Tested on Linux, macOS and Windows. | |
48 | - :metal: **No dependency:** icmplib is a pure Python implementation of the ICMP protocol. It does not use any external dependencies. | |
49 | ||
50 | <br> | |
51 | ||
52 | ## Installation | |
53 | ||
54 | The recommended way to install or upgrade icmplib is to use `pip3`: | |
55 | ||
56 | ```shell | |
57 | $ pip3 install icmplib | |
58 | $ pip3 install --upgrade icmplib | |
59 | ``` | |
60 | ||
61 | *icmplib requires Python 3.6 or later.* | |
62 | ||
63 | To import icmplib into your project (only import what you need): | |
64 | ||
65 | ```python | |
66 | # For simple use | |
67 | from icmplib import ping, multiping, traceroute, resolve, Host, Hop | |
68 | ||
69 | # For advanced use (sockets) | |
70 | from icmplib import ICMPv4Socket, ICMPv6Socket, ICMPRequest, ICMPReply | |
71 | ||
72 | # Exceptions | |
73 | from icmplib import ICMPLibError, NameLookupError, ICMPSocketError | |
74 | from icmplib import SocketAddressError, SocketPermissionError | |
75 | from icmplib import SocketUnavailableError, SocketBroadcastError, TimeoutExceeded | |
76 | from icmplib import ICMPError, DestinationUnreachable, TimeExceeded | |
77 | ``` | |
78 | ||
79 | <br> | |
80 | ||
81 | ## Built-in functions | |
82 | ||
83 | ### Ping | |
84 | Send ICMP Echo Request packets to a network host. | |
85 | ||
86 | ```python | |
87 | ping(address, count=4, interval=1, timeout=2, id=PID, source=None, privileged=True, **kwargs) | |
88 | ``` | |
89 | ||
90 | #### Parameters | |
91 | - `address` | |
92 | ||
93 | The IP address, hostname or FQDN of the host to which messages should be sent. For deterministic behavior, prefer to use an IP address. | |
94 | ||
95 | - Type: `str` | |
96 | ||
97 | - `count` | |
98 | ||
99 | The number of ping to perform. | |
100 | ||
101 | - Type: `int` | |
102 | - Default: `4` | |
103 | ||
104 | - `interval` | |
105 | ||
106 | The interval in seconds between sending each packet. | |
107 | ||
108 | - Type: `int` or `float` | |
109 | - Default: `1` | |
110 | ||
111 | - `timeout` | |
112 | ||
113 | The maximum waiting time for receiving a reply in seconds. | |
114 | ||
115 | - Type: `int` or `float` | |
116 | - Default: `2` | |
117 | ||
118 | - `id` | |
119 | ||
120 | 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. | |
121 | ||
122 | - Type: `int` | |
123 | - Default: `PID` | |
124 | ||
125 | - `source` | |
126 | ||
127 | The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. | |
128 | ||
129 | - Type: `str` | |
130 | - Default: `None` | |
131 | ||
132 | - `privileged` | |
133 | ||
134 | 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. | |
135 | ||
136 | *Only available on Unix systems. Ignored on Windows.* | |
137 | ||
138 | - Type: `bool` | |
139 | - Default: `True` | |
140 | ||
141 | - `payload` | |
142 | ||
143 | The payload content in bytes. A random payload is used by default. | |
144 | ||
145 | - Type: `bytes` | |
146 | - Default: `None` | |
147 | ||
148 | - `payload_size` | |
149 | ||
150 | The payload size. Ignored when the `payload` parameter is set. | |
151 | ||
152 | - Type: `int` | |
153 | - Default: `56` | |
154 | ||
155 | - `traffic_class` | |
156 | ||
157 | 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. | |
158 | ||
159 | *Only available on Unix systems. Ignored on Windows.* | |
160 | ||
161 | - Type: `int` | |
162 | - Default: `0` | |
163 | ||
164 | #### Return value | |
165 | - A `Host` object containing statistics about the desired destination:<br> | |
166 | `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive` | |
167 | ||
168 | #### Exceptions | |
169 | - `NameLookupError` | |
170 | ||
171 | If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved. | |
172 | ||
173 | - `SocketPermissionError` | |
174 | ||
175 | If the privileges are insufficient to create the socket. | |
176 | ||
177 | - `SocketAddressError` | |
178 | ||
179 | If the source address cannot be assigned to the socket. | |
180 | ||
181 | - `ICMPSocketError` | |
182 | ||
183 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
184 | ||
185 | #### Example | |
186 | ```python | |
187 | >>> host = ping('1.1.1.1', count=10, interval=0.2) | |
188 | ||
189 | >>> host.address # The IP address of the host that responded | |
190 | '1.1.1.1' # to the request | |
191 | ||
192 | >>> host.min_rtt # The minimum round-trip time | |
193 | 12.2 | |
194 | ||
195 | >>> host.avg_rtt # The average round-trip time | |
196 | 13.2 | |
197 | ||
198 | >>> host.max_rtt # The maximum round-trip time | |
199 | 17.6 | |
200 | ||
201 | >>> host.packets_sent # The number of packets transmitted to the | |
202 | 10 # destination host | |
203 | ||
204 | >>> host.packets_received # The number of packets sent by the remote | |
205 | 9 # host and received by the current host | |
206 | ||
207 | >>> host.packet_loss # Packet loss occurs when packets fail to | |
208 | 0.1 # reach their destination. Returns a float | |
209 | # between 0 and 1 (all packets are lost) | |
210 | >>> host.is_alive # Indicates whether the host is reachable | |
211 | True | |
212 | ``` | |
213 | ||
214 | <br> | |
215 | ||
216 | ### Multiping | |
217 | Send ICMP Echo Request packets to several network hosts. | |
218 | ||
219 | 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. | |
220 | ||
221 | ```python | |
222 | multiping(addresses, count=2, interval=0.01, timeout=2, id=PID, source=None, privileged=True, **kwargs) | |
223 | ``` | |
224 | ||
225 | #### Parameters | |
226 | - `addresses` | |
227 | ||
228 | 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. | |
229 | ||
230 | - Type: `list of str` | |
231 | ||
232 | - `count` | |
233 | ||
234 | The number of ping to perform per address. | |
235 | ||
236 | - Type: `int` | |
237 | - Default: `2` | |
238 | ||
239 | - `interval` | |
240 | ||
241 | The interval in seconds between sending each packet. | |
242 | ||
243 | - Type: `int` or `float` | |
244 | - Default: `0.01` | |
245 | ||
246 | - `timeout` | |
247 | ||
248 | The maximum waiting time for receiving all responses in seconds. | |
249 | ||
250 | - Type: `int` or `float` | |
251 | - Default: `2` | |
252 | ||
253 | - `id` | |
254 | ||
255 | 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. | |
256 | ||
257 | - Type: `int` | |
258 | - Default: `PID` | |
259 | ||
260 | - `source` | |
261 | ||
262 | 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. | |
263 | ||
264 | - Type: `str` | |
265 | - Default: `None` | |
266 | ||
267 | - `privileged` | |
268 | ||
269 | 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. | |
270 | ||
271 | *Only available on Unix systems. Ignored on Windows.* | |
272 | ||
273 | - Type: `bool` | |
274 | - Default: `True` | |
275 | ||
276 | - `payload` | |
277 | ||
278 | The payload content in bytes. A random payload is used by default. | |
279 | ||
280 | - Type: `bytes` | |
281 | - Default: `None` | |
282 | ||
283 | - `payload_size` | |
284 | ||
285 | The payload size. Ignored when the `payload` parameter is set. | |
286 | ||
287 | - Type: `int` | |
288 | - Default: `56` | |
289 | ||
290 | - `traffic_class` | |
291 | ||
292 | 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. | |
293 | ||
294 | *Only available on Unix systems. Ignored on Windows.* | |
295 | ||
296 | - Type: `int` | |
297 | - Default: `0` | |
298 | ||
299 | #### Return value | |
300 | - A `list of Host` objects containing statistics about the desired destinations:<br> | |
301 | `address`, `min_rtt`, `avg_rtt`, `max_rtt`, `packets_sent`, `packets_received`, `packet_loss`, `is_alive` | |
302 | ||
303 | The list is sorted in the same order as the addresses passed in parameters. | |
304 | ||
305 | #### Exceptions | |
306 | - `SocketPermissionError` | |
307 | ||
308 | If the privileges are insufficient to create the socket. | |
309 | ||
310 | - `SocketAddressError` | |
311 | ||
312 | If the source address cannot be assigned to the socket. | |
313 | ||
314 | - `ICMPSocketError` | |
315 | ||
316 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
317 | ||
318 | #### Example | |
319 | ```python | |
320 | >>> hosts = multiping(['10.0.0.5', '127.0.0.1', '::1']) | |
321 | ||
322 | >>> for host in hosts: | |
323 | ... if host.is_alive: | |
324 | ... # See the Host class for details | |
325 | ... print(f'{host.address} is alive!') | |
326 | ... | |
327 | ... else: | |
328 | ... print(f'{host.address} is dead!') | |
329 | ... | |
330 | ||
331 | # 10.0.0.5 is dead! | |
332 | # 127.0.0.1 is alive! | |
333 | # ::1 is alive! | |
334 | ``` | |
335 | ||
336 | <br> | |
337 | ||
338 | ### Traceroute | |
339 | Determine the route to a destination host. | |
340 | ||
341 | 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. | |
342 | ||
343 | *This function requires root privileges to run.* | |
344 | ||
345 | ```python | |
346 | traceroute(address, count=2, interval=0.05, timeout=2, id=PID, first_hop=1, max_hops=30, source=None, fast=False, **kwargs) | |
347 | ``` | |
348 | ||
349 | #### Parameters | |
350 | - `address` | |
351 | ||
352 | The IP address, hostname or FQDN of the host to reach. For deterministic behavior, prefer to use an IP address. | |
353 | ||
354 | - Type: `str` | |
355 | ||
356 | - `count` | |
357 | ||
358 | The number of ping to perform per hop. | |
359 | ||
360 | - Type: `int` | |
361 | - Default: `2` | |
362 | ||
363 | - `interval` | |
364 | ||
365 | The interval in seconds between sending each packet. | |
366 | ||
367 | - Type: `int` or `float` | |
368 | - Default: `0.05` | |
369 | ||
370 | - `timeout` | |
371 | ||
372 | The maximum waiting time for receiving a reply in seconds. | |
373 | ||
374 | - Type: `int` or `float` | |
375 | - Default: `2` | |
376 | ||
377 | - `id` | |
378 | ||
379 | The identifier of ICMP requests. Used to match the responses with requests. In practice, a unique identifier should be used for every traceroute process. | |
380 | ||
381 | - Type: `int` | |
382 | - Default: `PID` | |
383 | ||
384 | - `first_hop` | |
385 | ||
386 | The initial time to live value used in outgoing probe packets. | |
387 | ||
388 | - Type: `int` | |
389 | - Default: `1` | |
390 | ||
391 | - `max_hops` | |
392 | ||
393 | The maximum time to live (max number of hops) used in outgoing probe packets. | |
394 | ||
395 | - Type: `int` | |
396 | - Default: `30` | |
397 | ||
398 | - `source` | |
399 | ||
400 | The IP address from which you want to send packets. By default, the interface is automatically chosen according to the specified destination. | |
401 | ||
402 | - Type: `str` | |
403 | - Default: `None` | |
404 | ||
405 | - `fast` | |
406 | ||
407 | 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. | |
408 | ||
409 | - Type: `bool` | |
410 | - Default: `False` | |
411 | ||
412 | - `payload` | |
413 | ||
414 | The payload content in bytes. A random payload is used by default. | |
415 | ||
416 | - Type: `bytes` | |
417 | - Default: `None` | |
418 | ||
419 | - `payload_size` | |
420 | ||
421 | The payload size. Ignored when the `payload` parameter is set. | |
422 | ||
423 | - Type: `int` | |
424 | - Default: `56` | |
425 | ||
426 | - `traffic_class` | |
427 | ||
428 | 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. | |
429 | ||
430 | *Only available on Unix systems. Ignored on Windows.* | |
431 | ||
432 | - Type: `int` | |
433 | - Default: `0` | |
434 | ||
435 | #### Return value | |
436 | - 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`. | |
437 | ||
438 | 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. | |
439 | ||
440 | #### Exceptions | |
441 | - `NameLookupError` | |
442 | ||
443 | If you pass a hostname or FQDN in parameters and it does not exist or cannot be resolved. | |
444 | ||
445 | - `SocketPermissionError` | |
446 | ||
447 | If the privileges are insufficient to create the socket. | |
448 | ||
449 | - `SocketAddressError` | |
450 | ||
451 | If the source address cannot be assigned to the socket. | |
452 | ||
453 | - `ICMPSocketError` | |
454 | ||
455 | If another error occurs. See the `ICMPv4Socket` or `ICMPv6Socket` class for details. | |
456 | ||
457 | #### Example | |
458 | ```python | |
459 | >>> hops = traceroute('1.1.1.1') | |
460 | ||
461 | >>> print('Distance/TTL Address Average round-trip time') | |
462 | >>> last_distance = 0 | |
463 | ||
464 | >>> for hop in hops: | |
465 | ... if last_distance + 1 != hop.distance: | |
466 | ... print('Some gateways are not responding') | |
467 | ... | |
468 | ... # See the Hop class for details | |
469 | ... print(f'{hop.distance} {hop.address} {hop.avg_rtt} ms') | |
470 | ... | |
471 | ... last_distance = hop.distance | |
472 | ... | |
473 | ||
474 | # Distance/TTL Address Average round-trip time | |
475 | # 1 10.0.0.1 5.196 ms | |
476 | # 2 194.149.169.49 7.552 ms | |
477 | # 3 194.149.166.54 12.21 ms | |
478 | # * Some gateways are not responding | |
479 | # 5 212.73.205.22 22.15 ms | |
480 | # 6 1.1.1.1 13.59 ms | |
481 | ``` | |
482 | ||
483 | <br> | |
484 | ||
485 | ## ICMP sockets | |
486 | ||
487 | 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. | |
488 | ||
489 | ``` | |
490 | ┌──────────────────┐ | |
491 | ┌─────────────────┐ send(...) │ ICMPv4Socket │ receive() ┌─────────────────┐ | |
492 | │ ICMPRequest │ ────────────> │ or │ ────────────> │ ICMPReply │ | |
493 | └─────────────────┘ │ ICMPv6Socket │ └─────────────────┘ | |
494 | └──────────────────┘ | |
495 | ``` | |
496 | ||
497 | ### ICMPRequest | |
498 | A user-created object that represents an ICMP Echo Request. | |
499 | ||
500 | ```python | |
501 | ICMPRequest(destination, id, sequence, payload=None, payload_size=56, ttl=64, traffic_class=0) | |
502 | ``` | |
503 | ||
504 | #### Parameters and properties | |
505 | - `destination` | |
506 | ||
507 | The IP address of the host to which the message should be sent. | |
508 | ||
509 | - Type: `str` | |
510 | ||
511 | - `id` | |
512 | ||
513 | 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. | |
514 | ||
515 | - Type: `int` | |
516 | ||
517 | - `sequence` | |
518 | ||
519 | The sequence number. Used to match the reply with the request. Typically, the sequence number is incremented for each packet sent during the process. | |
520 | ||
521 | - Type: `int` | |
522 | ||
523 | - `payload` | |
524 | ||
525 | The payload content in bytes. A random payload is used by default. | |
526 | ||
527 | - Type: `bytes` | |
528 | - Default: `None` | |
529 | ||
530 | - `payload_size` | |
531 | ||
532 | The payload size. Ignored when the `payload` parameter is set. | |
533 | ||
534 | - Type: `int` | |
535 | - Default: `56` | |
536 | ||
537 | - `ttl` | |
538 | ||
539 | The time to live of the packet in terms of hops. | |
540 | ||
541 | - Type: `int` | |
542 | - Default: `64` | |
543 | ||
544 | - `traffic_class` | |
545 | ||
546 | 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. | |
547 | ||
548 | *Only available on Unix systems. Ignored on Windows.* | |
549 | ||
550 | - Type: `int` | |
551 | - Default: `0` | |
552 | ||
553 | #### Properties only | |
554 | - `time` | |
555 | ||
556 | 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. | |
557 | ||
558 | - Type: `float` | |
559 | ||
560 | <br> | |
561 | ||
562 | ### ICMPReply | |
563 | A class that represents an ICMP reply. Generated from an ICMP socket (`ICMPv4Socket` or `ICMPv6Socket`). | |
564 | ||
565 | ```python | |
566 | ICMPReply(source, id, sequence, type, code, bytes_received, time) | |
567 | ``` | |
568 | ||
569 | #### Parameters and properties | |
570 | - `source` | |
571 | ||
572 | The IP address of the gateway or host that composes the ICMP message. | |
573 | ||
574 | - Type: `str` | |
575 | ||
576 | - `id` | |
577 | ||
578 | The identifier of the reply. Used to match the reply with the request. | |
579 | ||
580 | - Type: `int` | |
581 | ||
582 | - `sequence` | |
583 | ||
584 | The sequence number. Used to match the reply with the request. | |
585 | ||
586 | - Type: `int` | |
587 | ||
588 | - `type` | |
589 | ||
590 | The type of ICMP message. | |
591 | ||
592 | - Type: `int` | |
593 | ||
594 | - `code` | |
595 | ||
596 | The ICMP error code. | |
597 | ||
598 | - Type: `int` | |
599 | ||
600 | - `bytes_received` | |
601 | ||
602 | The number of bytes received. | |
603 | ||
604 | - Type: `int` | |
605 | ||
606 | - `time` | |
607 | ||
608 | The timestamp of the ICMP reply. | |
609 | ||
610 | - Type: `float` | |
611 | ||
612 | #### Methods | |
613 | - `raise_for_status()` | |
614 | ||
615 | Throw an exception if the reply is not an ICMP Echo Reply. Otherwise, do nothing. | |
616 | ||
617 | - Raises `DestinationUnreachable`: If the destination is unreachable for some reason. | |
618 | - Raises `TimeExceeded`: If the time to live field of the ICMP request has reached zero. | |
619 | - Raises `ICMPError`: Raised for any other type and ICMP error code, except ICMP Echo Reply messages. | |
620 | ||
621 | <br> | |
622 | ||
623 | ### ICMPv4Socket | |
624 | Class for sending and receiving ICMPv4 packets. | |
625 | ||
626 | ```python | |
627 | ICMPv4Socket(address=None, privileged=True) | |
628 | ``` | |
629 | ||
630 | #### Parameters | |
631 | - `source` | |
632 | ||
633 | The IP address from which you want to listen and send packets. By default, the socket listens on all interfaces. | |
634 | ||
635 | - Type: `str` | |
636 | - Default: `None` | |
637 | ||
638 | - `privileged` | |
639 | ||
640 | 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. | |
641 | ||
642 | *Only available on Unix systems. Ignored on Windows.* | |
643 | ||
644 | - Type: `bool` | |
645 | - Default: `True` | |
646 | ||
647 | #### Methods | |
648 | - `__init__(address=None, privileged=True)` | |
649 | ||
650 | *Constructor. Automatically called: do not call it directly.* | |
651 | ||
652 | - Raises `SocketPermissionError`: If the privileges are insufficient to create the socket. | |
653 | - Raises `SocketAddressError`: If the requested address cannot be assigned to the socket. | |
654 | - Raises `ICMPSocketError`: If another error occurs while creating the socket. | |
655 | ||
656 | - `__del__()` | |
657 | ||
658 | *Destructor. Automatically called: do not call it directly.* | |
659 | ||
660 | Call the `close` method. | |
661 | ||
662 | - `send(request)` | |
663 | ||
664 | Send an ICMP request message over the network to a remote host.<br> | |
665 | This operation is non-blocking. Use the `receive` method to get the reply. | |
666 | ||
667 | - 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. | |
668 | - Raises `SocketBroadcastError`: If a broadcast address is used and the corresponding option is not enabled on the socket (ICMPv4 only). | |
669 | - Raises `SocketUnavailableError`: If the socket is closed. | |
670 | - Raises `ICMPSocketError`: If another error occurs while sending. | |
671 | ||
672 | - `receive(request=None, timeout=2)` | |
673 | ||
674 | Receive an ICMP reply message from the socket.<br> | |
675 | This method can be called multiple times if you expect several responses as with a broadcast address. | |
676 | ||
677 | - Parameter `request` *(ICMPRequest)*: The ICMP request to use to match the response. By default, all ICMP packets arriving on the socket are returned. | |
678 | - Parameter `timeout` *(int or float)*: The maximum waiting time for receiving the response in seconds. Default to `2`. | |
679 | - Raises `TimeoutExceeded`: If no response is received before the timeout specified in parameters. | |
680 | - Raises `SocketUnavailableError`: If the socket is closed. | |
681 | - Raises `ICMPSocketError`: If another error occurs while receiving. | |
682 | ||
683 | Returns an `ICMPReply` object representing the response of the desired destination or an upstream gateway. | |
684 | ||
685 | - `close()` | |
686 | ||
687 | Close the socket. It cannot be used after this call. | |
688 | ||
689 | #### Properties | |
690 | - `address` | |
691 | ||
692 | The IP address from which the socket listens and sends packets. Return `None` if the socket listens on all interfaces. | |
693 | ||
694 | - Type: `str` | |
695 | ||
696 | - `is_privileged` | |
697 | ||
698 | Indicate whether the socket is running in privileged mode. | |
699 | ||
700 | - Type: `bool` | |
701 | ||
702 | - `is_closed` | |
703 | ||
704 | Indicate whether the socket is closed. | |
705 | ||
706 | - Type: `bool` | |
707 | ||
708 | #### Properties and setters | |
709 | - `broadcast` | |
710 | ||
711 | Enable or disable the broadcast support on the socket. | |
712 | ||
713 | - Type: `bool` | |
714 | - Default: `False` | |
715 | ||
716 | <br> | |
717 | ||
718 | ### ICMPv6Socket | |
719 | Class for sending and receiving ICMPv6 packets. | |
720 | ||
721 | ```python | |
722 | ICMPv6Socket(address=None, privileged=True) | |
723 | ``` | |
724 | ||
725 | #### Methods and properties | |
726 | The same methods and properties as for the `ICMPv4Socket` class, except the `broadcast` property. | |
727 | ||
728 | <br> | |
729 | ||
730 | ### Exceptions | |
731 | The library contains many exceptions to adapt to your needs: | |
732 | ||
733 | ``` | |
734 | ICMPLibError | |
735 | ├─ NameLookupError | |
736 | ├─ ICMPSocketError | |
737 | │ ├─ SocketAddressError | |
738 | │ ├─ SocketPermissionError | |
739 | │ ├─ SocketUnavailableError | |
740 | │ ├─ SocketBroadcastError | |
741 | │ └─ TimeoutExceeded | |
742 | │ | |
743 | └─ ICMPError | |
744 | ├─ DestinationUnreachable | |
745 | │ ├─ ICMPv4DestinationUnreachable | |
746 | │ └─ ICMPv6DestinationUnreachable | |
747 | │ | |
748 | └─ TimeExceeded | |
749 | ├─ ICMPv4TimeExceeded | |
750 | └─ ICMPv6TimeExceeded | |
751 | ``` | |
752 | ||
753 | - `ICMPLibError`: Exception class for the icmplib package. | |
754 | - `NameLookupError`: Raised when the requested name does not exist or cannot be resolved. This concerns both Fully Qualified Domain Names and hostnames. | |
755 | - `ICMPSocketError`: Base class for ICMP sockets exceptions. | |
756 | - `SocketAddressError`: Raised when the requested address cannot be assigned to the socket. | |
757 | - `SocketPermissionError`: Raised when the privileges are insufficient to create the socket. | |
758 | - `SocketUnavailableError`: Raised when an action is performed while the socket is closed. | |
759 | - `SocketBroadcastError`: Raised when a broadcast address is used and the corresponding option is not enabled on the socket. | |
760 | - `TimeoutExceeded`: Raised when a timeout occurs on a socket. | |
761 | - `ICMPError`: Base class for ICMP error messages. | |
762 | - `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. | |
763 | - `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. | |
764 | ||
765 | Use the `message` property to get the error message. `ICMPError` subclasses have a `reply` property to retrieve the response. | |
766 | ||
767 | <br> | |
768 | ||
769 | ### Examples | |
770 | #### Sockets in action | |
771 | ```python | |
772 | def single_ping(address, timeout=2, id=PID): | |
773 | # Create an ICMP socket | |
774 | sock = ICMPv4Socket() | |
775 | ||
776 | # Create an ICMP request | |
777 | # See the 'ICMPRequest' class for details | |
778 | request = ICMPRequest( | |
779 | destination=address, | |
780 | id=id, | |
781 | sequence=1) | |
782 | ||
783 | try: | |
784 | sock.send(request) | |
785 | ||
786 | # If the program arrives in this section, it means that the | |
787 | # packet has been transmitted. | |
788 | ||
789 | reply = sock.receive(request, timeout) | |
790 | ||
791 | # If the program arrives in this section, it means that a | |
792 | # packet has been received. The reply has the same identifier | |
793 | # and sequence number that the request but it can come from | |
794 | # an intermediate gateway. | |
795 | ||
796 | reply.raise_for_status() | |
797 | ||
798 | # If the program arrives in this section, it means that the | |
799 | # destination host has responded to the request. | |
800 | ||
801 | except TimeoutExceeded as err: | |
802 | # The timeout has been reached | |
803 | print(err) | |
804 | ||
805 | except DestinationUnreachable as err: | |
806 | # The reply indicates that the destination host is unreachable | |
807 | print(err) | |
808 | ||
809 | # Retrieve the response | |
810 | reply = err.reply | |
811 | ||
812 | except TimeExceeded as err: | |
813 | # The reply indicates that the time to live exceeded in transit | |
814 | print(err) | |
815 | ||
816 | # Retrieve the response | |
817 | reply = err.reply | |
818 | ||
819 | except ICMPLibError as err: | |
820 | # All other errors | |
821 | print(err) | |
822 | ||
823 | # Automatic socket closure (garbage collector) | |
824 | ``` | |
825 | ||
826 | #### Verbose ping | |
827 | ```python | |
828 | def verbose_ping(address, count=4, interval=1, timeout=2, id=PID): | |
829 | # A payload of 56 bytes is used by default. You can modify it using | |
830 | # the 'payload_size' parameter of your ICMP request. | |
831 | print(f'PING {address}: 56 data bytes\n') | |
832 | ||
833 | # We detect the socket to use from the specified IP address | |
834 | if is_ipv6_address(address): | |
835 | sock = ICMPv6Socket() | |
836 | ||
837 | else: | |
838 | sock = ICMPv4Socket() | |
839 | ||
840 | for sequence in range(count): | |
841 | # We create an ICMP request | |
842 | request = ICMPRequest( | |
843 | destination=address, | |
844 | id=id, | |
845 | sequence=sequence) | |
846 | ||
847 | try: | |
848 | # We send the request | |
849 | sock.send(request) | |
850 | ||
851 | # We are awaiting receipt of an ICMP reply | |
852 | reply = sock.receive(request, timeout) | |
853 | ||
854 | # We received a reply | |
855 | # We display some information | |
856 | print(f'{reply.bytes_received} bytes from ' | |
857 | f'{reply.source}: ', end='') | |
858 | ||
859 | # We throw an exception if it is an ICMP error message | |
860 | reply.raise_for_status() | |
861 | ||
862 | # We calculate the round-trip time and we display it | |
863 | round_trip_time = (reply.time - request.time) * 1000 | |
864 | ||
865 | print(f'icmp_seq={sequence} ' | |
866 | f'time={round(round_trip_time, 3)} ms') | |
867 | ||
868 | # We pause before continuing | |
869 | if sequence < count - 1: | |
870 | sleep(interval) | |
871 | ||
872 | except TimeoutExceeded: | |
873 | # The timeout has been reached | |
874 | print(f'Request timeout for icmp_seq {sequence}') | |
875 | ||
876 | except ICMPError as err: | |
877 | # An ICMP error message has been received | |
878 | print(err) | |
879 | ||
880 | except ICMPLibError: | |
881 | # All other errors | |
882 | print('An error has occurred.') | |
883 | ||
884 | ||
885 | verbose_ping('1.1.1.1') | |
886 | ||
887 | # PING 1.1.1.1: 56 data bytes | |
888 | # 64 bytes from 1.1.1.1: icmp_seq=0 time=12.061 ms | |
889 | # 64 bytes from 1.1.1.1: icmp_seq=1 time=12.597 ms | |
890 | # 64 bytes from 1.1.1.1: icmp_seq=2 time=12.475 ms | |
891 | # 64 bytes from 1.1.1.1: icmp_seq=3 time=10.822 ms | |
892 | ``` | |
893 | ||
894 | <br> | |
895 | ||
896 | ## FAQ | |
897 | ||
898 | ### How to resolve a FQDN/domain name or a hostname? | |
899 | The use of the built-in `resolve` function is recommended: | |
900 | ||
901 | ```python | |
902 | >>> resolve('github.com') | |
903 | '140.82.118.4' | |
904 | ``` | |
905 | ||
906 | - If several IP addresses are available, only the first one is returned. This function searches for IPv4 addresses first before searching for IPv6 addresses. | |
907 | - If you pass an IP address, no lookup is done. The same address is returned. | |
908 | - Raises a `NameLookupError` exception if the requested name does not exist or cannot be resolved. | |
909 | ||
910 | ### How to use the library without root privileges? | |
911 | Since its version 2.0, icmplib can be used without root privileges. | |
912 | ||
913 | For this, you can set the `privileged` parameter to `False` on the `ping` and `multiping` functions, as well as the low level classes. By disabling this parameter, the kernel handles some parts of the ICMP headers. | |
914 | ||
915 | On some Linux systems, you must allow this feature: | |
916 | ||
917 | ```shell | |
918 | $ echo 'net.ipv4.ping_group_range = 0 2147483647' | sudo tee -a /etc/sysctl.conf | |
919 | $ sudo sysctl -p | |
920 | ``` | |
921 | ||
922 | You can check the current value with the following command: | |
923 | ||
924 | ```shell | |
925 | $ sysctl net.ipv4.ping_group_range | |
926 | net.ipv4.ping_group_range = 0 2147483647 | |
927 | ``` | |
928 | ||
929 | *Since Ubuntu 20.04 LTS, this manipulation is no longer necessary.* | |
930 | ||
931 | [Read more on www.kernel.org](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt) | |
932 | ||
933 | ### Why I have no response from a remote host? | |
934 | In the event of no response from a remote host, several causes are possible: | |
935 | - 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. | |
936 | - The remote host or an upstream gateway is down. | |
937 | - The remote host or an upstream gateway drops ICMP messages for security reasons. | |
938 | - 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. | |
939 | ||
940 | <br> | |
941 | ||
942 | ## Contributing | |
943 | ||
944 | Comments and enhancements are welcome. | |
945 | ||
946 | All development is done on [GitHub](https://github.com/ValentinBELYN/icmplib). Use [Issues](https://github.com/ValentinBELYN/icmplib/issues) to report problems and submit feature requests. Please include a minimal example that reproduces the bug. | |
947 | ||
948 | ## Donate | |
949 | ||
950 | icmplib is completely free and open source. It has been fully developed on my free time. If you enjoy it, please consider donating to support the development. | |
951 | ||
952 | - [:tada: Donate via PayPal](https://paypal.me/ValentinBELYN) | |
953 | ||
954 | ## License | |
955 | ||
956 | Copyright 2017-2021 Valentin BELYN. | |
957 | ||
958 | Code released under the GNU LGPLv3 license. See the [LICENSE](LICENSE) for details. | |
959 | ||
960 | Keywords: pure,implementation,icmp,sockets,ping,multiping,traceroute,ipv4,ipv6,python3 | |
961 | Platform: UNKNOWN | |
962 | Classifier: Development Status :: 5 - Production/Stable | |
963 | Classifier: Intended Audience :: Developers | |
964 | Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) | |
965 | Classifier: Operating System :: OS Independent | |
966 | Classifier: Programming Language :: Python | |
967 | Classifier: Programming Language :: Python :: 3 | |
968 | Classifier: Programming Language :: Python :: 3 :: Only | |
969 | Classifier: Programming Language :: Python :: 3.7 | |
970 | Classifier: Programming Language :: Python :: 3.8 | |
971 | Classifier: Programming Language :: Python :: 3.9 | |
972 | Classifier: Programming Language :: Python :: 3.10 | |
973 | Classifier: Topic :: Software Development :: Libraries | |
974 | Classifier: Topic :: Software Development :: Libraries :: Python Modules | |
975 | Requires-Python: >=3.7 | |
976 | Description-Content-Type: text/markdown |
0 | LICENSE | |
1 | README.md | |
2 | setup.cfg | |
3 | setup.py | |
4 | icmplib/__init__.py | |
5 | icmplib/exceptions.py | |
6 | icmplib/models.py | |
7 | icmplib/multiping.py | |
8 | icmplib/ping.py | |
9 | icmplib/sockets.py | |
10 | icmplib/traceroute.py | |
11 | icmplib/utils.py | |
12 | icmplib.egg-info/PKG-INFO | |
13 | icmplib.egg-info/SOURCES.txt | |
14 | icmplib.egg-info/dependency_links.txt | |
15 | icmplib.egg-info/top_level.txt⏎ |
0 | icmplib |
0 | 0 | [metadata] |
1 | name = icmplib | |
2 | version = 1.2.2 | |
3 | description = Easily forge ICMP packets and make your own ping and traceroute. | |
4 | keywords = pure, implementation, icmp, sockets, ping, multiping, traceroute, ipv4, ipv6, python3 | |
5 | ||
6 | author = Valentin BELYN | |
7 | author_email = [email protected] | |
8 | ||
9 | long_description = file: README.md | |
10 | long_description_content_type = text/markdown | |
11 | url = https://github.com/ValentinBELYN/icmplib | |
12 | ||
13 | license = GNU Lesser General Public License v3.0 | |
14 | license_file = LICENSE | |
15 | ||
16 | classifiers = | |
17 | Development Status :: 5 - Production/Stable | |
18 | Intended Audience :: Developers | |
19 | License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) | |
20 | Operating System :: OS Independent | |
21 | Programming Language :: Python :: 3 | |
22 | Programming Language :: Python :: 3 :: Only | |
23 | Programming Language :: Python :: 3.6 | |
24 | Programming Language :: Python :: 3.7 | |
25 | Programming Language :: Python :: 3.8 | |
26 | Topic :: Software Development :: Libraries | |
27 | Topic :: Software Development :: Libraries :: Python Modules | |
1 | name = icmplib | |
2 | version = 3.0 | |
3 | description = Easily forge ICMP packets and make your own ping and traceroute. | |
4 | keywords = pure, implementation, icmp, sockets, ping, multiping, traceroute, ipv4, ipv6, python3 | |
5 | author = Valentin BELYN | |
6 | author_email = [email protected] | |
7 | long_description = file: README.md | |
8 | long_description_content_type = text/markdown | |
9 | url = https://github.com/ValentinBELYN/icmplib | |
10 | license = GNU Lesser General Public License v3.0 | |
11 | license_file = LICENSE | |
12 | classifiers = | |
13 | Development Status :: 5 - Production/Stable | |
14 | Intended Audience :: Developers | |
15 | License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+) | |
16 | Operating System :: OS Independent | |
17 | Programming Language :: Python | |
18 | Programming Language :: Python :: 3 | |
19 | Programming Language :: Python :: 3 :: Only | |
20 | Programming Language :: Python :: 3.7 | |
21 | Programming Language :: Python :: 3.8 | |
22 | Programming Language :: Python :: 3.9 | |
23 | Programming Language :: Python :: 3.10 | |
24 | Topic :: Software Development :: Libraries | |
25 | Topic :: Software Development :: Libraries :: Python Modules | |
28 | 26 | |
29 | 27 | [options] |
30 | packages = icmplib | |
31 | python_requires = >=3.6 | |
28 | packages = icmplib | |
29 | python_requires = >=3.7 | |
30 | ||
31 | [egg_info] | |
32 | tag_build = | |
33 | tag_date = 0 | |
34 |
1 | 1 | icmplib |
2 | 2 | ~~~~~~~ |
3 | 3 | |
4 | A powerful library for forging ICMP packets and performing ping | |
5 | and traceroute. | |
6 | ||
4 | 7 | https://github.com/ValentinBELYN/icmplib |
5 | 8 | |
6 | :copyright: Copyright 2017-2020 Valentin BELYN. | |
9 | :copyright: Copyright 2017-2021 Valentin BELYN. | |
7 | 10 | :license: GNU LGPLv3, see the LICENSE for details. |
8 | 11 | |
9 | 12 | ~~~~~~~ |