Annotation of embedaddon/mtr/packet/probe_cygwin.c, revision 1.1.1.3
1.1 misho 1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 2016 Matt Kimball
4:
5: This program is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
1.1.1.2 misho 14: You should have received a copy of the GNU General Public License along
15: with this program; if not, write to the Free Software Foundation, Inc.,
16: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.1 misho 17: */
18:
19: #include "probe.h"
20:
1.1.1.2 misho 21: #include <assert.h>
1.1 misho 22: #include <errno.h>
1.1.1.2 misho 23: #include <error.h>
24: #include <fcntl.h>
25: #include <io.h>
1.1 misho 26: #include <stdio.h>
1.1.1.2 misho 27: #include <unistd.h>
1.1 misho 28: #include <winternl.h>
29:
30: #include "protocols.h"
31:
1.1.1.2 misho 32:
33: /*
34: Implementation notes (or "Why this uses a worker thread")
35:
36: Having done my time debugging various race conditions over the
37: last twenty-plus years as a software developer, both of my own
38: creation and discovered in the code of others, I almost always
39: try to structure my code to be single-threaded. However,
40: I think in this case, the ICMP service thread is unavoidable.
41:
42: I would have liked to avoid multithreading entirely, but here are
43: the constraints:
44:
45: a) mtr was originally a Unix program which used "raw sockets".
46: b) In order to port mtr to Windows, Cygwin is used to get a
47: Unix-like environment.
48: c) You can't use a raw socket to receive an ICMP reply on Windows.
49: However, Windows provides a separate API in the form of
50: ICMP.DLL for sending and receiving ICMP messages.
51: d) The ICMP API works asynchronously, and requires completion
52: through an asynchronous procedure call ("APC")
53: e) APCs are only delivered during blocking Win32 operations
54: which are flagged as "alertable." This prevents apps from
55: having APCs execute unexpectedly during an I/O operation.
56: f) Cygwin's implementation of POSIX functions does all I/O
57: through non-alertable I/O operations. This is reasonable
58: because APCs don't exist in the POSIX API.
59: g) Cygwin implements Unix-style signals at the application level,
60: since the Windows kernel doesn't have them. We want our
61: program to respond to SIGTERM and SIGKILL, at least.
62: h) Cygwin's signal implementation will deliver signals during
63: blocking I/O functions in the Cygwin library, but won't
64: respond to signals if the signal is sent while the application
65: is in a blocking Windows API call which Cygwin is not aware of.
66: i) Since we want to both send/receive ICMP probes and also respond
67: to Unix-style signals, we require two threads: one which
68: uses Cygwin's POSIX style blocking I/O and can respond to
69: signals, and one which uses alertable waits using Win32
70: blocking APIs.
71:
72: The solution is to have the main thread using select() as the
73: blocking operation in its loop, and also to have an ICMP service
74: thread using WaitForSingleObjectEx() as its blocking operation.
75: The main thread will respond to signals. The ICMP service thread
76: will run the APCs completing ICMP.DLL requests.
77:
78: These two threads communicate through a pair of pipes. One pipe
79: sends requests from the main thread to the ICMP service thread,
80: and another pipe sends the requests back as they complete.
81:
82: We use the Cygwin pipe() to create the pipes, but in the ICMP
83: service thread we use the Win32 HANDLE that corresponds to the
84: receiving end of the input pipe to wait for ICMP requests.
85: */
86:
87:
88: static DWORD WINAPI icmp_service_thread(LPVOID param);
89:
1.1 misho 90: /* Windows doesn't require any initialization at a privileged level */
91: void init_net_state_privileged(
92: struct net_state_t *net_state)
93: {
94: }
95:
1.1.1.2 misho 96: /*
97: Convenience similar to error(), but for reporting Windows
98: error codes instead of errno codes.
99: */
100: void error_win(int exit_code, int win_error, const char *str) {
101: fprintf(stderr, "%s (code %d)\n", str, win_error);
102: exit(exit_code);
103: }
104:
105: /* Open the ICMP.DLL interface and start the ICMP service thread */
1.1 misho 106: void init_net_state(
107: struct net_state_t *net_state)
108: {
1.1.1.2 misho 109: HANDLE thread;
110: int in_pipe[2], out_pipe[2];
111: int err;
112:
1.1 misho 113: memset(net_state, 0, sizeof(struct net_state_t));
114:
115: net_state->platform.icmp4 = IcmpCreateFile();
116: net_state->platform.icmp6 = Icmp6CreateFile();
117:
118: if (net_state->platform.icmp4 == INVALID_HANDLE_VALUE
119: && net_state->platform.icmp6 == INVALID_HANDLE_VALUE) {
1.1.1.2 misho 120:
121: error_win(EXIT_FAILURE, GetLastError(), "Failure opening ICMP");
122: }
123: net_state->platform.ip4_socket_raw = false;
124: net_state->platform.ip6_socket_raw = false;
125:
126: /*
127: We need a pipe for communication with the ICMP thread
128: in each direction.
129: */
130: if (pipe(in_pipe) == -1 || pipe(out_pipe) == -1) {
131: error(EXIT_FAILURE, errno, "Failure creating thread pipe");
132: }
133:
134: net_state->platform.thread_in_pipe_read = in_pipe[0];
135: net_state->platform.thread_in_pipe_write = in_pipe[1];
136: net_state->platform.thread_out_pipe_read = out_pipe[0];
137: net_state->platform.thread_out_pipe_write = out_pipe[1];
138:
139: net_state->platform.thread_in_pipe_read_handle =
140: (HANDLE)get_osfhandle(in_pipe[0]);
141:
142: /*
143: The read on the out pipe needs to be nonblocking because
144: it will be occasionally checked in the main thread.
145: */
146: err = fcntl(out_pipe[0], F_SETFL, O_NONBLOCK);
147: if (err == -1) {
148: error(
149: EXIT_FAILURE, errno,
150: "Failure setting pipe to non-blocking");
151: }
152:
153: /* Spin up the ICMP service thread */
154: thread = CreateThread(
155: NULL, 0, icmp_service_thread, net_state, 0, NULL);
156:
157: if (thread == NULL) {
158: error_win(
159: EXIT_FAILURE, GetLastError(),
160: "Failure creating ICMP service thread");
1.1 misho 161: }
162: }
163:
164: /*
165: If we succeeded at opening the ICMP file handle, we can
166: assume that IP protocol version is supported.
167: */
168: bool is_ip_version_supported(
169: struct net_state_t *net_state,
170: int ip_version)
171: {
172: if (ip_version == 4) {
173: return (net_state->platform.icmp4 != INVALID_HANDLE_VALUE);
174: } else if (ip_version == 6) {
175: return (net_state->platform.icmp6 != INVALID_HANDLE_VALUE);
176: }
177:
178: return false;
179: }
180:
181: /* On Windows, we only support ICMP probes */
182: bool is_protocol_supported(
183: struct net_state_t * net_state,
184: int protocol)
185: {
186: if (protocol == IPPROTO_ICMP) {
187: return true;
188: }
189:
190: return false;
191: }
192:
193: /* Set the back pointer to the net_state when a probe is allocated */
194: void platform_alloc_probe(
195: struct net_state_t *net_state,
196: struct probe_t *probe)
197: {
198: probe->platform.net_state = net_state;
199: }
200:
201: /* Free the reply buffer when the probe is freed */
202: void platform_free_probe(
203: struct probe_t *probe)
204: {
205: }
206:
207: /* Report a windows error code using a platform-independent error string */
208: static
209: void report_win_error(
210: int command_token,
211: int err)
212: {
213: /* It could be that we got no reply because of timeout */
214: if (err == IP_REQ_TIMED_OUT || err == IP_SOURCE_QUENCH) {
215: printf("%d no-reply\n", command_token);
216: } else if (err == ERROR_INVALID_NETNAME) {
217: printf("%d address-not-available\n", command_token);
218: } else if (err == ERROR_INVALID_PARAMETER) {
219: printf("%d invalid-argument\n", command_token);
220: } else {
221: printf("%d unexpected-error winerror %d\n", command_token, err);
222: }
223: }
224:
225: /*
1.1.1.2 misho 226: After we have the result of an ICMP probe on the ICMP service
227: thread, this is used to send the result back to the main thread
228: for probe result reporting.
229: */
230: static
231: void queue_thread_result(struct icmp_thread_request_t *request)
232: {
233: int byte_count;
234:
235: /* Pass ownership of the request back through the result pipe */
236: byte_count = write(
237: request->net_state->platform.thread_out_pipe_write,
238: &request,
239: sizeof(struct icmp_thread_request_t *));
240: if (byte_count == -1) {
241: error(
242: EXIT_FAILURE, errno,
243: "failure writing to probe result queue");
244: }
245: }
246:
247: /*
1.1 misho 248: The overlapped I/O style completion routine to be called by
249: Windows during an altertable wait when an ICMP probe has
250: completed, either by reply, or by ICMP.DLL timeout.
251: */
252: static
253: void WINAPI on_icmp_reply(
254: PVOID context,
255: PIO_STATUS_BLOCK status,
256: ULONG reserved)
257: {
1.1.1.2 misho 258: struct icmp_thread_request_t *request =
259: (struct icmp_thread_request_t *) context;
1.1 misho 260: int icmp_type;
261: int round_trip_us = 0;
262: int reply_count;
263: int reply_status = 0;
264: struct sockaddr_storage remote_addr;
265: struct sockaddr_in *remote_addr4;
266: struct sockaddr_in6 *remote_addr6;
1.1.1.2 misho 267: ICMP_ECHO_REPLY *reply4;
1.1 misho 268: ICMPV6_ECHO_REPLY *reply6;
269:
1.1.1.2 misho 270: if (request->ip_version == 6) {
271: reply6 = request->reply6;
1.1 misho 272: reply_count = Icmp6ParseReplies(reply6, sizeof(ICMPV6_ECHO_REPLY));
273:
274: if (reply_count > 0) {
275: reply_status = reply6->Status;
276:
277: /* Unfortunately, ICMP.DLL only has millisecond precision */
278: round_trip_us = reply6->RoundTripTime * 1000;
279:
280: remote_addr6 = (struct sockaddr_in6 *) &remote_addr;
281: remote_addr6->sin6_family = AF_INET6;
282: remote_addr6->sin6_port = 0;
283: remote_addr6->sin6_flowinfo = 0;
284: memcpy(&remote_addr6->sin6_addr, reply6->AddressBits,
285: sizeof(struct in6_addr));
286: remote_addr6->sin6_scope_id = 0;
287: }
288: } else {
1.1.1.2 misho 289: reply4 = request->reply4;
1.1 misho 290: reply_count = IcmpParseReplies(reply4, sizeof(ICMP_ECHO_REPLY));
291:
292: if (reply_count > 0) {
293: reply_status = reply4->Status;
294:
295: /* Unfortunately, ICMP.DLL only has millisecond precision */
296: round_trip_us = reply4->RoundTripTime * 1000;
297:
298: remote_addr4 = (struct sockaddr_in *) &remote_addr;
299: remote_addr4->sin_family = AF_INET;
300: remote_addr4->sin_port = 0;
301: remote_addr4->sin_addr.s_addr = reply4->Address;
302: }
303: }
304:
305: if (reply_count == 0) {
306: reply_status = GetLastError();
307: }
308:
309: icmp_type = -1;
310: if (reply_status == IP_SUCCESS) {
311: icmp_type = ICMP_ECHOREPLY;
312: } else if (reply_status == IP_TTL_EXPIRED_TRANSIT
313: || reply_status == IP_TTL_EXPIRED_REASSEM) {
314:
1.1.1.2 misho 315: icmp_type = ICMP_TIME_EXCEEDED;
316: } else if (reply_status == IP_DEST_HOST_UNREACHABLE
317: || reply_status == IP_DEST_PORT_UNREACHABLE
318: || reply_status == IP_DEST_PROT_UNREACHABLE
319: || reply_status == IP_DEST_NET_UNREACHABLE
320: || reply_status == IP_DEST_UNREACHABLE
321: || reply_status == IP_DEST_NO_ROUTE
322: || reply_status == IP_BAD_ROUTE
323: || reply_status == IP_BAD_DESTINATION) {
324:
325: icmp_type = ICMP_DEST_UNREACH;
326: }
327:
328: request->icmp_type = icmp_type;
329: request->reply_status = reply_status;
330: request->remote_addr = remote_addr;
331: request->round_trip_us = round_trip_us;
332: queue_thread_result(request);
1.1 misho 333: }
334:
335: /* Use ICMP.DLL's send echo support to send a probe */
336: static
337: void icmp_send_probe(
1.1.1.2 misho 338: struct icmp_thread_request_t *request,
1.1 misho 339: char *payload,
340: int payload_size)
341: {
342: IP_OPTION_INFORMATION option;
343: DWORD timeout;
344: DWORD send_result;
345: int reply_size;
346: int err;
347: struct sockaddr_in *dest_sockaddr4;
348: struct sockaddr_in6 *src_sockaddr6;
349: struct sockaddr_in6 *dest_sockaddr6;
350:
1.1.1.2 misho 351: if (request->timeout > 0) {
352: timeout = 1000 * request->timeout;
1.1 misho 353: } else {
354: /*
1.1.1.3 ! misho 355: IcmpSendEcho2 will return invalid argument on a timeout of
1.1 misho 356: zero. Our Unix implementation allows it. Bump up the timeout
357: to 1 millisecond.
358: */
359: timeout = 1;
360: }
361:
1.1.1.2 misho 362: memset(&option, 0, sizeof(IP_OPTION_INFORMATION));
363: option.Ttl = request->ttl;
1.1 misho 364:
1.1.1.2 misho 365: if (request->ip_version == 6) {
1.1 misho 366: reply_size = sizeof(ICMPV6_ECHO_REPLY) + payload_size;
367: } else {
1.1.1.2 misho 368: reply_size = sizeof(ICMP_ECHO_REPLY) + payload_size;
1.1 misho 369: }
370:
1.1.1.2 misho 371: request->reply4 = malloc(reply_size);
372: if (request->reply4 == NULL) {
373: error(EXIT_FAILURE, errno, "failure to allocate reply buffer");
1.1 misho 374: }
375:
1.1.1.2 misho 376: if (request->ip_version == 6) {
377: src_sockaddr6 = (struct sockaddr_in6 *) &request->src_sockaddr;
378: dest_sockaddr6 = (struct sockaddr_in6 *) &request->dest_sockaddr;
1.1 misho 379:
1.1.1.2 misho 380: send_result = Icmp6SendEcho2(request->net_state->platform.icmp6,
381: NULL,
382: (FARPROC) on_icmp_reply,
383: request,
1.1 misho 384: src_sockaddr6, dest_sockaddr6,
385: payload, payload_size, &option,
1.1.1.2 misho 386: request->reply6,
387: reply_size, timeout);
1.1 misho 388: } else {
1.1.1.2 misho 389: dest_sockaddr4 = (struct sockaddr_in *) &request->dest_sockaddr;
1.1 misho 390:
1.1.1.2 misho 391: send_result = IcmpSendEcho2(request->net_state->platform.icmp4,
392: NULL,
393: (FARPROC) on_icmp_reply,
394: request,
1.1 misho 395: dest_sockaddr4->sin_addr.s_addr,
396: payload, payload_size, &option,
1.1.1.2 misho 397: request->reply4,
398: reply_size, timeout);
1.1 misho 399: }
400:
401: if (send_result == 0) {
402: err = GetLastError();
403:
404: /*
1.1.1.2 misho 405: ERROR_IO_PENDING is expected when the probe is sent.
406: Other errors indicate the probe wasn't sent, and should
407: be reported in the main thread.
408: */
1.1 misho 409: if (err != ERROR_IO_PENDING) {
1.1.1.2 misho 410: request->icmp_type = -1;
411: request->reply_status = err;
412: queue_thread_result(request);
1.1 misho 413: }
414: }
415: }
416:
417: /* Fill the payload of the packet as specified by the probe parameters */
418: static
419: int fill_payload(
1.1.1.2 misho 420: const struct icmp_thread_request_t *request,
1.1 misho 421: char *payload,
422: int payload_buffer_size)
423: {
424: int ip_icmp_size;
425: int payload_size;
426:
1.1.1.2 misho 427: if (request->ip_version == 6) {
1.1 misho 428: ip_icmp_size =
429: sizeof(struct IP6Header) + sizeof(struct ICMPHeader);
1.1.1.2 misho 430: } else if (request->ip_version == 4) {
1.1 misho 431: ip_icmp_size = sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
432: } else {
433: errno = EINVAL;
434: return -1;
435: }
436:
1.1.1.2 misho 437: payload_size = request->packet_size - ip_icmp_size;
1.1 misho 438: if (payload_size < 0) {
439: payload_size = 0;
440: }
441:
442: if (payload_size > payload_buffer_size) {
443: errno = EINVAL;
444: return -1;
445: }
446:
1.1.1.2 misho 447: memset(payload, request->bit_pattern, payload_size);
1.1 misho 448:
449: return payload_size;
450: }
451:
1.1.1.2 misho 452: /*
453: We've received a probe request from the main thread, so
454: fill out a payload buffer and then send the probe.
455: */
456: static
457: void icmp_handle_probe_request(struct icmp_thread_request_t *request)
458: {
459: char payload[PACKET_BUFFER_SIZE];
460: int payload_size;
461:
462: payload_size = fill_payload(request, payload, PACKET_BUFFER_SIZE);
463: if (payload_size < 0) {
464: error(EXIT_FAILURE, errno, "Error constructing packet");
465: }
466:
467: icmp_send_probe(request, payload, payload_size);
468: }
469:
470: /*
471: The main loop of the ICMP service thread. The loop starts
472: an overlapped read on the incoming request pipe, then waits
473: in an alertable wait for that read to complete. Because
474: the wait is alertable, ICMP probes can complete through
475: APCs in that wait.
476: */
477: static
478: DWORD WINAPI icmp_service_thread(LPVOID param) {
479: struct net_state_t *net_state;
480: struct icmp_thread_request_t *request;
481: DWORD wait_status;
482: OVERLAPPED overlapped;
483: HANDLE event;
484: BOOL success;
485: bool read_pending;
486: DWORD read_count;
487: int err;
488:
489: /*
490: We need an event to signal completion of reads from the request
491: pipe.
492: */
493: event = CreateEvent(NULL, TRUE, FALSE, NULL);
494: if (event == NULL) {
495: error_win(
496: EXIT_FAILURE, GetLastError(),
497: "failure creating ICMP thread event");
498: }
499:
500: net_state = (struct net_state_t *)param;
501: read_pending = false;
502: while (true) {
503: /*
504: Start a new read on the request pipe if none is
505: currently pending.
506: */
507: if (!read_pending) {
508: request = NULL;
509:
510: ResetEvent(event);
511:
512: memset(&overlapped, 0, sizeof(OVERLAPPED));
513: overlapped.hEvent = event;
514:
515: success = ReadFile(
516: net_state->platform.thread_in_pipe_read_handle,
517: &request,
518: sizeof(struct icmp_thread_request_t *),
519: NULL,
520: &overlapped);
521:
522: if (!success) {
523: err = GetLastError();
524:
525: if (err != ERROR_IO_PENDING) {
526: error_win(
527: EXIT_FAILURE, err,
528: "failure starting overlapped thread pipe read");
529: }
530: }
531:
532: read_pending = true;
533: }
534:
535: /*
536: Wait for either the request read to complete, or
537: an APC which completes an ICMP probe.
538: */
539: wait_status = WaitForSingleObjectEx(
540: event,
541: INFINITE,
542: TRUE);
543:
544: /*
545: If the event we waited on has been signalled, read
546: the request from the pipe.
547: */
548: if (wait_status == WAIT_OBJECT_0) {
549: read_pending = false;
550:
551: success = GetOverlappedResult(
552: net_state->platform.thread_in_pipe_read_handle,
553: &overlapped,
554: &read_count,
555: FALSE);
556:
557: if (!success) {
558: error_win(
559: EXIT_FAILURE, GetLastError(),
560: "failure completing overlapped thread pipe read");
561: }
562:
563: if (read_count == 0) {
564: continue;
565: }
566:
567: assert(
568: read_count == sizeof(struct icmp_thread_request_t *));
569:
570: /* Start the new probe from the request */
571: icmp_handle_probe_request(request);
572: }
573: }
574: }
575:
576: /*
577: When we are on the main thread and need the ICMP service thread
578: to start a new probe, this is used to pass the request for the
579: new probe to the service thread.
580: */
581: static
582: void queue_thread_request(
583: struct net_state_t *net_state,
584: struct probe_t *probe,
585: const struct probe_param_t *param,
586: struct sockaddr_storage *dest_sockaddr,
587: struct sockaddr_storage *src_sockaddr)
588: {
589: struct icmp_thread_request_t *request;
590: int byte_count;
591:
592: request = malloc(sizeof(struct icmp_thread_request_t));
593: if (request == NULL) {
594: error(EXIT_FAILURE, errno, "failure to allocate request");
595: }
596: memset(request, 0, sizeof(struct icmp_thread_request_t));
597:
598: request->ip_version = param->ip_version;
599: request->ttl = param->ttl;
600: request->timeout = param->timeout;
601: request->packet_size = param->packet_size;
602: request->bit_pattern = param->bit_pattern;
603:
604: request->net_state = net_state;
605: request->probe = probe;
606: request->dest_sockaddr = *dest_sockaddr;
607: request->src_sockaddr = *src_sockaddr;
608:
609: /*
610: The ownership of the request is passed to the ICMP thread
611: through the pipe.
612: */
613: byte_count = write(
614: net_state->platform.thread_in_pipe_write,
615: &request,
616: sizeof(struct icmp_thread_request_t *));
617:
618: if (byte_count == -1) {
619: error(
620: EXIT_FAILURE, errno,
621: "failure writing to probe request queue");
622: }
623: }
624:
1.1 misho 625: /* Decode the probe parameters and send a probe */
626: void send_probe(
627: struct net_state_t *net_state,
628: const struct probe_param_t *param)
629: {
630: struct probe_t *probe;
631: struct sockaddr_storage dest_sockaddr;
632: struct sockaddr_storage src_sockaddr;
633:
1.1.1.2 misho 634: if (resolve_probe_addresses(net_state, param, &dest_sockaddr,
635: &src_sockaddr)) {
1.1 misho 636: printf("%d invalid-argument\n", param->command_token);
637: return;
638: }
639:
640: probe = alloc_probe(net_state, param->command_token);
641: if (probe == NULL) {
642: printf("%d probes-exhausted\n", param->command_token);
643: return;
644: }
645:
646: probe->platform.ip_version = param->ip_version;
647:
1.1.1.2 misho 648: queue_thread_request(
649: net_state, probe, param, &dest_sockaddr, &src_sockaddr);
650: }
651:
652: /*
653: After we've receive the result from the ICMP service thread,
654: report either the probe status, or any Windows error we
655: encountered while attempting to send the probe.
656: */
657: static
658: void complete_icmp_result(struct icmp_thread_request_t *request)
659: {
660: struct net_state_t *net_state;
661: struct probe_t *probe;
662:
663: /*
664: We can de-const the net_state and probe, since we are back
665: on the main thread.
666: */
667: net_state = (struct net_state_t *)request->net_state;
668: probe = (struct probe_t *)request->probe;
1.1 misho 669:
1.1.1.2 misho 670: if (request->icmp_type != -1) {
671: /* Record probe result */
672: respond_to_probe(net_state, probe,
673: request->icmp_type, &request->remote_addr,
674: request->round_trip_us, 0, NULL);
675: } else {
676: report_win_error(probe->token, request->reply_status);
677: free_probe(net_state, probe);
678: }
1.1 misho 679: }
680:
681: /*
1.1.1.2 misho 682: Read the status of completed probes from the ICMP service
683: if any has completed.
1.1 misho 684: */
685: void receive_replies(
686: struct net_state_t *net_state)
687: {
1.1.1.2 misho 688: int read_count;
689: struct icmp_thread_request_t *request;
690:
691: read_count = read(
692: net_state->platform.thread_out_pipe_read,
693: &request,
694: sizeof(struct icmp_thread_request_t *));
695:
696: if (read_count == -1) {
697: /*
698: EINTR and EAGAIN can occur under normal conditions, and
699: should be retried. We will retry the next iteration
700: of the main loop.
701: */
702: if (errno == EINTR || errno == EAGAIN) {
703: return;
704: }
705:
706: error(EXIT_FAILURE, errno, "thread result pipe read error");
707: }
708:
709: assert(read_count == sizeof(struct icmp_thread_request_t *));
710: complete_icmp_result(request);
711:
712: if (request->reply4) {
713: free(request->reply4);
714: request->reply4 = NULL;
715: }
716: free(request);
1.1 misho 717: }
718:
719: /*
1.1.1.2 misho 720: On Windows, an implementation of check_probe_timeout is unnecessary because
1.1 misho 721: timeouts are managed by ICMP.DLL, including a call to the I/O completion
722: routine when the time fully expires.
723: */
724: void check_probe_timeouts(
725: struct net_state_t *net_state)
726: {
727: }
728:
729: /*
730: As in the case of check_probe_timeout, getting the next probe timeout is
731: unnecessary under Windows, as ICMP.DLL manages timeouts for us.
732: */
733: bool get_next_probe_timeout(
734: const struct net_state_t *net_state,
735: struct timeval *timeout)
736: {
737: return false;
738: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>