mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-05 18:01:34 +00:00
nsiproxy: Parse any received ICMP_ECHO_REPLY.
Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
aa73a91125
commit
fffd222b3d
1 changed files with 203 additions and 2 deletions
|
@ -233,6 +233,100 @@ static void ipv4_linux_ping_set_socket_opts( struct icmp_data *data, struct icmp
|
|||
}
|
||||
#endif
|
||||
|
||||
static int ipv4_reply_buffer_len( int reply_len )
|
||||
{
|
||||
return sizeof(struct ip_hdr) + sizeof(struct icmp_hdr) + reply_len - sizeof(struct nsiproxy_icmp_echo_reply);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static int ipv4_linux_ping_reply_buffer_len( int reply_len )
|
||||
{
|
||||
return sizeof(struct icmp_hdr) + reply_len - sizeof(struct nsiproxy_icmp_echo_reply);
|
||||
}
|
||||
#endif
|
||||
|
||||
static BOOL ipv4_parse_ip_hdr( struct msghdr *msg, int recvd,
|
||||
int *ip_hdr_len, struct nsiproxy_icmp_echo_reply *reply, void **opts )
|
||||
{
|
||||
struct ip_hdr *ip_hdr;
|
||||
|
||||
if (recvd < sizeof(*ip_hdr)) return FALSE;
|
||||
ip_hdr = msg->msg_iov[0].iov_base;
|
||||
if (ip_hdr->v_hl >> 4 != 4 || ip_hdr->protocol != IPPROTO_ICMP) return FALSE;
|
||||
*ip_hdr_len = (ip_hdr->v_hl & 0xf) << 2;
|
||||
if (*ip_hdr_len < sizeof(*ip_hdr)) return FALSE;
|
||||
*opts = ip_hdr + 1;
|
||||
reply->opts.ttl = ip_hdr->ttl;
|
||||
reply->opts.tos = ip_hdr->tos;
|
||||
reply->opts.flags = ip_hdr->frag_off >> 13;
|
||||
reply->opts.options_size = *ip_hdr_len - sizeof(*ip_hdr);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static BOOL ipv4_linux_ping_parse_ip_hdr( struct msghdr *msg, int recvd,
|
||||
int *ip_hdr_len, struct nsiproxy_icmp_echo_reply *reply, void **opts )
|
||||
{
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
*ip_hdr_len = 0;
|
||||
*opts = NULL;
|
||||
reply->opts.ttl = 0;
|
||||
reply->opts.tos = 0;
|
||||
reply->opts.flags = 0;
|
||||
reply->opts.options_size = 0; /* FIXME from IP_OPTIONS but will require checking for space in the reply */
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR( msg ); cmsg; cmsg = CMSG_NXTHDR( msg, cmsg ))
|
||||
{
|
||||
if (cmsg->cmsg_level != IPPROTO_IP) continue;
|
||||
switch (cmsg->cmsg_type)
|
||||
{
|
||||
case IP_TTL:
|
||||
reply->opts.ttl = *(BYTE *)CMSG_DATA( cmsg );
|
||||
break;
|
||||
case IP_TOS:
|
||||
reply->opts.tos = *(BYTE *)CMSG_DATA( cmsg );
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ipv4_parse_icmp_hdr_( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size,
|
||||
struct nsiproxy_icmp_echo_reply *reply, int ping_socket )
|
||||
{
|
||||
switch (icmp->type)
|
||||
{
|
||||
case ICMP4_ECHO_REPLY:
|
||||
if ((!ping_socket && icmp->un.echo.id != data->id) ||
|
||||
icmp->un.echo.sequence != data->seq) return -1;
|
||||
|
||||
reply->status = IP_SUCCESS;
|
||||
return icmp_size - sizeof(*icmp);
|
||||
|
||||
/* FIXME: handle other icmp messages */
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int ipv4_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size,
|
||||
struct nsiproxy_icmp_echo_reply *reply )
|
||||
{
|
||||
return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, reply, 0 );
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static int ipv4_linux_ping_parse_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_size,
|
||||
struct nsiproxy_icmp_echo_reply *reply )
|
||||
{
|
||||
return ipv4_parse_icmp_hdr_( data, icmp, icmp_size, reply, 1 );
|
||||
}
|
||||
#endif
|
||||
|
||||
struct family_ops
|
||||
{
|
||||
int family;
|
||||
|
@ -240,6 +334,11 @@ struct family_ops
|
|||
void (*init_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp_hdr );
|
||||
unsigned short (*chksum)( BYTE *data, unsigned int count );
|
||||
void (*set_socket_opts)( struct icmp_data *data, struct icmp_send_echo_params *params );
|
||||
int (*reply_buffer_len)( int reply_len );
|
||||
BOOL (*parse_ip_hdr)( struct msghdr *msg, int recvd,
|
||||
int *ip_hdr_len, struct nsiproxy_icmp_echo_reply *reply, void **opts );
|
||||
int (*parse_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp, int icmp_len,
|
||||
struct nsiproxy_icmp_echo_reply *reply );
|
||||
};
|
||||
|
||||
static const struct family_ops ipv4 =
|
||||
|
@ -249,6 +348,9 @@ static const struct family_ops ipv4 =
|
|||
ipv4_init_icmp_hdr,
|
||||
chksum,
|
||||
ipv4_set_socket_opts,
|
||||
ipv4_reply_buffer_len,
|
||||
ipv4_parse_ip_hdr,
|
||||
ipv4_parse_icmp_hdr,
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
|
@ -260,6 +362,9 @@ static const struct family_ops ipv4_linux_ping =
|
|||
ipv4_init_icmp_hdr,
|
||||
null_chksum,
|
||||
ipv4_linux_ping_set_socket_opts,
|
||||
ipv4_linux_ping_reply_buffer_len,
|
||||
ipv4_linux_ping_parse_ip_hdr,
|
||||
ipv4_linux_ping_parse_icmp_hdr,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -302,6 +407,34 @@ static int SOCKADDR_INET_to_sockaddr( const SOCKADDR_INET *in, struct sockaddr *
|
|||
return 0;
|
||||
}
|
||||
|
||||
static BOOL sockaddr_to_SOCKADDR_INET( const struct sockaddr *in, SOCKADDR_INET *out )
|
||||
{
|
||||
switch (in->sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
{
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *)in;
|
||||
|
||||
out->Ipv4.sin_family = WS_AF_INET;
|
||||
out->Ipv4.sin_port = sa->sin_port;
|
||||
out->Ipv4.sin_addr.WS_s_addr = sa->sin_addr.s_addr;
|
||||
return TRUE;
|
||||
}
|
||||
case AF_INET6:
|
||||
{
|
||||
struct sockaddr_in6 *sa = (struct sockaddr_in6 *)in;
|
||||
|
||||
out->Ipv6.sin6_family = WS_AF_INET6;
|
||||
out->Ipv6.sin6_port = sa->sin6_port;
|
||||
out->Ipv6.sin6_flowinfo = sa->sin6_flowinfo;
|
||||
memcpy( out->Ipv6.sin6_addr.WS_s6_addr, sa->sin6_addr.s6_addr, sizeof(sa->sin6_addr.s6_addr) );
|
||||
out->Ipv6.sin6_scope_id = sa->sin6_scope_id;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data **icmp_data )
|
||||
{
|
||||
struct icmp_data *data;
|
||||
|
@ -404,11 +537,78 @@ static int get_timeout( LARGE_INTEGER start, DWORD timeout )
|
|||
return min( (end.QuadPart - now.QuadPart) / 10000, INT_MAX );
|
||||
}
|
||||
|
||||
static ULONG get_rtt( LARGE_INTEGER start )
|
||||
{
|
||||
LARGE_INTEGER now;
|
||||
|
||||
NtQueryPerformanceCounter( &now, NULL );
|
||||
return (now.QuadPart - start.QuadPart) / 10000;
|
||||
}
|
||||
|
||||
static NTSTATUS recv_msg( struct icmp_data *data, struct icmp_listen_params *params )
|
||||
{
|
||||
struct nsiproxy_icmp_echo_reply *reply = (struct nsiproxy_icmp_echo_reply *)params->reply;
|
||||
struct sockaddr_storage addr;
|
||||
struct iovec iov[1];
|
||||
BYTE cmsg_buf[1024];
|
||||
struct msghdr msg = { .msg_name = &addr, .msg_namelen = sizeof(addr),
|
||||
.msg_iov = iov, .msg_iovlen = ARRAY_SIZE(iov),
|
||||
.msg_control = cmsg_buf, .msg_controllen = sizeof(cmsg_buf) };
|
||||
int ip_hdr_len, recvd, reply_buf_len, data_size;
|
||||
char *reply_buf;
|
||||
void *opts;
|
||||
struct icmp_hdr *icmp_hdr;
|
||||
|
||||
reply_buf_len = data->ops->reply_buffer_len( params->reply_len );
|
||||
reply_buf = malloc( reply_buf_len );
|
||||
if (!reply_buf) return STATUS_NO_MEMORY;
|
||||
|
||||
iov[0].iov_base = reply_buf;
|
||||
iov[0].iov_len = reply_buf_len;
|
||||
|
||||
recvd = recvmsg( data->socket, &msg, 0 );
|
||||
TRACE( "recvmsg() rets %d errno %d addr_len %d iovlen %d msg_flags %x\n",
|
||||
recvd, errno, msg.msg_namelen, (int)iov[0].iov_len, msg.msg_flags );
|
||||
|
||||
if (!data->ops->parse_ip_hdr( &msg, recvd, &ip_hdr_len, reply, &opts )) goto skip;
|
||||
if (recvd < ip_hdr_len + sizeof(*icmp_hdr)) goto skip;
|
||||
|
||||
icmp_hdr = (struct icmp_hdr *)(reply_buf + ip_hdr_len);
|
||||
if ((data_size = data->ops->parse_icmp_hdr( data, icmp_hdr, recvd - ip_hdr_len, reply )) < 0) goto skip;
|
||||
reply->data_size = data_size;
|
||||
if (reply->data_size && msg.msg_flags & MSG_TRUNC)
|
||||
{
|
||||
free( reply_buf );
|
||||
return set_reply_ip_status( params, IP_GENERAL_FAILURE );
|
||||
}
|
||||
|
||||
sockaddr_to_SOCKADDR_INET( (struct sockaddr *)&addr, &reply->addr );
|
||||
reply->round_trip_time = get_rtt( data->send_time );
|
||||
reply->num_of_pkts = 1;
|
||||
reply->opts.options_offset = sizeof(*reply);
|
||||
reply->data_offset = sizeof(*reply) + ((reply->opts.options_size + 3) & ~3);
|
||||
if (reply->opts.options_size)
|
||||
memcpy( (char *)reply + reply->opts.options_offset, opts, reply->opts.options_size );
|
||||
if (reply->opts.options_size & 3)
|
||||
memset( (char *)reply + reply->opts.options_offset + reply->opts.options_size, 0, 4 - (reply->opts.options_size & 3) );
|
||||
if (reply->data_size)
|
||||
memcpy( (char *)reply + reply->data_offset, icmp_hdr + 1, reply->data_size );
|
||||
|
||||
params->reply_len = reply->data_offset + reply->data_size;
|
||||
free( reply_buf );
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
skip:
|
||||
free( reply_buf );
|
||||
return STATUS_RETRY;
|
||||
}
|
||||
|
||||
NTSTATUS icmp_listen( void *args )
|
||||
{
|
||||
struct icmp_listen_params *params = args;
|
||||
struct icmp_data *data;
|
||||
struct pollfd fds[1];
|
||||
NTSTATUS status;
|
||||
int ret;
|
||||
|
||||
data = handle_data( params->handle );
|
||||
|
@ -419,8 +619,9 @@ NTSTATUS icmp_listen( void *args )
|
|||
|
||||
while ((ret = poll( fds, ARRAY_SIZE(fds), get_timeout( data->send_time, params->timeout ) )) > 0)
|
||||
{
|
||||
/* FIXME */
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
status = recv_msg( data, params );
|
||||
if (status == STATUS_RETRY) continue;
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!ret) /* timeout */
|
||||
|
|
Loading…
Reference in a new issue