Remove unused NONAMELESS defines.
[wine.git] / dlls / nsiproxy.sys / icmp_echo.c
blobe92fa4394c14f557226b14ef143e5d4bdb92e316
1 /*
2 * nsiproxy.sys icmp_echo implementation
4 * Copyright 2021 Huw Davies
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #if 0
21 #pragma makedep unix
22 #endif
24 #include "config.h"
25 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <limits.h>
31 #include <pthread.h>
32 #include <poll.h>
33 #include <sys/socket.h>
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
39 #ifdef HAVE_NETINET_IP_H
40 #include <netinet/ip.h>
41 #endif
43 #include "ntstatus.h"
44 #define WIN32_NO_STATUS
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winternl.h"
48 #include "winioctl.h"
49 #define USE_WS_PREFIX
50 #include "ddk/wdm.h"
51 #include "ifdef.h"
52 #include "netiodef.h"
53 #include "ipexport.h"
54 #include "ipmib.h"
55 #include "wine/nsi.h"
56 #include "wine/debug.h"
58 #include "nsiproxy_private.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(nsi);
62 static LONG icmp_sequence;
64 struct ip_hdr
66 uint8_t v_hl; /* version << 4 | hdr_len */
67 uint8_t tos;
68 uint16_t tot_len;
69 uint16_t id;
70 uint16_t frag_off;
71 uint8_t ttl;
72 uint8_t protocol;
73 uint16_t checksum;
74 uint32_t saddr;
75 uint32_t daddr;
78 struct icmp_hdr
80 uint8_t type;
81 uint8_t code;
82 uint16_t checksum;
83 union
85 struct
87 uint16_t id;
88 uint16_t sequence;
89 } echo;
90 } un;
93 struct icmp_reply_ctx
95 SOCKADDR_INET addr;
96 ULONG status;
97 ULONG round_trip_time;
98 LONG data_size;
99 BYTE ttl;
100 BYTE tos;
101 BYTE flags;
102 BYTE options_size;
103 void *options_data;
104 void *data;
107 struct family_ops;
108 struct icmp_data
110 LARGE_INTEGER send_time;
111 int socket;
112 int cancel_pipe[2];
113 unsigned short id;
114 unsigned short seq;
115 const struct family_ops *ops;
118 #define MAX_HANDLES 256 /* Max number of simultaneous pings - could become dynamic if need be */
119 static struct icmp_data *handle_table[MAX_HANDLES];
120 static pthread_mutex_t handle_lock = PTHREAD_MUTEX_INITIALIZER;
121 static struct icmp_data **next_free, **next_unused = handle_table;
123 static icmp_handle handle_alloc( struct icmp_data *data )
125 struct icmp_data **entry;
126 icmp_handle h;
128 pthread_mutex_lock( &handle_lock );
129 entry = next_free;
130 if (entry) next_free = *(struct icmp_data ***)entry;
131 else if (next_unused < handle_table + MAX_HANDLES) entry = next_unused++;
132 else
134 pthread_mutex_unlock( &handle_lock );
135 FIXME( "Exhausted icmp handle count\n" );
136 return 0;
138 *entry = data;
139 h = entry - handle_table + 1;
140 pthread_mutex_unlock( &handle_lock );
141 TRACE( "returning handle %x\n", h );
142 return h;
145 static struct icmp_data **handle_entry( icmp_handle h )
147 if (!h || h > MAX_HANDLES)
149 ERR( "Invalid icmp handle\n" );
150 return NULL;
152 return handle_table + h - 1;
155 static struct icmp_data *handle_data( icmp_handle h )
157 struct icmp_data **entry = handle_entry( h );
159 if (!entry) return NULL;
160 return *entry;
163 static void handle_free( icmp_handle h )
165 struct icmp_data **entry;
167 TRACE( "%x\n", h );
168 pthread_mutex_lock( &handle_lock );
169 entry = handle_entry( h );
170 if (entry)
172 *(struct icmp_data ***)entry = next_free;
173 next_free = entry;
175 pthread_mutex_unlock( &handle_lock );
178 static void ipv4_init_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp_hdr )
180 icmp_hdr->type = ICMP4_ECHO_REQUEST;
181 icmp_hdr->code = 0;
182 icmp_hdr->checksum = 0;
183 icmp_hdr->un.echo.id = data->id = getpid() & 0xffff; /* will be overwritten for linux ping socks */
184 icmp_hdr->un.echo.sequence = data->seq = InterlockedIncrement( &icmp_sequence ) & 0xffff;
187 /* rfc 1071 checksum */
188 static unsigned short chksum( BYTE *data, unsigned int count )
190 unsigned int sum = 0, carry = 0;
191 unsigned short check, s;
193 while (count > 1)
195 s = *(unsigned short *)data;
196 data += 2;
197 sum += carry;
198 sum += s;
199 carry = s > sum;
200 count -= 2;
202 sum += carry; /* This won't produce another carry */
203 sum = (sum & 0xffff) + (sum >> 16);
205 if (count) sum += *data; /* LE-only */
207 sum = (sum & 0xffff) + (sum >> 16);
208 /* fold in any carry */
209 sum = (sum & 0xffff) + (sum >> 16);
211 check = ~sum;
212 return check;
215 #ifdef __linux__
216 static unsigned short null_chksum( BYTE *data, unsigned int count )
218 return 0;
220 #endif
222 static int ipv4_set_reply_ip_status( IP_STATUS ip_status, unsigned int bits, void *out )
224 if (bits == 32)
226 struct icmp_echo_reply_32 *reply = out;
227 memset( reply, 0, sizeof(*reply) );
228 reply->status = ip_status;
229 return sizeof(*reply);
231 else
233 struct icmp_echo_reply_64 *reply = out;
234 memset( reply, 0, sizeof(*reply) );
235 reply->status = ip_status;
236 return sizeof(*reply);
240 static void ipv4_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params )
242 int val;
244 val = params->ttl;
245 if (val) setsockopt( data->socket, IPPROTO_IP, IP_TTL, &val, sizeof(val) );
246 val = params->tos;
247 if (val) setsockopt( data->socket, IPPROTO_IP, IP_TOS, &val, sizeof(val) );
250 #ifdef __linux__
251 static void ipv4_linux_ping_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params )
253 static const int val = 1;
255 ipv4_set_socket_opts( data, params );
257 setsockopt( data->socket, IPPROTO_IP, IP_RECVTTL, &val, sizeof(val) );
258 setsockopt( data->socket, IPPROTO_IP, IP_RECVTOS, &val, sizeof(val) );
260 #endif
262 static int ipv4_reply_buffer_len( struct icmp_listen_params *params )
264 int struct_len = (params->bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64);
265 return sizeof(struct ip_hdr) + sizeof(struct icmp_hdr) + params->reply_len - struct_len;
268 #ifdef __linux__
269 static int ipv4_linux_ping_reply_buffer_len( struct icmp_listen_params *params )
271 int struct_len = (params->bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64);
272 return sizeof(struct icmp_hdr) + params->reply_len - struct_len;
274 #endif
276 static BOOL ipv4_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip_hdr_len,
277 struct icmp_reply_ctx *ctx )
279 struct ip_hdr *ip_hdr;
281 if (recvd < sizeof(*ip_hdr)) return FALSE;
282 ip_hdr = msg->msg_iov[0].iov_base;
283 if (ip_hdr->v_hl >> 4 != 4 || ip_hdr->protocol != IPPROTO_ICMP) return FALSE;
284 *ip_hdr_len = (ip_hdr->v_hl & 0xf) << 2;
285 if (*ip_hdr_len < sizeof(*ip_hdr)) return FALSE;
286 ctx->options_data = ip_hdr + 1;
287 ctx->ttl = ip_hdr->ttl;
288 ctx->tos = ip_hdr->tos;
289 ctx->flags = ip_hdr->frag_off >> 13;
290 ctx->options_size = *ip_hdr_len - sizeof(*ip_hdr);
292 return TRUE;
295 #ifdef __linux__
296 static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd, int *ip_hdr_len,
297 struct icmp_reply_ctx *ctx )
299 struct cmsghdr *cmsg;
301 *ip_hdr_len = 0;
302 ctx->options_data = NULL;
303 ctx->ttl = 0;
304 ctx->tos = 0;
305 ctx->flags = 0;
306 ctx->options_size = 0; /* FIXME from IP_OPTIONS but will require checking for space in the reply */
308 for (cmsg = CMSG_FIRSTHDR( msg ); cmsg; cmsg = CMSG_NXTHDR( msg, cmsg ))
310 if (cmsg->cmsg_level != IPPROTO_IP) continue;
311 switch (cmsg->cmsg_type)
313 case IP_TTL:
314 ctx->ttl = *(BYTE *)CMSG_DATA( cmsg );
315 break;
316 case IP_TOS:
317 ctx->tos = *(BYTE *)CMSG_DATA( cmsg );
318 break;
321 return TRUE;
323 #endif
325 static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size,
326 struct icmp_reply_ctx *ctx, int ping_socket )
328 static const IP_STATUS unreach_codes[] =
330 IP_DEST_NET_UNREACHABLE, /* ICMP_UNREACH_NET */
331 IP_DEST_HOST_UNREACHABLE, /* ICMP_UNREACH_HOST */
332 IP_DEST_PROT_UNREACHABLE, /* ICMP_UNREACH_PROTOCOL */
333 IP_DEST_PORT_UNREACHABLE, /* ICMP_UNREACH_PORT */
334 IP_PACKET_TOO_BIG, /* ICMP_UNREACH_NEEDFRAG */
335 IP_BAD_ROUTE, /* ICMP_UNREACH_SRCFAIL */
336 IP_DEST_NET_UNREACHABLE, /* ICMP_UNREACH_NET_UNKNOWN */
337 IP_DEST_HOST_UNREACHABLE, /* ICMP_UNREACH_HOST_UNKNOWN */
338 IP_DEST_HOST_UNREACHABLE, /* ICMP_UNREACH_ISOLATED */
339 IP_DEST_NET_UNREACHABLE, /* ICMP_UNREACH_NET_PROHIB */
340 IP_DEST_HOST_UNREACHABLE, /* ICMP_UNREACH_HOST_PROHIB */
341 IP_DEST_NET_UNREACHABLE, /* ICMP_UNREACH_TOSNET */
342 IP_DEST_HOST_UNREACHABLE, /* ICMP_UNREACH_TOSHOST */
343 IP_DEST_HOST_UNREACHABLE, /* ICMP_UNREACH_FILTER_PROHIB */
344 IP_DEST_HOST_UNREACHABLE, /* ICMP_UNREACH_HOST_PRECEDENCE */
345 IP_DEST_HOST_UNREACHABLE /* ICMP_UNREACH_PRECEDENCE_CUTOFF */
347 const struct ip_hdr *orig_ip_hdr;
348 const struct icmp_hdr *orig_icmp_hdr;
349 int orig_ip_hdr_len;
350 IP_STATUS status;
352 switch (icmp->type)
354 case ICMP4_ECHO_REPLY:
355 if ((!ping_socket && icmp->un.echo.id != data->id) ||
356 icmp->un.echo.sequence != data->seq) return -1;
358 ctx->status = IP_SUCCESS;
359 return icmp_size - sizeof(*icmp);
361 case ICMP4_DST_UNREACH:
362 if (icmp->code < ARRAY_SIZE(unreach_codes))
363 status = unreach_codes[icmp->code];
364 else
365 status = IP_DEST_HOST_UNREACHABLE;
366 break;
368 case ICMP4_TIME_EXCEEDED:
369 if (icmp->code == 1) /* ICMP_TIMXCEED_REASS */
370 status = IP_TTL_EXPIRED_REASSEM;
371 else
372 status = IP_TTL_EXPIRED_TRANSIT;
373 break;
375 case ICMP4_PARAM_PROB:
376 status = IP_PARAM_PROBLEM;
377 break;
379 case ICMP4_SOURCE_QUENCH:
380 status = IP_SOURCE_QUENCH;
381 break;
383 default:
384 return -1;
387 /* Check that the appended packet is really ours -
388 * all handled icmp replies have an 8-byte header
389 * followed by the original ip hdr. */
390 if (icmp_size < sizeof(*icmp) + sizeof(*orig_ip_hdr)) return -1;
391 orig_ip_hdr = (struct ip_hdr *)(icmp + 1);
392 if (orig_ip_hdr->v_hl >> 4 != 4 || orig_ip_hdr->protocol != IPPROTO_ICMP) return -1;
393 orig_ip_hdr_len = (orig_ip_hdr->v_hl & 0xf) << 2;
394 if (icmp_size < sizeof(*icmp) + orig_ip_hdr_len + sizeof(*orig_icmp_hdr)) return -1;
395 orig_icmp_hdr = (const struct icmp_hdr *)((const BYTE *)orig_ip_hdr + orig_ip_hdr_len);
396 if (orig_icmp_hdr->type != ICMP4_ECHO_REQUEST ||
397 orig_icmp_hdr->code != 0 ||
398 (!ping_socket && orig_icmp_hdr->un.echo.id != data->id) ||
399 orig_icmp_hdr->un.echo.sequence != data->seq) return -1;
401 ctx->status = status;
402 return 0;
405 static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp,
406 int icmp_size, struct icmp_reply_ctx *ctx)
408 return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, ctx, 0 );
411 #ifdef __linux__
412 static int ipv4_linux_ping_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp,
413 int icmp_size, struct icmp_reply_ctx *ctx )
415 return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, ctx, 1 );
417 #endif
419 static void ipv4_fill_reply( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx)
421 void *options_data;
422 ULONG data_offset;
423 if (params->bits == 32)
425 struct icmp_echo_reply_32 *reply = params->reply;
426 data_offset = sizeof(*reply) + ((ctx->options_size + 3) & ~3);
427 reply->addr = ctx->addr.Ipv4.sin_addr.WS_s_addr;
428 reply->status = ctx->status;
429 reply->round_trip_time = ctx->round_trip_time;
430 reply->data_size = ctx->data_size;
431 reply->num_of_pkts = 1;
432 reply->data_ptr = params->user_reply_ptr + data_offset;
433 reply->opts.ttl = ctx->ttl;
434 reply->opts.tos = ctx->tos;
435 reply->opts.flags = ctx->flags;
436 reply->opts.options_size = ctx->options_size;
437 reply->opts.options_ptr = params->user_reply_ptr + sizeof(*reply);
438 options_data = reply + 1;
440 else
442 struct icmp_echo_reply_64 *reply = params->reply;
443 data_offset = sizeof(*reply) + ((ctx->options_size + 3) & ~3);
444 reply->addr = ctx->addr.Ipv4.sin_addr.WS_s_addr;
445 reply->status = ctx->status;
446 reply->round_trip_time = ctx->round_trip_time;
447 reply->data_size = ctx->data_size;
448 reply->num_of_pkts = 1;
449 reply->data_ptr = params->user_reply_ptr + data_offset;
450 reply->opts.ttl = ctx->ttl;
451 reply->opts.tos = ctx->tos;
452 reply->opts.flags = ctx->flags;
453 reply->opts.options_size = ctx->options_size;
454 reply->opts.options_ptr = params->user_reply_ptr + sizeof(*reply);
455 options_data = reply + 1;
458 memcpy( options_data, ctx->options_data, ctx->options_size );
459 if (ctx->options_size & 3)
460 memset( (char *)options_data + ctx->options_size, 0, 4 - (ctx->options_size & 3) );
462 memcpy( (char *)params->reply + data_offset, ctx->data, ctx->data_size );
463 params->reply_len = data_offset + ctx->data_size;
466 struct family_ops
468 int family;
469 int icmp_protocol;
470 void (*init_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp_hdr );
471 unsigned short (*chksum)( BYTE *data, unsigned int count );
472 int (*set_reply_ip_status)( IP_STATUS ip_status, unsigned int bits, void *out );
473 void (*set_socket_opts)( struct icmp_data *data, struct icmp_send_echo_params *params );
474 int (*reply_buffer_len)( struct icmp_listen_params *params );
475 BOOL (*parse_ip_hdr)( struct msghdr *msg, int recvd, int *ip_hdr_len, struct icmp_reply_ctx *ctx );
476 int (*parse_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_len, struct icmp_reply_ctx *ctx );
477 void (*fill_reply)( struct icmp_listen_params *params, struct icmp_reply_ctx *ctx );
480 static const struct family_ops ipv4 =
482 AF_INET,
483 IPPROTO_ICMP,
484 ipv4_init_icmp_hdr,
485 chksum,
486 ipv4_set_reply_ip_status,
487 ipv4_set_socket_opts,
488 ipv4_reply_buffer_len,
489 ipv4_parse_ip_hdr,
490 ipv4_parse_icmp_hdr,
491 ipv4_fill_reply,
494 #ifdef __linux__
495 /* linux ipv4 ping sockets behave more like ipv6 raw sockets */
496 static const struct family_ops ipv4_linux_ping =
498 AF_INET,
499 IPPROTO_ICMP,
500 ipv4_init_icmp_hdr,
501 null_chksum,
502 ipv4_set_reply_ip_status,
503 ipv4_linux_ping_set_socket_opts,
504 ipv4_linux_ping_reply_buffer_len,
505 ipv4_linux_ping_parse_ip_hdr,
506 ipv4_linux_ping_parse_icmp_hdr,
507 ipv4_fill_reply,
509 #endif
511 static IP_STATUS errno_to_ip_status( int err )
513 switch( err )
515 case EHOSTUNREACH: return IP_DEST_HOST_UNREACHABLE;
516 default: return IP_GENERAL_FAILURE;
520 static int SOCKADDR_INET_to_sockaddr( const SOCKADDR_INET *in, struct sockaddr *out, int len )
522 switch (in->si_family)
524 case WS_AF_INET:
526 struct sockaddr_in *sa = (struct sockaddr_in *)out;
528 if (len < sizeof(*sa)) return 0;
529 sa->sin_family = AF_INET;
530 sa->sin_port = in->Ipv4.sin_port;
531 sa->sin_addr.s_addr = in->Ipv4.sin_addr.WS_s_addr;
532 return sizeof(*sa);
534 case WS_AF_INET6:
536 struct sockaddr_in6 *sa = (struct sockaddr_in6 *)out;
538 if (len < sizeof(*sa)) return 0;
539 sa->sin6_family = AF_INET6;
540 sa->sin6_port = in->Ipv6.sin6_port;
541 sa->sin6_flowinfo = in->Ipv6.sin6_flowinfo;
542 memcpy( sa->sin6_addr.s6_addr, in->Ipv6.sin6_addr.WS_s6_addr, sizeof(sa->sin6_addr.s6_addr) );
543 sa->sin6_scope_id = in->Ipv6.sin6_scope_id;
544 return sizeof(*sa);
547 return 0;
550 static BOOL sockaddr_to_SOCKADDR_INET( const struct sockaddr *in, SOCKADDR_INET *out )
552 switch (in->sa_family)
554 case AF_INET:
556 struct sockaddr_in *sa = (struct sockaddr_in *)in;
558 out->Ipv4.sin_family = WS_AF_INET;
559 out->Ipv4.sin_port = sa->sin_port;
560 out->Ipv4.sin_addr.WS_s_addr = sa->sin_addr.s_addr;
561 return TRUE;
563 case AF_INET6:
565 struct sockaddr_in6 *sa = (struct sockaddr_in6 *)in;
567 out->Ipv6.sin6_family = WS_AF_INET6;
568 out->Ipv6.sin6_port = sa->sin6_port;
569 out->Ipv6.sin6_flowinfo = sa->sin6_flowinfo;
570 memcpy( out->Ipv6.sin6_addr.WS_s6_addr, sa->sin6_addr.s6_addr, sizeof(sa->sin6_addr.s6_addr) );
571 out->Ipv6.sin6_scope_id = sa->sin6_scope_id;
572 return TRUE;
575 return FALSE;
578 static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data **icmp_data )
580 struct icmp_data *data;
581 const struct family_ops *ops;
583 if (win_family == WS_AF_INET) ops = &ipv4;
584 else return STATUS_INVALID_PARAMETER;
586 data = malloc( sizeof(*data) );
587 if (!data) return STATUS_NO_MEMORY;
589 data->socket = socket( ops->family, SOCK_RAW, ops->icmp_protocol );
590 if (data->socket < 0) /* Try a ping-socket */
592 TRACE( "failed to open raw sock, trying a dgram sock\n" );
593 data->socket = socket( ops->family, SOCK_DGRAM, ops->icmp_protocol );
594 if (data->socket < 0)
596 WARN( "Unable to create socket\n" );
597 free( data );
598 return STATUS_ACCESS_DENIED;
600 #ifdef __linux__
601 if (ops->family == AF_INET) ops = &ipv4_linux_ping;
602 #endif
604 if (pipe( data->cancel_pipe ))
606 close( data->socket );
607 free( data );
608 return STATUS_ACCESS_DENIED;
611 data->ops = ops;
612 *icmp_data = data;
613 return STATUS_SUCCESS;
616 static void icmp_data_free( struct icmp_data *data )
618 close( data->socket );
619 close( data->cancel_pipe[0] );
620 close( data->cancel_pipe[1] );
621 free( data );
624 NTSTATUS icmp_send_echo( void *args )
626 struct icmp_send_echo_params *params = args;
627 struct icmp_hdr *icmp_hdr; /* this is the same for both ipv4 and ipv6 */
628 struct sockaddr_storage dst_storage;
629 struct sockaddr *dst = (struct sockaddr *)&dst_storage;
630 struct icmp_data *data;
631 int dst_len, ret;
632 NTSTATUS status;
634 status = icmp_data_create( params->dst->si_family, &data );
635 if (status) return status;
636 data->ops->set_socket_opts( data, params );
638 icmp_hdr = malloc( sizeof(*icmp_hdr) + params->request_size );
639 if (!icmp_hdr)
641 icmp_data_free( data );
642 return STATUS_NO_MEMORY;
644 data->ops->init_icmp_hdr( data, icmp_hdr );
645 memcpy( icmp_hdr + 1, params->request, params->request_size );
646 icmp_hdr->checksum = data->ops->chksum( (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size );
648 dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(dst_storage) );
650 NtQueryPerformanceCounter( &data->send_time, NULL );
651 ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, dst_len );
652 free( icmp_hdr );
654 if (ret < 0)
656 TRACE( "sendto() rets %d errno %d\n", ret, errno );
657 params->reply_len = data->ops->set_reply_ip_status( errno_to_ip_status( errno ), params->bits, params->reply );
658 icmp_data_free( data );
659 return STATUS_SUCCESS;
662 *params->handle = handle_alloc( data );
663 if (!*params->handle) icmp_data_free( data );
664 return *params->handle ? STATUS_PENDING : STATUS_NO_MEMORY;
667 static int get_timeout( LARGE_INTEGER start, UINT timeout )
669 LARGE_INTEGER now, end;
671 end.QuadPart = start.QuadPart + (ULONGLONG)timeout * 10000;
672 NtQueryPerformanceCounter( &now, NULL );
673 if (now.QuadPart >= end.QuadPart) return 0;
675 return min( (end.QuadPart - now.QuadPart) / 10000, INT_MAX );
678 static ULONG get_rtt( LARGE_INTEGER start )
680 LARGE_INTEGER now;
682 NtQueryPerformanceCounter( &now, NULL );
683 return (now.QuadPart - start.QuadPart) / 10000;
686 static NTSTATUS recv_msg( struct icmp_data *data, struct icmp_listen_params *params )
688 struct sockaddr_storage addr;
689 struct icmp_reply_ctx ctx;
690 struct iovec iov[1];
691 BYTE cmsg_buf[1024];
692 struct msghdr msg = { .msg_name = &addr, .msg_namelen = sizeof(addr),
693 .msg_iov = iov, .msg_iovlen = ARRAY_SIZE(iov),
694 .msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf) };
695 int ip_hdr_len, recvd, reply_buf_len;
696 char *reply_buf;
697 struct icmp_hdr *icmp_hdr;
699 reply_buf_len = data->ops->reply_buffer_len( params );
700 reply_buf = malloc( reply_buf_len );
701 if (!reply_buf) return STATUS_NO_MEMORY;
703 iov[0].iov_base = reply_buf;
704 iov[0].iov_len = reply_buf_len;
706 recvd = recvmsg( data->socket, &msg, 0 );
707 TRACE( "recvmsg() rets %d errno %d addr_len %d iovlen %d msg_flags %x\n",
708 recvd, errno, msg.msg_namelen, (int)iov[0].iov_len, msg.msg_flags );
710 if (recvd < 0) goto skip;
711 if (!data->ops->parse_ip_hdr( &msg, recvd, &ip_hdr_len, &ctx )) goto skip;
712 if (recvd < ip_hdr_len + sizeof(*icmp_hdr)) goto skip;
714 icmp_hdr = (struct icmp_hdr *)(reply_buf + ip_hdr_len);
715 if ((ctx.data_size = data->ops->parse_icmp_hdr( data, icmp_hdr, recvd - ip_hdr_len, &ctx )) < 0) goto skip;
716 if (ctx.data_size && msg.msg_flags & MSG_TRUNC)
718 free( reply_buf );
719 params->reply_len = data->ops->set_reply_ip_status( IP_GENERAL_FAILURE, params->bits, params->reply );
720 return STATUS_SUCCESS;
723 sockaddr_to_SOCKADDR_INET( (struct sockaddr *)&addr, &ctx.addr );
724 ctx.round_trip_time = get_rtt( data->send_time );
725 ctx.data = icmp_hdr + 1;
727 data->ops->fill_reply( params, &ctx );
729 free( reply_buf );
730 return STATUS_SUCCESS;
732 skip:
733 free( reply_buf );
734 return STATUS_RETRY;
737 NTSTATUS icmp_listen( void *args )
739 struct icmp_listen_params *params = args;
740 struct icmp_data *data;
741 struct pollfd fds[2];
742 NTSTATUS status;
743 int ret;
745 data = handle_data( params->handle );
746 if (!data) return STATUS_INVALID_PARAMETER;
748 fds[0].fd = data->socket;
749 fds[0].events = POLLIN;
750 fds[1].fd = data->cancel_pipe[0];
751 fds[1].events = POLLIN;
753 while ((ret = poll( fds, ARRAY_SIZE(fds), get_timeout( data->send_time, params->timeout ) )) > 0)
755 if (fds[1].revents & POLLIN)
757 TRACE( "cancelled\n" );
758 return STATUS_CANCELLED;
761 status = recv_msg( data, params );
762 if (status == STATUS_RETRY) continue;
763 return status;
766 if (!ret) /* timeout */
768 TRACE( "timeout\n" );
769 params->reply_len = data->ops->set_reply_ip_status( IP_REQ_TIMED_OUT, params->bits, params->reply );
770 return STATUS_SUCCESS;
772 /* ret < 0 */
773 params->reply_len = data->ops->set_reply_ip_status( errno_to_ip_status( errno ), params->bits, params->reply );
774 return STATUS_SUCCESS;
777 NTSTATUS icmp_cancel_listen( void *args )
779 struct icmp_cancel_listen_params *params = args;
780 struct icmp_data *data = handle_data( params->handle );
782 if (!data) return STATUS_INVALID_PARAMETER;
783 write( data->cancel_pipe[1], "x", 1 );
784 return STATUS_SUCCESS;
787 NTSTATUS icmp_close( void *args )
789 struct icmp_close_params *params = args;
790 struct icmp_data *data = handle_data( params->handle );
792 if (!data) return STATUS_INVALID_PARAMETER;
793 icmp_data_free( data );
794 handle_free( params->handle );
795 return STATUS_SUCCESS;