Commit 1e35966e authored by Paul Gofman's avatar Paul Gofman Committed by Alexandre Julliard

ntdll: Support SOCK_RAW / IPPROTO_ICMP fallback over SOCK_DGRAM.

parent 86f08493
......@@ -115,6 +115,7 @@ struct async_recv_ioctl
DWORD *ret_flags;
int unix_flags;
unsigned int count;
BOOL icmp_over_dgram;
struct iovec iov[1];
};
......@@ -566,6 +567,89 @@ static int wow64_translate_control( const WSABUF *control64, struct afd_wsabuf_3
return 1;
}
struct ip_hdr
{
BYTE v_hl; /* version << 4 | hdr_len */
BYTE tos;
UINT16 tot_len;
UINT16 id;
UINT16 frag_off;
BYTE ttl;
BYTE protocol;
UINT16 checksum;
ULONG saddr;
ULONG daddr;
};
static ssize_t fixup_icmp_over_dgram( struct msghdr *hdr, union unix_sockaddr *unix_addr,
ssize_t recv_len, NTSTATUS *status )
{
unsigned int tot_len = sizeof(struct ip_hdr) + recv_len;
struct cmsghdr *cmsg;
struct ip_hdr ip_h;
size_t buf_len;
char *buf;
if (hdr->msg_iovlen != 1)
{
FIXME( "Buffer count %zu is not supported for ICMP fixup.\n", (size_t)hdr->msg_iovlen );
return recv_len;
}
buf = hdr->msg_iov[0].iov_base;
buf_len = hdr->msg_iov[0].iov_len;
if (recv_len + sizeof(ip_h) > buf_len)
*status = STATUS_BUFFER_OVERFLOW;
if (buf_len < sizeof(ip_h))
{
recv_len = buf_len;
}
else
{
recv_len = min( recv_len, buf_len - sizeof(ip_h) );
memmove( buf + sizeof(ip_h), buf, recv_len );
recv_len += sizeof(ip_h);
}
memset( &ip_h, 0, sizeof(ip_h) );
ip_h.v_hl = (4 << 4) | (sizeof(ip_h) >> 2);
ip_h.tot_len = htons( tot_len );
ip_h.protocol = 1;
ip_h.saddr = unix_addr->in.sin_addr.s_addr;
for (cmsg = CMSG_FIRSTHDR( hdr ); cmsg; cmsg = CMSG_NXTHDR( hdr, cmsg ))
{
if (cmsg->cmsg_level != IPPROTO_IP) continue;
switch (cmsg->cmsg_type)
{
#if defined(IP_TTL)
case IP_TTL:
ip_h.ttl = *(BYTE *)CMSG_DATA( cmsg );
break;
#endif
#if defined(IP_TOS)
case IP_TOS:
ip_h.tos = *(BYTE *)CMSG_DATA( cmsg );
break;
#endif
#if defined(IP_PKTINFO)
case IP_PKTINFO:
{
struct in_pktinfo *info;
info = (struct in_pktinfo *)CMSG_DATA( cmsg );
ip_h.daddr = info->ipi_addr.s_addr;
break;
}
#endif
}
}
memcpy( buf, &ip_h, min( sizeof(ip_h), buf_len ));
return recv_len;
}
static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *size )
{
#ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
......@@ -577,7 +661,7 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
ssize_t ret;
memset( &hdr, 0, sizeof(hdr) );
if (async->addr)
if (async->addr || async->icmp_over_dgram)
{
hdr.msg_name = &unix_addr.addr;
hdr.msg_namelen = sizeof(unix_addr);
......@@ -602,9 +686,14 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz
}
status = (hdr.msg_flags & MSG_TRUNC) ? STATUS_BUFFER_OVERFLOW : STATUS_SUCCESS;
if (async->icmp_over_dgram)
ret = fixup_icmp_over_dgram( &hdr, &unix_addr, ret, &status );
if (async->control)
{
if (async->icmp_over_dgram)
FIXME( "May return extra control headers.\n" );
if (in_wow64_call())
{
char control_buffer64[512];
......@@ -675,6 +764,23 @@ static BOOL async_recv_proc( void *user, ULONG_PTR *info, NTSTATUS *status )
return TRUE;
}
static BOOL is_icmp_over_dgram( int fd )
{
#ifdef linux
socklen_t len;
int val;
len = sizeof(val);
if (getsockopt( fd, SOL_SOCKET, SO_PROTOCOL, (char *)&val, &len ) || val != IPPROTO_ICMP)
return FALSE;
len = sizeof(val);
return !getsockopt( fd, SOL_SOCKET, SO_TYPE, (char *)&val, &len ) && val == SOCK_DGRAM;
#else
return FALSE;
#endif
}
static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io,
int fd, struct async_recv_ioctl *async, int force_async )
{
......@@ -777,6 +883,7 @@ static NTSTATUS sock_ioctl_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap
async->addr = addr;
async->addr_len = addr_len;
async->ret_flags = ret_flags;
async->icmp_over_dgram = is_icmp_over_dgram( fd );
return sock_recv( handle, event, apc, apc_user, io, fd, async, force_async );
}
......
......@@ -1618,6 +1618,23 @@ static int init_socket( struct sock *sock, int family, int type, int protocol )
}
sockfd = socket( unix_family, unix_type, unix_protocol );
#ifdef linux
if (sockfd == -1 && errno == EPERM && unix_family == AF_INET
&& unix_type == SOCK_RAW && unix_protocol == IPPROTO_ICMP)
{
sockfd = socket( unix_family, SOCK_DGRAM, unix_protocol );
if (sockfd != -1)
{
const int val = 1;
setsockopt( sockfd, IPPROTO_IP, IP_RECVTTL, (const char *)&val, sizeof(val) );
setsockopt( sockfd, IPPROTO_IP, IP_RECVTOS, (const char *)&val, sizeof(val) );
setsockopt( sockfd, IPPROTO_IP, IP_PKTINFO, (const char *)&val, sizeof(val) );
}
}
#endif
if (sockfd == -1)
{
if (errno == EINVAL) set_win32_error( WSAESOCKTNOSUPPORT );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment