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
33 #include <sys/socket.h>
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
39 #ifdef HAVE_NETINET_IP_H
40 #include <netinet/ip.h>
44 #define WIN32_NO_STATUS
56 #include "wine/debug.h"
58 #include "nsiproxy_private.h"
60 WINE_DEFAULT_DEBUG_CHANNEL(nsi
);
62 static LONG icmp_sequence
;
66 uint8_t v_hl
; /* version << 4 | hdr_len */
97 ULONG round_trip_time
;
110 LARGE_INTEGER send_time
;
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
;
128 pthread_mutex_lock( &handle_lock
);
130 if (entry
) next_free
= *(struct icmp_data
***)entry
;
131 else if (next_unused
< handle_table
+ MAX_HANDLES
) entry
= next_unused
++;
134 pthread_mutex_unlock( &handle_lock
);
135 FIXME( "Exhausted icmp handle count\n" );
139 h
= entry
- handle_table
+ 1;
140 pthread_mutex_unlock( &handle_lock
);
141 TRACE( "returning handle %x\n", h
);
145 static struct icmp_data
**handle_entry( icmp_handle h
)
147 if (!h
|| h
> MAX_HANDLES
)
149 ERR( "Invalid icmp handle\n" );
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
;
163 static void handle_free( icmp_handle h
)
165 struct icmp_data
**entry
;
168 pthread_mutex_lock( &handle_lock
);
169 entry
= handle_entry( h
);
172 *(struct icmp_data
***)entry
= next_free
;
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
;
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
;
195 s
= *(unsigned short *)data
;
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);
216 static unsigned short null_chksum( BYTE
*data
, unsigned int count
)
222 static int ipv4_set_reply_ip_status( IP_STATUS ip_status
, unsigned int bits
, void *out
)
226 struct icmp_echo_reply_32
*reply
= out
;
227 memset( reply
, 0, sizeof(*reply
) );
228 reply
->status
= ip_status
;
229 return sizeof(*reply
);
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
)
245 if (val
) setsockopt( data
->socket
, IPPROTO_IP
, IP_TTL
, &val
, sizeof(val
) );
247 if (val
) setsockopt( data
->socket
, IPPROTO_IP
, IP_TOS
, &val
, sizeof(val
) );
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
) );
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
;
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
;
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
);
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
;
302 ctx
->options_data
= NULL
;
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
)
314 ctx
->ttl
= *(BYTE
*)CMSG_DATA( cmsg
);
317 ctx
->tos
= *(BYTE
*)CMSG_DATA( cmsg
);
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
;
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
];
365 status
= IP_DEST_HOST_UNREACHABLE
;
368 case ICMP4_TIME_EXCEEDED
:
369 if (icmp
->code
== 1) /* ICMP_TIMXCEED_REASS */
370 status
= IP_TTL_EXPIRED_REASSEM
;
372 status
= IP_TTL_EXPIRED_TRANSIT
;
375 case ICMP4_PARAM_PROB
:
376 status
= IP_PARAM_PROBLEM
;
379 case ICMP4_SOURCE_QUENCH
:
380 status
= IP_SOURCE_QUENCH
;
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
;
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 );
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 );
419 static void ipv4_fill_reply( struct icmp_listen_params
*params
, struct icmp_reply_ctx
*ctx
)
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;
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
;
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
=
486 ipv4_set_reply_ip_status
,
487 ipv4_set_socket_opts
,
488 ipv4_reply_buffer_len
,
495 /* linux ipv4 ping sockets behave more like ipv6 raw sockets */
496 static const struct family_ops ipv4_linux_ping
=
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
,
511 static IP_STATUS
errno_to_ip_status( int 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
)
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
;
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
;
550 static BOOL
sockaddr_to_SOCKADDR_INET( const struct sockaddr
*in
, SOCKADDR_INET
*out
)
552 switch (in
->sa_family
)
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
;
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
;
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" );
598 return STATUS_ACCESS_DENIED
;
601 if (ops
->family
== AF_INET
) ops
= &ipv4_linux_ping
;
604 if (pipe( data
->cancel_pipe
))
606 close( data
->socket
);
608 return STATUS_ACCESS_DENIED
;
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] );
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
;
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
);
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
);
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
)
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
;
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
;
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
)
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
);
730 return STATUS_SUCCESS
;
737 NTSTATUS
icmp_listen( void *args
)
739 struct icmp_listen_params
*params
= args
;
740 struct icmp_data
*data
;
741 struct pollfd fds
[2];
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;
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
;
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
;