From: Paul Gofman pgofman@codeweavers.com
Signed-off-by: Paul Gofman pgofman@codeweavers.com --- dlls/ntdll/unix/socket.c | 112 +++++++++++++++++++++++++++++++++++---- server/protocol.def | 8 ++- server/sock.c | 32 +++++++++++ 3 files changed, 140 insertions(+), 12 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 351028d4983..d200c0c999c 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -115,6 +115,7 @@ struct async_recv_ioctl DWORD *ret_flags; int unix_flags; unsigned int count; + enum sock_prot_fixup_type fixup_type; struct iovec iov[1]; };
@@ -566,6 +567,20 @@ 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 NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *size ) { #ifndef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS @@ -576,8 +591,14 @@ static NTSTATUS try_recv( int fd, struct async_recv_ioctl *async, ULONG_PTR *siz NTSTATUS status; ssize_t ret;
+ if (async->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM && async->count != 1) + { + FIXME( "async->count %d not supported for ICMP fixup.\n", async->count ); + async->fixup_type = SOCK_PROT_FIXUP_NONE; + } + memset( &hdr, 0, sizeof(hdr) ); - if (async->addr) + if (async->addr || async->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM) { hdr.msg_name = &unix_addr.addr; hdr.msg_namelen = sizeof(unix_addr); @@ -602,9 +623,68 @@ 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->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM) + { + unsigned int tot_len = sizeof(struct ip_hdr) + ret; + size_t len = hdr.msg_iov[0].iov_len; + char *buf = hdr.msg_iov[0].iov_base; + struct cmsghdr *cmsg; + struct ip_hdr ip_h; + + if (ret + sizeof(ip_h) > len) + status = STATUS_BUFFER_OVERFLOW; + + if (len < sizeof(ip_h)) + { + ret = len; + } + else + { + ret = min( ret, len - sizeof(ip_h) ); + memmove( buf + sizeof(ip_h), buf, ret ); + ret += 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), len )); + }
if (async->control) { + if (async->fixup_type == SOCK_PROT_FIXUP_ICMP_OVER_DGRAM) + FIXME( "May return extra control headers.\n" ); + if (in_wow64_call()) { char control_buffer64[512]; @@ -727,6 +807,7 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi async->addr = addr; async->addr_len = addr_len; async->ret_flags = ret_flags; + async->fixup_type = SOCK_PROT_FIXUP_NONE;
for (i = 0; i < count; ++i) { @@ -737,17 +818,26 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi } }
- SERVER_START_REQ( recv_socket ) + do { - req->force_async = force_async; - req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); - req->oob = !!(unix_flags & MSG_OOB); - status = wine_server_call( req ); - wait_handle = wine_server_ptr_handle( reply->wait ); - options = reply->options; - nonblocking = reply->nonblocking; - } - SERVER_END_REQ; + SERVER_START_REQ( recv_socket ) + { + req->force_async = force_async; + req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); + req->oob = !!(unix_flags & MSG_OOB); + req->fixup_type = async->fixup_type; + status = wine_server_call( req ); + if (status == STATUS_MORE_PROCESSING_REQUIRED) + { + async->fixup_type = reply->fixup_type; + continue; + } + wait_handle = wine_server_ptr_handle( reply->wait ); + options = reply->options; + nonblocking = reply->nonblocking; + } + SERVER_END_REQ; + } while (status == STATUS_MORE_PROCESSING_REQUIRED);
alerted = status == STATUS_ALERTED; if (alerted) diff --git a/server/protocol.def b/server/protocol.def index a8beccaf468..e1e5f3d9faa 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1446,17 +1446,23 @@ enum server_fd_type file_pos_t count; /* count of bytes to unlock */ @END
- /* Perform a recv on a socket */ @REQ(recv_socket) int oob; /* are we receiving OOB data? */ async_data_t async; /* async I/O parameters */ int force_async; /* Force asynchronous mode? */ + int fixup_type; /* protocol fixup type returned previously */ @REPLY obj_handle_t wait; /* handle to wait on for blocking recv */ unsigned int options; /* device open options */ int nonblocking; /* is socket non-blocking? */ + int fixup_type; /* protocol fixup type (see below) */ @END +enum sock_prot_fixup_type +{ + SOCK_PROT_FIXUP_NONE, + SOCK_PROT_FIXUP_ICMP_OVER_DGRAM, +};
/* Perform a send on a socket */ diff --git a/server/sock.c b/server/sock.c index 2f1b33a333d..5c09c3bff3b 100644 --- a/server/sock.c +++ b/server/sock.c @@ -216,6 +216,7 @@ struct sock unsigned int sndbuf; /* advisory send buffer size */ unsigned int rcvtimeo; /* receive timeout in ms */ unsigned int sndtimeo; /* send timeout in ms */ + enum sock_prot_fixup_type fixup_type; /* protocol fixup performed */ unsigned int rd_shutdown : 1; /* is the read end shut down? */ unsigned int wr_shutdown : 1; /* is the write end shut down? */ unsigned int wr_shutdown_pending : 1; /* is a write shutdown pending? */ @@ -1551,6 +1552,7 @@ static struct sock *create_socket(void) sock->sndbuf = 0; sock->rcvtimeo = 0; sock->sndtimeo = 0; + sock->fixup_type = SOCK_PROT_FIXUP_NONE; init_async_queue( &sock->read_q ); init_async_queue( &sock->write_q ); init_async_queue( &sock->ifchange_q ); @@ -1667,6 +1669,24 @@ static int init_socket( struct sock *sock, int family, int type, int protocol, u }
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; + + sock->fixup_type = SOCK_PROT_FIXUP_ICMP_OVER_DGRAM; + 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 ); @@ -3459,6 +3479,18 @@ DECL_HANDLER(recv_socket) struct fd *fd;
if (!sock) return; + + if (sock->fixup_type != req->fixup_type) + { + /* Protocol fixup information should be reflected in client's async data before the + * async is delivered to process. So let the client set up the info and restart the + * call. */ + reply->fixup_type = sock->fixup_type; + set_error( STATUS_MORE_PROCESSING_REQUIRED ); + release_object( sock ); + return; + } + fd = sock->fd;
if (!req->force_async && !sock->nonblocking && is_fd_overlapped( fd ))