Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/tests/sock.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 76d0a3f3b19..2d2b4c40141 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -3592,6 +3592,7 @@ static void test_getsockname(void) ok(sock != INVALID_SOCKET, "failed to create socket, error %u\n", WSAGetLastError());
sa_get = sa_set; + WSASetLastError(0xdeadbeef); ret = getsockname(sock, (struct sockaddr *)&sa_get, &sa_get_len); ok(ret == SOCKET_ERROR, "expected failure\n"); ok(WSAGetLastError() == WSAEINVAL, "got error %u\n", WSAGetLastError()); @@ -3600,12 +3601,21 @@ static void test_getsockname(void) ret = bind(sock, (struct sockaddr *) &sa_set, sa_set_len); ok(!ret, "failed to bind, error %u\n", WSAGetLastError());
+ WSASetLastError(0xdeadbeef); ret = getsockname(sock, (struct sockaddr *) &sa_get, &sa_get_len); - ok(!ret, "failed to get address, error %u\n", WSAGetLastError()); + ok(!ret, "got %d\n", ret); + todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError());
ret = memcmp(sa_get.sin_zero, null_padding, 8); ok(ret == 0, "getsockname did not zero the sockaddr_in structure\n");
+ sa_get_len = sizeof(sa_get) - 1; + WSASetLastError(0xdeadbeef); + ret = getsockname(sock, (struct sockaddr *)&sa_get, &sa_get_len); + ok(ret == -1, "expected failure\n"); + ok(WSAGetLastError() == WSAEFAULT, "got error %u\n", WSAGetLastError()); + ok(sa_get_len == sizeof(sa_get) - 1, "got size %d\n", sa_get_len); + closesocket(sock);
h = gethostbyname(""); @@ -5546,11 +5556,11 @@ todo_wine static void test_WSASendTo(void) { SOCKET s; - struct sockaddr_in addr; + struct sockaddr_in addr, ret_addr; char buf[12] = "hello world"; WSABUF data_buf; DWORD bytesSent; - int ret; + int ret, len;
addr.sin_family = AF_INET; addr.sin_port = htons(139); @@ -5566,6 +5576,11 @@ static void test_WSASendTo(void) ok(ret == SOCKET_ERROR && WSAGetLastError() == WSAENOTSOCK, "WSASendTo() failed: %d/%d\n", ret, WSAGetLastError());
+ len = sizeof(ret_addr); + ret = getsockname(s, (struct sockaddr *)&ret_addr, &len); + ok(ret == -1, "expected failure\n"); + ok(WSAGetLastError() == WSAEINVAL, "got error %u\n", WSAGetLastError()); + WSASetLastError(12345); ret = WSASendTo(s, &data_buf, 1, NULL, 0, (struct sockaddr*)&addr, sizeof(addr), NULL, NULL); ok(ret == SOCKET_ERROR && WSAGetLastError() == WSAEFAULT, @@ -5575,6 +5590,12 @@ static void test_WSASendTo(void) ret = WSASendTo(s, &data_buf, 1, &bytesSent, 0, (struct sockaddr *)&addr, sizeof(addr), NULL, NULL); ok(!ret, "expected success\n"); ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); + + len = sizeof(ret_addr); + ret = getsockname(s, (struct sockaddr *)&ret_addr, &len); + ok(!ret, "got error %u\n", WSAGetLastError()); + ok(ret_addr.sin_family == AF_INET, "got family %u\n", ret_addr.sin_family); + ok(ret_addr.sin_port, "expected nonzero port\n"); }
static DWORD WINAPI recv_thread(LPVOID arg)
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/unix/socket.c | 10 ++ include/wine/afd.h | 7 + server/sock.c | 302 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 318 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 3b8206474f0..0d77e417148 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -1157,6 +1157,16 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
switch (code) { + case IOCTL_AFD_BIND: + { + const struct afd_bind_params *params = in_buffer; + + if (params->unknown) FIXME( "bind: got unknown %#x\n", params->unknown ); + + status = STATUS_BAD_DEVICE_TYPE; + break; + } + case IOCTL_AFD_LISTEN: { const struct afd_listen_params *params = in_buffer; diff --git a/include/wine/afd.h b/include/wine/afd.h index 35d047c59d8..87da44ecca7 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -32,6 +32,7 @@ # define WS(x) x #endif
+#define IOCTL_AFD_BIND CTL_CODE(FILE_DEVICE_BEEP, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_LISTEN CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_RECV CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_POLL CTL_CODE(FILE_DEVICE_BEEP, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) @@ -70,6 +71,12 @@ enum afd_poll_bit #define AFD_POLL_UNK1 0x0200 #define AFD_POLL_UNK2 0x0400
+struct afd_bind_params +{ + int unknown; + struct WS(sockaddr) addr; /* variable size */ +}; + struct afd_listen_params { int unknown1; diff --git a/server/sock.c b/server/sock.c index a3062cff8ab..425c07566ac 100644 --- a/server/sock.c +++ b/server/sock.c @@ -30,6 +30,12 @@ #include <string.h> #include <stdlib.h> #include <errno.h> +#ifdef HAVE_IFADDRS_H +# include <ifaddrs.h> +#endif +#ifdef HAVE_NET_IF_H +# include <net/if.h> +#endif #ifdef HAVE_NETINET_IN_H # include <netinet/in.h> #endif @@ -50,6 +56,9 @@ #include <time.h> #include <unistd.h> #include <limits.h> +#ifdef HAVE_LINUX_FILTER_H +# include <linux/filter.h> +#endif #ifdef HAVE_LINUX_RTNETLINK_H # include <linux/rtnetlink.h> #endif @@ -96,6 +105,10 @@ #include "request.h" #include "user.h"
+#if defined(linux) && !defined(IP_UNICAST_IF) +#define IP_UNICAST_IF 50 +#endif + static struct list poll_list = LIST_INIT( poll_list );
struct poll_req @@ -338,6 +351,108 @@ static int sockaddr_from_unix( const union unix_sockaddr *uaddr, struct WS_socka } }
+static socklen_t sockaddr_to_unix( const struct WS_sockaddr *wsaddr, int wsaddrlen, union unix_sockaddr *uaddr ) +{ + memset( uaddr, 0, sizeof(*uaddr) ); + + switch (wsaddr->sa_family) + { + case WS_AF_INET: + { + struct WS_sockaddr_in win = {0}; + + if (wsaddrlen < sizeof(win)) return 0; + memcpy( &win, wsaddr, sizeof(win) ); + uaddr->in.sin_family = AF_INET; + uaddr->in.sin_port = win.sin_port; + memcpy( &uaddr->in.sin_addr, &win.sin_addr, sizeof(win.sin_addr) ); + return sizeof(uaddr->in); + } + + case WS_AF_INET6: + { + struct WS_sockaddr_in6 win = {0}; + + if (wsaddrlen < sizeof(struct WS_sockaddr_in6_old)) return 0; + if (wsaddrlen < sizeof(struct WS_sockaddr_in6)) + memcpy( &win, wsaddr, sizeof(struct WS_sockaddr_in6_old) ); + else + memcpy( &win, wsaddr, sizeof(struct WS_sockaddr_in6) ); + + uaddr->in6.sin6_family = AF_INET6; + uaddr->in6.sin6_port = win.sin6_port; + uaddr->in6.sin6_flowinfo = win.sin6_flowinfo; + memcpy( &uaddr->in6.sin6_addr, &win.sin6_addr, sizeof(win.sin6_addr) ); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID + if (wsaddrlen >= sizeof(struct WS_sockaddr_in6)) + uaddr->in6.sin6_scope_id = win.sin6_scope_id; +#endif + return sizeof(uaddr->in6); + } + +#ifdef HAS_IPX + case WS_AF_IPX: + { + struct WS_sockaddr_ipx win = {0}; + + if (wsaddrlen < sizeof(win)) return 0; + memcpy( &win, wsaddr, sizeof(win) ); + uaddr->ipx.sipx_family = AF_IPX; + memcpy( &uaddr->ipx.sipx_network, win.sa_netnum, sizeof(win.sa_netnum) ); + memcpy( &uaddr->ipx.sipx_node, win.sa_nodenum, sizeof(win.sa_nodenum) ); + uaddr->ipx.sipx_port = win.sa_socket; + return sizeof(uaddr->ipx); + } +#endif + +#ifdef HAS_IRDA + case WS_AF_IRDA: + { + SOCKADDR_IRDA win = {0}; + unsigned int lsap_sel; + + if (wsaddrlen < sizeof(win)) return 0; + memcpy( &win, wsaddr, sizeof(win) ); + uaddr->irda.sir_family = AF_IRDA; + if (sscanf( win.irdaServiceName, "LSAP-SEL%u", &lsap_sel ) == 1) + uaddr->sir_lsap_sel = lsap_sel; + else + { + uaddr->sir_lsap_sel = LSAP_ANY; + memcpy( uaddr->irda.sir_name, win.irdaServiceName, sizeof(win.irdaServiceName) ); + } + memcpy( &uaddr->irda.sir_addr, win.irdaDeviceID, sizeof(win.irdaDeviceID) ); + return sizeof(uaddr->irda); + } +#endif + + case WS_AF_UNSPEC: + switch (wsaddrlen) + { + default: /* likely an ipv4 address */ + case sizeof(struct WS_sockaddr_in): + return sizeof(uaddr->in); + +#ifdef HAS_IPX + case sizeof(struct WS_sockaddr_ipx): + return sizeof(uaddr->ipx); +#endif + +#ifdef HAS_IRDA + case sizeof(SOCKADDR_IRDA): + return sizeof(uaddr->irda); +#endif + + case sizeof(struct WS_sockaddr_in6): + case sizeof(struct WS_sockaddr_in6_old): + return sizeof(uaddr->in6); + } + + default: + return 0; + } +} + /* some events are generated at the same time but must be sent in a particular * order (e.g. CONNECT must be sent before READ) */ static const enum afd_poll_bit event_bitorder[] = @@ -1551,6 +1666,137 @@ static int accept_into_socket( struct sock *sock, struct sock *acceptsock ) return TRUE; }
+#ifdef IP_BOUND_IF + +static int bind_to_index( int fd, in_addr_t bind_addr, unsigned int index ) +{ + return setsockopt( fd, IPPROTO_IP, IP_BOUND_IF, &index, sizeof(index) ); +} + +#elif defined(IP_UNICAST_IF) && defined(SO_ATTACH_FILTER) + +struct interface_filter +{ + struct sock_filter iface_memaddr; + struct sock_filter iface_rule; + struct sock_filter ip_memaddr; + struct sock_filter ip_rule; + struct sock_filter return_keep; + struct sock_filter return_dump; +}; +# define FILTER_JUMP_DUMP(here) (u_char)(offsetof(struct interface_filter, return_dump) \ + -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \ + /sizeof(struct sock_filter) +# define FILTER_JUMP_KEEP(here) (u_char)(offsetof(struct interface_filter, return_keep) \ + -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \ + /sizeof(struct sock_filter) +# define FILTER_JUMP_NEXT() (u_char)(0) +# define SKF_NET_DESTIP 16 /* offset in the network header to the destination IP */ +static struct interface_filter generic_interface_filter = +{ + /* This filter rule allows incoming packets on the specified interface, which works for all + * remotely generated packets and for locally generated broadcast packets. */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SKF_AD_OFF+SKF_AD_IFINDEX), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xdeadbeef, FILTER_JUMP_KEEP(iface_rule), FILTER_JUMP_NEXT()), + /* This rule allows locally generated packets targeted at the specific IP address of the chosen + * adapter (local packets not destined for the broadcast address do not have IFINDEX set) */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SKF_NET_OFF+SKF_NET_DESTIP), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xdeadbeef, FILTER_JUMP_KEEP(ip_rule), FILTER_JUMP_DUMP(ip_rule)), + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), /* keep packet */ + BPF_STMT(BPF_RET+BPF_K, 0) /* dump packet */ +}; + +static int bind_to_index( int fd, in_addr_t bind_addr, unsigned int index ) +{ + in_addr_t ifindex = htonl( index ); + struct interface_filter specific_interface_filter; + struct sock_fprog filter_prog; + int ret; + + if ((ret = setsockopt( fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex) )) < 0) + return ret; + + specific_interface_filter = generic_interface_filter; + specific_interface_filter.iface_rule.k = index; + specific_interface_filter.ip_rule.k = htonl( bind_addr ); + filter_prog.len = sizeof(generic_interface_filter) / sizeof(struct sock_filter); + filter_prog.filter = (struct sock_filter *)&specific_interface_filter; + return setsockopt( fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog) ); +} + +#else + +static int bind_to_index( int fd, in_addr_t bind_addr, unsigned int index ) +{ + errno = EOPNOTSUPP; + return -1; +} + +#endif /* LINUX_BOUND_IF */ + +/* Take bind() calls on any name corresponding to a local network adapter and + * restrict the given socket to operating only on the specified interface. This + * restriction consists of two components: + * 1) An outgoing packet restriction suggesting the egress interface for all + * packets. + * 2) An incoming packet restriction dropping packets not meant for the + * interface. + * If the function succeeds in placing these restrictions, then the name for the + * bind() may safely be changed to INADDR_ANY, permitting the transmission and + * receipt of broadcast packets on the socket. This behavior is only relevant to + * UDP sockets and is needed for applications that expect to be able to receive + * broadcast packets on a socket that is bound to a specific network interface. + */ +static int bind_to_interface( struct sock *sock, const struct sockaddr_in *addr ) +{ + in_addr_t bind_addr = addr->sin_addr.s_addr; + struct ifaddrs *ifaddrs, *ifaddr; + int fd = get_unix_fd( sock->fd ); + static const int enable = 1; + unsigned int index; + + if (bind_addr == htonl( INADDR_ANY ) || bind_addr == htonl( INADDR_LOOPBACK )) + return 0; + if (sock->type != WS_SOCK_DGRAM) + return 0; + + if (getifaddrs( &ifaddrs ) < 0) return 0; + + for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) + { + if (ifaddr->ifa_addr && ifaddr->ifa_addr->sa_family == AF_INET + && ((struct sockaddr_in *)ifaddr->ifa_addr)->sin_addr.s_addr == bind_addr) + { + index = if_nametoindex( ifaddr->ifa_name ); + if (!index) + { + if (debug_level) + fprintf( stderr, "Unable to look up interface index for %s: %s\n", + ifaddr->ifa_name, strerror( errno ) ); + continue; + } + + freeifaddrs( ifaddrs ); + + if (bind_to_index( fd, bind_addr, index ) < 0) + { + if (debug_level) + fprintf( stderr, "failed to bind to interface: %s\n", strerror( errno ) ); + return 0; + } + + if (setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable) ) < 0) + { + if (debug_level) + fprintf( stderr, "failed to reuse address: %s\n", strerror( errno ) ); + return 0; + } + return 1; + } + } + return 0; +} + /* return an errno value mapped to a WSA error */ static unsigned int sock_get_error( int err ) { @@ -1646,7 +1892,7 @@ static int sock_get_ntstatus( int err ) case ENOPROTOOPT: return STATUS_INVALID_PARAMETER; case EOPNOTSUPP: return STATUS_NOT_SUPPORTED; case EADDRINUSE: return STATUS_SHARING_VIOLATION; - case EADDRNOTAVAIL: return STATUS_INVALID_PARAMETER; + case EADDRNOTAVAIL: return STATUS_INVALID_ADDRESS_COMPONENT; case ECONNREFUSED: return STATUS_CONNECTION_REFUSED; case ESHUTDOWN: return STATUS_PIPE_DISCONNECTED; case ENOTCONN: return STATUS_INVALID_CONNECTION; @@ -2102,6 +2348,60 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return 1; }
+ case IOCTL_AFD_BIND: + { + const struct afd_bind_params *params = get_req_data(); + union unix_sockaddr unix_addr; + data_size_t in_size; + socklen_t unix_len; + + /* the ioctl is METHOD_NEITHER, so ntdll gives us the output buffer as + * input */ + if (get_req_data_size() < get_reply_max_size()) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return 0; + } + in_size = get_req_data_size() - get_reply_max_size(); + if (in_size < offsetof(struct afd_bind_params, addr.sa_data) + || get_reply_max_size() < sizeof(struct WS_sockaddr)) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + + unix_len = sockaddr_to_unix( ¶ms->addr, in_size - sizeof(int), &unix_addr ); + if (!unix_len) + { + set_error( STATUS_INVALID_ADDRESS ); + return 0; + } + + if (unix_addr.addr.sa_family == WS_AF_INET) + { + static const char magic_loopback_addr[] = {127, 12, 34, 56}; + + if (!memcmp( &unix_addr.in.sin_addr, magic_loopback_addr, 4 ) + || bind_to_interface( sock, &unix_addr.in )) + unix_addr.in.sin_addr.s_addr = htonl( INADDR_ANY ); + } + + if (bind( unix_fd, &unix_addr.addr, unix_len ) < 0) + { + if (errno == EADDRINUSE) + { + int reuse; + socklen_t len = sizeof(reuse); + + if (!getsockopt( unix_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, &len ) && reuse) + errno = EACCES; + } + + set_error( sock_get_ntstatus( errno ) ); + } + return 1; + } + default: set_error( STATUS_NOT_SUPPORTED ); return 0;
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92590
Your paranoid android.
=== debiant2 (32 bit Arabic:Egypt report) ===
ntdll: om.c:2307: Test failed: got 86
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/unix/socket.c | 6 +++ include/wine/afd.h | 1 + server/sock.c | 83 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 3 deletions(-)
diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 0d77e417148..02700d37d6a 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -1167,6 +1167,12 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc break; }
+ case IOCTL_AFD_GETSOCKNAME: + if (in_size) FIXME( "unexpected input size %u\n", in_size ); + + status = STATUS_BAD_DEVICE_TYPE; + break; + case IOCTL_AFD_LISTEN: { const struct afd_listen_params *params = in_buffer; diff --git a/include/wine/afd.h b/include/wine/afd.h index 87da44ecca7..04468363673 100644 --- a/include/wine/afd.h +++ b/include/wine/afd.h @@ -36,6 +36,7 @@ #define IOCTL_AFD_LISTEN CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_RECV CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_POLL CTL_CODE(FILE_DEVICE_BEEP, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define IOCTL_AFD_GETSOCKNAME CTL_CODE(FILE_DEVICE_BEEP, 0x80b, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_EVENT_SELECT CTL_CODE(FILE_DEVICE_BEEP, 0x821, METHOD_NEITHER, FILE_ANY_ACCESS) #define IOCTL_AFD_GET_EVENTS CTL_CODE(FILE_DEVICE_BEEP, 0x822, METHOD_NEITHER, FILE_ANY_ACCESS)
diff --git a/server/sock.c b/server/sock.c index 425c07566ac..e5acf585dcc 100644 --- a/server/sock.c +++ b/server/sock.c @@ -109,6 +109,15 @@ #define IP_UNICAST_IF 50 #endif
+union win_sockaddr +{ + struct WS_sockaddr addr; + struct WS_sockaddr_in in; + struct WS_sockaddr_in6 in6; + struct WS_sockaddr_ipx ipx; + SOCKADDR_IRDA irda; +}; + static struct list poll_list = LIST_INIT( poll_list );
struct poll_req @@ -194,10 +203,13 @@ struct sock struct list accept_list; /* list of pending accept requests */ struct accept_req *accept_recv_req; /* pending accept-into request which will recv on this socket */ struct connect_req *connect_req; /* pending connection request */ + union win_sockaddr addr; /* socket name */ + int addr_len; /* socket name length */ 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? */ unsigned int nonblocking : 1; /* is the socket nonblocking? */ + unsigned int bound : 1; /* is the socket bound? */ };
static void sock_dump( struct object *obj, int verbose ); @@ -1365,10 +1377,13 @@ static struct sock *create_socket(void) sock->ifchange_obj = NULL; sock->accept_recv_req = NULL; sock->connect_req = NULL; + memset( &sock->addr, 0, sizeof(sock->addr) ); + sock->addr_len = 0; sock->rd_shutdown = 0; sock->wr_shutdown = 0; sock->wr_shutdown_pending = 0; sock->nonblocking = 0; + sock->bound = 0; init_async_queue( &sock->read_q ); init_async_queue( &sock->write_q ); init_async_queue( &sock->ifchange_q ); @@ -1583,6 +1598,9 @@ static struct sock *accept_socket( struct sock *sock ) } else { + union unix_sockaddr unix_addr; + socklen_t unix_len; + if ((acceptfd = accept_new_fd( sock )) == -1) return NULL; if (!(acceptsock = create_socket())) { @@ -1592,6 +1610,7 @@ static struct sock *accept_socket( struct sock *sock )
/* newly created socket gets the same properties of the listening socket */ acceptsock->state = SOCK_CONNECTED; + acceptsock->bound = 1; acceptsock->nonblocking = sock->nonblocking; acceptsock->mask = sock->mask; acceptsock->proto = sock->proto; @@ -1608,6 +1627,9 @@ static struct sock *accept_socket( struct sock *sock ) release_object( acceptsock ); return NULL; } + unix_len = sizeof(unix_addr); + if (!getsockname( acceptfd, &unix_addr.addr, &unix_len )) + acceptsock->addr_len = sockaddr_from_unix( &unix_addr, &acceptsock->addr.addr, sizeof(acceptsock->addr) ); } clear_error(); sock->pending_events &= ~AFD_POLL_ACCEPT; @@ -1618,6 +1640,8 @@ static struct sock *accept_socket( struct sock *sock )
static int accept_into_socket( struct sock *sock, struct sock *acceptsock ) { + union unix_sockaddr unix_addr; + socklen_t unix_len; int acceptfd; struct fd *newfd;
@@ -1658,6 +1682,10 @@ static int accept_into_socket( struct sock *sock, struct sock *acceptsock ) release_object( acceptsock->fd ); acceptsock->fd = newfd;
+ unix_len = sizeof(unix_addr); + if (!getsockname( get_unix_fd( newfd ), &unix_addr.addr, &unix_len )) + acceptsock->addr_len = sockaddr_from_unix( &unix_addr, &acceptsock->addr.addr, sizeof(acceptsock->addr) ); + clear_error(); sock->pending_events &= ~AFD_POLL_ACCEPT; sock->reported_events &= ~AFD_POLL_ACCEPT; @@ -2075,8 +2103,10 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) case IOCTL_AFD_WINE_CONNECT: { const struct afd_connect_params *params = get_req_data(); + union unix_sockaddr unix_addr; const struct sockaddr *addr; struct connect_req *req; + socklen_t unix_len; int send_len, ret;
if (get_req_data_size() < sizeof(*params) || @@ -2118,6 +2148,11 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) /* a connected or connecting socket can no longer be accepted into */ allow_fd_caching( sock->fd );
+ unix_len = sizeof(unix_addr); + if (!sock->bound && !getsockname( unix_fd, &unix_addr.addr, &unix_len )) + sock->addr_len = sockaddr_from_unix( &unix_addr, &sock->addr.addr, sizeof(sock->addr) ); + sock->bound = 1; + if (!ret) { sock->state = SOCK_CONNECTED; @@ -2351,7 +2386,7 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) case IOCTL_AFD_BIND: { const struct afd_bind_params *params = get_req_data(); - union unix_sockaddr unix_addr; + union unix_sockaddr unix_addr, bind_addr; data_size_t in_size; socklen_t unix_len;
@@ -2376,6 +2411,7 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) set_error( STATUS_INVALID_ADDRESS ); return 0; } + bind_addr = unix_addr;
if (unix_addr.addr.sa_family == WS_AF_INET) { @@ -2383,10 +2419,10 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
if (!memcmp( &unix_addr.in.sin_addr, magic_loopback_addr, 4 ) || bind_to_interface( sock, &unix_addr.in )) - unix_addr.in.sin_addr.s_addr = htonl( INADDR_ANY ); + bind_addr.in.sin_addr.s_addr = htonl( INADDR_ANY ); }
- if (bind( unix_fd, &unix_addr.addr, unix_len ) < 0) + if (bind( unix_fd, &bind_addr.addr, unix_len ) < 0) { if (errno == EADDRINUSE) { @@ -2398,10 +2434,40 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) }
set_error( sock_get_ntstatus( errno ) ); + return 1; } + + sock->bound = 1; + + unix_len = sizeof(bind_addr); + if (!getsockname( unix_fd, &bind_addr.addr, &unix_len )) + { + /* store the interface or magic loopback address instead of the + * actual unix address */ + if (bind_addr.addr.sa_family == AF_INET) + bind_addr.in.sin_addr = unix_addr.in.sin_addr; + sock->addr_len = sockaddr_from_unix( &bind_addr, &sock->addr.addr, sizeof(sock->addr) ); + } + return 1; }
+ case IOCTL_AFD_GETSOCKNAME: + if (!sock->bound) + { + set_error( STATUS_INVALID_PARAMETER ); + return 0; + } + + if (get_reply_max_size() < sock->addr_len) + { + set_error( STATUS_BUFFER_TOO_SMALL ); + return 0; + } + + set_reply_data( &sock->addr, sock->addr_len ); + return 1; + default: set_error( STATUS_NOT_SUPPORTED ); return 0; @@ -2972,6 +3038,17 @@ DECL_HANDLER(send_socket) if (!sock) return; fd = sock->fd;
+ if (sock->type == WS_SOCK_DGRAM) + { + /* sendto() and sendmsg() implicitly binds a socket */ + union unix_sockaddr unix_addr; + socklen_t unix_len = sizeof(unix_addr); + + if (!sock->bound && !getsockname( get_unix_fd( fd ), &unix_addr.addr, &unix_len )) + sock->addr_len = sockaddr_from_unix( &unix_addr, &sock->addr.addr, sizeof(sock->addr) ); + sock->bound = 1; + } + if (status != STATUS_SUCCESS) { /* send() calls only clear and reselect events if unsuccessful. */
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92591
Your paranoid android.
=== debiant2 (32 bit report) ===
ntdll: om.c:2307: Test failed: got 89
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- server/sock.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/server/sock.c b/server/sock.c index e5acf585dcc..8cd3475d7b2 100644 --- a/server/sock.c +++ b/server/sock.c @@ -2449,6 +2449,8 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) sock->addr_len = sockaddr_from_unix( &bind_addr, &sock->addr.addr, sizeof(sock->addr) ); }
+ if (get_reply_max_size() >= sock->addr_len) + set_reply_data( &sock->addr, sock->addr_len ); return 1; }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/tests/afd.c | 231 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index 443b6e4e42f..d4633f7a8bb 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -1333,6 +1333,235 @@ static void test_get_events(void) CloseHandle(event); }
+static void test_bind(void) +{ + const struct sockaddr_in invalid_addr = {.sin_family = AF_INET, .sin_addr.s_addr = inet_addr("192.0.2.0")}; + const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; + struct afd_bind_params params = {0}; + struct sockaddr_in addr, addr2; + struct hostent *host; + IO_STATUS_BLOCK io; + unsigned int i; + HANDLE event; + SOCKET s, s2; + int ret; + + event = CreateEventW(NULL, TRUE, FALSE, NULL); + memset(&addr, 0xcc, sizeof(addr)); + + s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + params.addr.sa_family = 0xdead; + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr)); + ok(ret == STATUS_INVALID_ADDRESS, "got %#x\n", ret); + + memcpy(¶ms.addr, &bind_addr, sizeof(bind_addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params) - 1, &addr, sizeof(addr)); + ok(ret == STATUS_INVALID_ADDRESS, "got %#x\n", ret); + + memcpy(¶ms.addr, &bind_addr, sizeof(bind_addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, offsetof(struct afd_bind_params, addr.sa_data), &addr, sizeof(addr)); + ok(ret == STATUS_INVALID_ADDRESS, "got %#x\n", ret); + + memcpy(¶ms.addr, &bind_addr, sizeof(bind_addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, offsetof(struct afd_bind_params, addr.sa_data) - 1, &addr, sizeof(addr)); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + memcpy(¶ms.addr, &invalid_addr, sizeof(invalid_addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr)); + todo_wine ok(ret == STATUS_PENDING, "got %#x\n", ret); + ret = WaitForSingleObject(event, 0); + ok(!ret, "got %#x\n", ret); + ok(io.Status == STATUS_INVALID_ADDRESS_COMPONENT, "got %#x\n", io.Status); + + memcpy(¶ms.addr, &bind_addr, sizeof(bind_addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr) - 1); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + memcpy(¶ms.addr, &bind_addr, sizeof(bind_addr)); + memset(&io, 0xcc, sizeof(io)); + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr)); + todo_wine ok(ret == STATUS_PENDING, "got %#x\n", ret); + ret = WaitForSingleObject(event, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); + ok(addr.sin_family == AF_INET, "got family %u\n", addr.sin_family); + ok(addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK), "got address %#08x\n", addr.sin_addr.s_addr); + ok(addr.sin_port, "expected nonzero port\n"); + + /* getsockname() returns EINVAL here. Possibly the socket name is cached (in shared memory?) */ + memset(&addr2, 0xcc, sizeof(addr2)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr2, sizeof(addr2)); + ok(!ret, "got %#x\n", ret); + ok(!memcmp(&addr, &addr2, sizeof(addr)), "addresses didn't match\n"); + + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr)); + todo_wine ok(ret == STATUS_ADDRESS_ALREADY_ASSOCIATED, "got %#x\n", ret); + + s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + memcpy(¶ms.addr, &addr2, sizeof(addr2)); + memset(&io, 0xcc, sizeof(io)); + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)s2, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr)); + todo_wine ok(ret == STATUS_PENDING, "got %#x\n", ret); + ret = WaitForSingleObject(event, 0); + ok(!ret, "got %#x\n", ret); + ok(io.Status == STATUS_SHARING_VIOLATION, "got %#x\n", io.Status); + ok(!io.Information, "got %#Ix\n", io.Information); + + closesocket(s2); + closesocket(s); + + /* test UDP */ + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + memcpy(¶ms.addr, &bind_addr, sizeof(bind_addr)); + memset(&io, 0xcc, sizeof(io)); + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr)); + todo_wine ok(ret == STATUS_PENDING, "got %#x\n", ret); + ret = WaitForSingleObject(event, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); + ok(addr.sin_family == AF_INET, "got family %u\n", addr.sin_family); + ok(addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK), "got address %#08x\n", addr.sin_addr.s_addr); + ok(addr.sin_port, "expected nonzero port\n"); + + memset(&addr2, 0xcc, sizeof(addr2)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr2, sizeof(addr2)); + ok(!ret, "got %#x\n", ret); + ok(!memcmp(&addr, &addr2, sizeof(addr)), "addresses didn't match\n"); + + closesocket(s); + + host = gethostbyname(""); + if (host && host->h_length == 4) + { + for (i = 0; host->h_addr_list[i]; ++i) + { + ULONG in_addr = *(ULONG *)host->h_addr_list[i]; + + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + ((struct sockaddr_in *)¶ms.addr)->sin_addr.s_addr = in_addr; + memset(&io, 0xcc, sizeof(io)); + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, + ¶ms, sizeof(params), &addr, sizeof(addr)); + todo_wine ok(ret == STATUS_PENDING, "got %#x\n", ret); + ret = WaitForSingleObject(event, 0); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); + ok(addr.sin_family == AF_INET, "got family %u\n", addr.sin_family); + ok(addr.sin_addr.s_addr == in_addr, "expected address %#08x, got %#08x\n", in_addr, addr.sin_addr.s_addr); + ok(addr.sin_port, "expected nonzero port\n"); + + memset(&addr2, 0xcc, sizeof(addr2)); + ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr2, sizeof(addr2)); + ok(!ret, "got %#x\n", ret); + ok(!memcmp(&addr, &addr2, sizeof(addr)), "addresses didn't match\n"); + + closesocket(s); + } + } + + CloseHandle(event); +} + +static void test_getsockname(void) +{ + const struct sockaddr_in bind_addr = {.sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK)}; + struct sockaddr addr, addr2; + SOCKET server, client; + IO_STATUS_BLOCK io; + HANDLE event; + int ret, len; + + event = CreateEventW(NULL, TRUE, FALSE, NULL); + + tcp_socketpair(&client, &server); + + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr) - 1); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got %#x\n", ret); + + memset(&io, 0xcc, sizeof(io)); + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr)); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); + len = sizeof(addr2); + ret = getsockname(client, (struct sockaddr *)&addr2, &len); + ok(!ret, "got error %u\n", WSAGetLastError()); + ok(!memcmp(&addr, &addr2, sizeof(struct sockaddr)), "addresses didn't match\n"); + + memset(&io, 0xcc, sizeof(io)); + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)server, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr)); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); + len = sizeof(addr2); + ret = getsockname(server, (struct sockaddr *)&addr2, &len); + ok(!ret, "got error %u\n", WSAGetLastError()); + ok(!memcmp(&addr, &addr2, sizeof(struct sockaddr)), "addresses didn't match\n"); + + closesocket(server); + closesocket(client); + + client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr)); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr) - 1); + ok(ret == STATUS_INVALID_PARAMETER, "got %#x\n", ret); + + ret = bind(client, (const struct sockaddr *)&bind_addr, sizeof(bind_addr)); + ok(!ret, "got error %u\n", WSAGetLastError()); + + memset(&io, 0xcc, sizeof(io)); + memset(&addr, 0xcc, sizeof(addr)); + ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, + IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr)); + todo_wine ok(!ret, "got %#x\n", ret); + todo_wine ok(!io.Status, "got %#x\n", io.Status); + todo_wine ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); + len = sizeof(addr2); + ret = getsockname(client, (struct sockaddr *)&addr2, &len); + ok(!ret, "got error %u\n", WSAGetLastError()); + todo_wine ok(!memcmp(&addr, &addr2, sizeof(struct sockaddr)), "addresses didn't match\n"); + + closesocket(client); + + CloseHandle(event); +} + START_TEST(afd) { WSADATA data; @@ -1345,6 +1574,8 @@ START_TEST(afd) test_recv(); test_event_select(); test_get_events(); + test_bind(); + test_getsockname();
WSACleanup(); }
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/tests/afd.c | 2 +- server/sock.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index d4633f7a8bb..eb4f0a42f8d 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -1407,7 +1407,7 @@ static void test_bind(void)
ret = NtDeviceIoControlFile((HANDLE)s, event, NULL, NULL, &io, IOCTL_AFD_BIND, ¶ms, sizeof(params), &addr, sizeof(addr)); - todo_wine ok(ret == STATUS_ADDRESS_ALREADY_ASSOCIATED, "got %#x\n", ret); + ok(ret == STATUS_ADDRESS_ALREADY_ASSOCIATED, "got %#x\n", ret);
s2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
diff --git a/server/sock.c b/server/sock.c index 8cd3475d7b2..6b0c4af6b11 100644 --- a/server/sock.c +++ b/server/sock.c @@ -2405,6 +2405,12 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async ) return 0; }
+ if (sock->bound) + { + set_error( STATUS_ADDRESS_ALREADY_ASSOCIATED ); + return 0; + } + unix_len = sockaddr_to_unix( ¶ms->addr, in_size - sizeof(int), &unix_addr ); if (!unix_len) {
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ws2_32/socket.c | 352 ++++++----------------------------- dlls/ws2_32/tests/afd.c | 8 +- dlls/ws2_32/tests/sock.c | 4 +- dlls/ws2_32/ws2_32_private.h | 4 - 4 files changed, 59 insertions(+), 309 deletions(-)
diff --git a/dlls/ws2_32/socket.c b/dlls/ws2_32/socket.c index 2fa1471ae3b..63c26a9c769 100644 --- a/dlls/ws2_32/socket.c +++ b/dlls/ws2_32/socket.c @@ -160,44 +160,8 @@ static const WSAPROTOCOL_INFOW supported_protocols[] = }, };
-#if defined(IP_UNICAST_IF) && defined(SO_ATTACH_FILTER) -# define LINUX_BOUND_IF -struct interface_filter { - struct sock_filter iface_memaddr; - struct sock_filter iface_rule; - struct sock_filter ip_memaddr; - struct sock_filter ip_rule; - struct sock_filter return_keep; - struct sock_filter return_dump; -}; -# define FILTER_JUMP_DUMP(here) (u_char)(offsetof(struct interface_filter, return_dump) \ - -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \ - /sizeof(struct sock_filter) -# define FILTER_JUMP_KEEP(here) (u_char)(offsetof(struct interface_filter, return_keep) \ - -offsetof(struct interface_filter, here)-sizeof(struct sock_filter)) \ - /sizeof(struct sock_filter) -# define FILTER_JUMP_NEXT() (u_char)(0) -# define SKF_NET_DESTIP 16 /* offset in the network header to the destination IP */ -static struct interface_filter generic_interface_filter = { - /* This filter rule allows incoming packets on the specified interface, which works for all - * remotely generated packets and for locally generated broadcast packets. */ - BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SKF_AD_OFF+SKF_AD_IFINDEX), - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xdeadbeef, FILTER_JUMP_KEEP(iface_rule), FILTER_JUMP_NEXT()), - /* This rule allows locally generated packets targeted at the specific IP address of the chosen - * adapter (local packets not destined for the broadcast address do not have IFINDEX set) */ - BPF_STMT(BPF_LD+BPF_W+BPF_ABS, SKF_NET_OFF+SKF_NET_DESTIP), - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xdeadbeef, FILTER_JUMP_KEEP(ip_rule), FILTER_JUMP_DUMP(ip_rule)), - BPF_STMT(BPF_RET+BPF_K, (u_int)-1), /* keep packet */ - BPF_STMT(BPF_RET+BPF_K, 0) /* dump packet */ -}; -#endif /* LINUX_BOUND_IF */ - -DECLARE_CRITICAL_SECTION(cs_if_addr_cache); DECLARE_CRITICAL_SECTION(cs_socket_list);
-static in_addr_t *if_addr_cache; -static unsigned int if_addr_cache_size; - static SOCKET *socket_list; static unsigned int socket_list_size;
@@ -1915,197 +1879,57 @@ static int WINAPI WS2_WSARecvMsg( SOCKET s, LPWSAMSG msg, LPDWORD lpNumberOfByte lpOverlapped, lpCompletionRoutine, &msg->Control ); }
-/*********************************************************************** - * interface_bind (INTERNAL) - * - * Take bind() calls on any name corresponding to a local network adapter and restrict the given socket to - * operating only on the specified interface. This restriction consists of two components: - * 1) An outgoing packet restriction suggesting the egress interface for all packets. - * 2) An incoming packet restriction dropping packets not meant for the interface. - * If the function succeeds in placing these restrictions (returns TRUE) then the name for the bind() may - * safely be changed to INADDR_ANY, permitting the transmission and receipt of broadcast packets on the - * socket. This behavior is only relevant to UDP sockets and is needed for applications that expect to be able - * to receive broadcast packets on a socket that is bound to a specific network interface. - */ -static BOOL interface_bind( SOCKET s, int fd, struct sockaddr *addr ) -{ -#if defined(HAVE_GETIFADDRS) && (defined(IP_BOUND_IF) || defined(LINUX_BOUND_IF)) - struct sockaddr_in *in_sock = (struct sockaddr_in *) addr; - in_addr_t bind_addr = in_sock->sin_addr.s_addr; - struct ifaddrs *ifaddrs, *ifaddr; - unsigned int index; - BOOL ret = FALSE; - int enable = 1; - - if (bind_addr == htonl(INADDR_ANY) || bind_addr == htonl(INADDR_LOOPBACK)) - return FALSE; /* Not binding to a network adapter, special interface binding unnecessary. */ - if (_get_fd_type(fd) != SOCK_DGRAM) - return FALSE; /* Special interface binding is only necessary for UDP datagrams. */ - - if (getifaddrs( &ifaddrs ) < 0) return FALSE; - - for (ifaddr = ifaddrs; ifaddr != NULL; ifaddr = ifaddr->ifa_next) - { - if (ifaddr->ifa_addr && ifaddr->ifa_addr->sa_family == AF_INET - && ((struct sockaddr_in *)ifaddr->ifa_addr)->sin_addr.s_addr == bind_addr) - { - index = if_nametoindex( ifaddr->ifa_name ); - if (!index) - { - ERR( "Unable to look up interface index for %s: %s\n", ifaddr->ifa_name, strerror( errno ) ); - continue; - } - -#if defined(IP_BOUND_IF) - /* IP_BOUND_IF sets both the incoming and outgoing restriction at once */ - if (setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &index, sizeof(index)) != 0) - goto cleanup; - ret = TRUE; -#elif defined(LINUX_BOUND_IF) - { - in_addr_t ifindex = (in_addr_t) htonl(index); - struct interface_filter specific_interface_filter; - struct sock_fprog filter_prog; - - if (setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)) != 0) - goto cleanup; /* Failed to suggest egress interface */ - specific_interface_filter = generic_interface_filter; - specific_interface_filter.iface_rule.k = index; - specific_interface_filter.ip_rule.k = htonl(bind_addr); - filter_prog.len = sizeof(generic_interface_filter)/sizeof(struct sock_filter); - filter_prog.filter = (struct sock_filter *) &specific_interface_filter; - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) != 0) - goto cleanup; /* Failed to specify incoming packet filter */ - ret = TRUE; - } -#endif - if (ret) - { - EnterCriticalSection(&cs_if_addr_cache); - if (if_addr_cache_size <= index) - { - unsigned int new_size; - in_addr_t *new; - - new_size = max(if_addr_cache_size * 2, index + 1); - if (!(new = heap_realloc(if_addr_cache, sizeof(*if_addr_cache) * new_size))) - { - ERR("No memory.\n"); - ret = FALSE; - LeaveCriticalSection(&cs_if_addr_cache); - break; - } - memset(new + if_addr_cache_size, 0, sizeof(*if_addr_cache) - * (new_size - if_addr_cache_size)); - if_addr_cache = new; - if_addr_cache_size = new_size; - } - if (if_addr_cache[index] && if_addr_cache[index] != bind_addr) - WARN("Adapter addr for iface index %u has changed.\n", index); - - if_addr_cache[index] = bind_addr; - LeaveCriticalSection(&cs_if_addr_cache); - } - break; - } - } - /* Will soon be switching to INADDR_ANY: permit address reuse */ - if (ret && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)) == 0) - TRACE("Socket %04lx bound to interface index %d\n", s, index); - else - ret = FALSE; - -cleanup: - if(!ret) - ERR("Failed to bind to interface, receiving broadcast packets will not work on socket %04lx.\n", s); - freeifaddrs( ifaddrs ); - return ret; -#else - FIXME( "Broadcast packets on interface-bound sockets are not currently supported on this platform.\n" ); - return FALSE; -#endif -}
/*********************************************************************** - * bind (WS2_32.2) + * bind (ws2_32.2) */ -int WINAPI WS_bind(SOCKET s, const struct WS_sockaddr* name, int namelen) +int WINAPI WS_bind( SOCKET s, const struct WS_sockaddr *addr, int len ) { - int fd = get_sock_fd( s, 0, NULL ); - int res = SOCKET_ERROR; + struct afd_bind_params *params; + struct WS_sockaddr *ret_addr; + IO_STATUS_BLOCK io; + HANDLE sync_event; + NTSTATUS status;
- TRACE("socket %04lx, ptr %p %s, length %d\n", s, name, debugstr_sockaddr(name), namelen); + TRACE( "socket %#lx, addr %s\n", s, debugstr_sockaddr(addr) );
- if (fd != -1) + if (!addr || (addr->sa_family && !supported_pf( addr->sa_family ))) { - if (!name || (name->sa_family && !supported_pf(name->sa_family))) - { - SetLastError(WSAEAFNOSUPPORT); - } - else - { - union generic_unix_sockaddr uaddr; - unsigned int uaddrlen = ws_sockaddr_ws2u(name, namelen, &uaddr); - if (!uaddrlen) - { - SetLastError(WSAEFAULT); - } - else - { - if (name->sa_family == WS_AF_INET) - { - struct sockaddr_in *in4 = (struct sockaddr_in*) &uaddr; - if (memcmp(&in4->sin_addr, magic_loopback_addr, 4) == 0) - { - /* Trying to bind to the default host interface, using - * INADDR_ANY instead*/ - WARN("Trying to bind to magic IP address, using " - "INADDR_ANY instead.\n"); - in4->sin_addr.s_addr = htonl(INADDR_ANY); - } - else if (interface_bind(s, fd, &uaddr.addr)) - in4->sin_addr.s_addr = htonl(INADDR_ANY); - } - if (bind(fd, &uaddr.addr, uaddrlen) < 0) - { - int loc_errno = errno; - WARN("\tfailure - errno = %i\n", errno); - errno = loc_errno; - switch (errno) - { - case EADDRNOTAVAIL: - SetLastError(WSAEINVAL); - break; - case EADDRINUSE: - { - int optval = 0; - socklen_t optlen = sizeof(optval); - /* Windows >= 2003 will return different results depending on - * SO_REUSEADDR, WSAEACCES may be returned representing that - * the socket hijacking protection prevented the bind */ - if (!getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, &optlen) && optval) - { - SetLastError(WSAEACCES); - break; - } - /* fall through */ - } - default: - SetLastError(wsaErrno()); - break; - } - } - else - { - res=0; /* success */ - } - } - } - release_sock_fd( s, fd ); + SetLastError( WSAEAFNOSUPPORT ); + return -1; } - return res; + + if (!(sync_event = get_sync_event())) return -1; + + params = HeapAlloc( GetProcessHeap(), 0, sizeof(int) + len ); + ret_addr = HeapAlloc( GetProcessHeap(), 0, len ); + if (!params || !ret_addr) + { + HeapFree( GetProcessHeap(), 0, params ); + HeapFree( GetProcessHeap(), 0, ret_addr ); + SetLastError( WSAENOBUFS ); + return -1; + } + params->unknown = 0; + memcpy( ¶ms->addr, addr, len ); + + status = NtDeviceIoControlFile( (HANDLE)s, sync_event, NULL, NULL, &io, IOCTL_AFD_BIND, + params, sizeof(int) + len, ret_addr, len ); + if (status == STATUS_PENDING) + { + if (WaitForSingleObject( sync_event, INFINITE ) == WAIT_FAILED) + return -1; + status = io.u.Status; + } + + HeapFree( GetProcessHeap(), 0, params ); + HeapFree( GetProcessHeap(), 0, ret_addr ); + + SetLastError( status == STATUS_INVALID_PARAMETER ? WSAEFAULT : NtStatusToWSAError( status ) ); + return status ? -1 : 0; }
+ /*********************************************************************** * closesocket (WS2_32.3) */ @@ -2339,98 +2163,28 @@ int WINAPI WS_getpeername(SOCKET s, struct WS_sockaddr *name, int *namelen) return res; }
-/* When binding to an UDP address with filter support the getsockname call on the socket - * will always return 0.0.0.0 instead of the filtered interface address. This function - * checks if the socket is interface-bound on UDP and return the correct address. - * This is required because applications often do a bind() with port zero followed by a - * getsockname() to retrieve the port and address acquired. - */ -static void interface_bind_check(int fd, struct sockaddr_in *addr) -{ -#if !defined(IP_BOUND_IF) && !defined(LINUX_BOUND_IF) - return; -#else - unsigned int ifindex; - int ret; - socklen_t len; - - /* Check for IPv4, address 0.0.0.0 and UDP socket */ - if (addr->sin_family != AF_INET || addr->sin_addr.s_addr != 0) - return; - if (_get_fd_type(fd) != SOCK_DGRAM) - return; - - len = sizeof(ifindex); -#if defined(IP_BOUND_IF) - ret = getsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &ifindex, &len); -#elif defined(LINUX_BOUND_IF) - ret = getsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, &len); - if (!ret) ifindex = ntohl(ifindex); -#endif - if (!ret && ifindex) - { - EnterCriticalSection(&cs_if_addr_cache); - if (ifindex < if_addr_cache_size) - addr->sin_addr.s_addr = if_addr_cache[ifindex]; - else - ERR("No cache entry for ifindex %u.\n", ifindex); - LeaveCriticalSection(&cs_if_addr_cache); - } -#endif -}
/*********************************************************************** - * getsockname (WS2_32.6) + * getsockname (ws2_32.6) */ -int WINAPI WS_getsockname(SOCKET s, struct WS_sockaddr *name, int *namelen) +int WINAPI WS_getsockname( SOCKET s, struct WS_sockaddr *addr, int *len ) { - int fd; - int res; + IO_STATUS_BLOCK io; + NTSTATUS status;
- TRACE("socket %04lx, ptr %p, len %08x\n", s, name, namelen ? *namelen : 0); + TRACE( "socket %#lx, addr %p, len %d\n", s, addr, len ? *len : 0 );
- /* Check if what we've received is valid. Should we use IsBadReadPtr? */ - if( (name == NULL) || (namelen == NULL) ) + if (!addr) { SetLastError( WSAEFAULT ); - return SOCKET_ERROR; + return -1; }
- fd = get_sock_fd( s, 0, NULL ); - res = SOCKET_ERROR; - - if (fd != -1) - { - union generic_unix_sockaddr uaddr; - socklen_t uaddrlen; - int bound = is_fd_bound(fd, &uaddr, &uaddrlen); - - if (bound <= 0) - { - SetLastError(bound == -1 ? wsaErrno() : WSAEINVAL); - } - else if (ws_sockaddr_u2ws(&uaddr.addr, name, namelen) != 0) - { - /* The buffer was too small */ - SetLastError(WSAEFAULT); - } - else - { - interface_bind_check(fd, (struct sockaddr_in*) &uaddr); - if (ws_sockaddr_u2ws(&uaddr.addr, name, namelen) != 0) - { - /* The buffer was too small */ - SetLastError(WSAEFAULT); - } - else - { - res = 0; - TRACE("=> %s\n", debugstr_sockaddr(name)); - } - } - release_sock_fd( s, fd ); - } - return res; + status = NtDeviceIoControlFile( (HANDLE)s, NULL, NULL, NULL, &io, IOCTL_AFD_GETSOCKNAME, NULL, 0, addr, *len ); + if (!status) + *len = io.Information; + WSASetLastError( NtStatusToWSAError( status ) ); + return status ? -1 : 0; }
/*********************************************************************** diff --git a/dlls/ws2_32/tests/afd.c b/dlls/ws2_32/tests/afd.c index eb4f0a42f8d..8f7f4acf4f2 100644 --- a/dlls/ws2_32/tests/afd.c +++ b/dlls/ws2_32/tests/afd.c @@ -1549,13 +1549,13 @@ static void test_getsockname(void) memset(&addr, 0xcc, sizeof(addr)); ret = NtDeviceIoControlFile((HANDLE)client, event, NULL, NULL, &io, IOCTL_AFD_GETSOCKNAME, NULL, 0, &addr, sizeof(addr)); - todo_wine ok(!ret, "got %#x\n", ret); - todo_wine ok(!io.Status, "got %#x\n", io.Status); - todo_wine ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); + ok(!ret, "got %#x\n", ret); + ok(!io.Status, "got %#x\n", io.Status); + ok(io.Information == sizeof(addr), "got %#Ix\n", io.Information); len = sizeof(addr2); ret = getsockname(client, (struct sockaddr *)&addr2, &len); ok(!ret, "got error %u\n", WSAGetLastError()); - todo_wine ok(!memcmp(&addr, &addr2, sizeof(struct sockaddr)), "addresses didn't match\n"); + ok(!memcmp(&addr, &addr2, sizeof(struct sockaddr)), "addresses didn't match\n");
closesocket(client);
diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 2d2b4c40141..bb662d78f92 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -3604,7 +3604,7 @@ static void test_getsockname(void) WSASetLastError(0xdeadbeef); ret = getsockname(sock, (struct sockaddr *) &sa_get, &sa_get_len); ok(!ret, "got %d\n", ret); - todo_wine ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError()); + ok(!WSAGetLastError(), "got error %u\n", WSAGetLastError());
ret = memcmp(sa_get.sin_zero, null_padding, 8); ok(ret == 0, "getsockname did not zero the sockaddr_in structure\n"); @@ -10292,7 +10292,7 @@ static void test_bind(void) WSASetLastError(0xdeadbeef); ret = bind(s, (const struct sockaddr *)&invalid_addr, sizeof(invalid_addr)); ok(ret == -1, "expected failure\n"); - todo_wine ok(WSAGetLastError() == WSAEADDRNOTAVAIL, "got error %u\n", WSAGetLastError()); + ok(WSAGetLastError() == WSAEADDRNOTAVAIL, "got error %u\n", WSAGetLastError());
WSASetLastError(0xdeadbeef); ret = bind(s, (const struct sockaddr *)&bind_addr, sizeof(bind_addr)); diff --git a/dlls/ws2_32/ws2_32_private.h b/dlls/ws2_32/ws2_32_private.h index c27c697a8b6..8baa62908dd 100644 --- a/dlls/ws2_32/ws2_32_private.h +++ b/dlls/ws2_32/ws2_32_private.h @@ -89,9 +89,6 @@ # undef if_indextoname # undef if_nametoindex #endif -#ifdef HAVE_LINUX_FILTER_H -# include <linux/filter.h> -#endif #ifdef HAVE_IFADDRS_H # include <ifaddrs.h> #endif @@ -151,7 +148,6 @@ #include "winnt.h" #define USE_WC_PREFIX /* For CMSG_DATA */ #include "iphlpapi.h" -#include "netioapi.h" #include "ip2string.h" #include "wine/afd.h" #include "wine/server.h"
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92595
Your paranoid android.
=== w2008s64 (32 bit report) ===
ws2_32: sock.c:3607: Test failed: got error 3735928559
=== wvistau64 (64 bit report) ===
ws2_32: sock.c:3607: Test failed: got error 3735928559
=== w2008s64 (64 bit report) ===
ws2_32: sock.c:3607: Test failed: got error 3735928559
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=92589
Your paranoid android.
=== w2008s64 (32 bit report) ===
ws2_32: sock.c:3607: Test failed: got error 3735928559
=== wvistau64 (64 bit report) ===
ws2_32: sock.c:3607: Test failed: got error 3735928559
=== w2008s64 (64 bit report) ===
ws2_32: sock.c:3607: Test failed: got error 3735928559
=== debiant2 (32 bit Hebrew:Israel report) ===
ws2_32: sock.c:8253: Test succeeded inside todo block: GetQueuedCompletionStatus returned 0 sock.c:8254: Test succeeded inside todo block: Last error was 64