resolv_test.c: also cope with CONNREFUSED errors returned by recvfrom

Message ID 20170910143816.v5tdzfyywtbi7aae@var.youpi.perso.aquilenet.fr
State Dropped, archived
Headers

Commit Message

Samuel Thibault Sept. 10, 2017, 2:38 p.m. UTC
  server_thread_udp_process_one already takes care of calling sendto()
instead of xsendto to be able to ignore the case where the client has
closed the socket.  Depending on the TCP/IP stack behavior, this error
could be notified later through recvfrom(), so we need to ignore it
there too.

* support/resolv_test.c (server_thread_udp_process_one): Call recvfrom
instead of xrecvfrom, and ignore ECONNREFUSED errors.
  

Comments

Samuel Thibault Sept. 24, 2017, 11:12 p.m. UTC | #1
Samuel Thibault, on dim. 10 sept. 2017 16:38:16 +0200, wrote:
> server_thread_udp_process_one already takes care of calling sendto()
> instead of xsendto to be able to ignore the case where the client has
> closed the socket.  Depending on the TCP/IP stack behavior, this error
> could be notified later through recvfrom(), so we need to ignore it
> there too.
> 
> * support/resolv_test.c (server_thread_udp_process_one): Call recvfrom
> instead of xrecvfrom, and ignore ECONNREFUSED errors.

I have commited it.

> diff --git a/support/resolv_test.c b/support/resolv_test.c
> index 1625dcf43a..c3325b89b1 100644
> --- a/support/resolv_test.c
> +++ b/support/resolv_test.c
> @@ -600,7 +600,7 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index)
>    unsigned char query[512];
>    struct sockaddr_storage peer;
>    socklen_t peerlen = sizeof (peer);
> -  size_t length = xrecvfrom (obj->servers[server_index].socket_udp,
> +  ssize_t length = recvfrom (obj->servers[server_index].socket_udp,
>                               query, sizeof (query), 0,
>                               (struct sockaddr *) &peer, &peerlen);
>    /* Check for termination.  */
> @@ -613,6 +613,12 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index)
>        return false;
>    }
>  
> +  if (length < 0)
> +    {
> +      /* The other end had closed the socket, and we are notified only now. */
> +      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
> +      return true;
> +    }
>  
>    struct query_info qinfo;
>    parse_query (&qinfo, query, length);
  
Florian Weimer Sept. 25, 2017, 7:31 a.m. UTC | #2
On 09/10/2017 04:38 PM, Samuel Thibault wrote:
> +  if (length < 0)
> +    {
> +      /* The other end had closed the socket, and we are notified only now. */
> +      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
> +      return true;
> +    }

Sorry for not replying sooner.

This UDP socket is unconnected.  If asynchronous error notifications are 
received on it, this is arguably a TCP/IP stack bug.

Thanks,
Florian
  
Samuel Thibault Sept. 25, 2017, 7:38 a.m. UTC | #3
Florian Weimer, on lun. 25 sept. 2017 09:31:02 +0200, wrote:
> On 09/10/2017 04:38 PM, Samuel Thibault wrote:
> > +  if (length < 0)
> > +    {
> > +      /* The other end had closed the socket, and we are notified only now. */
> > +      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
> > +      return true;
> > +    }
> 
> Sorry for not replying sooner.
> 
> This UDP socket is unconnected.  If asynchronous error notifications are
> received on it, this is arguably a TCP/IP stack bug.

Reception of "port unreachable" icmp packets can be asynchronous with
UDP too.

Samuel
  
Florian Weimer Sept. 25, 2017, 8:20 a.m. UTC | #4
On 09/25/2017 09:38 AM, Samuel Thibault wrote:
> Florian Weimer, on lun. 25 sept. 2017 09:31:02 +0200, wrote:
>> On 09/10/2017 04:38 PM, Samuel Thibault wrote:
>>> +  if (length < 0)
>>> +    {
>>> +      /* The other end had closed the socket, and we are notified only now. */
>>> +      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
>>> +      return true;
>>> +    }
>>
>> Sorry for not replying sooner.
>>
>> This UDP socket is unconnected.  If asynchronous error notifications are
>> received on it, this is arguably a TCP/IP stack bug.
> 
> Reception of "port unreachable" icmp packets can be asynchronous with
> UDP too.

Stevens says that this can happen on connected sockets only.  Both TCPv2 
and UNIX Network Programming have detailed explanations.  I can 
summarize those if you don't have access.

The only quoted exception is Linux, but the kernel behavior changed 
around 2.4.0.

Thanks,
Florian
  
Samuel Thibault Sept. 25, 2017, 8:30 a.m. UTC | #5
Florian Weimer, on lun. 25 sept. 2017 10:20:46 +0200, wrote:
> On 09/25/2017 09:38 AM, Samuel Thibault wrote:
> > Florian Weimer, on lun. 25 sept. 2017 09:31:02 +0200, wrote:
> > > On 09/10/2017 04:38 PM, Samuel Thibault wrote:
> > > > +  if (length < 0)
> > > > +    {
> > > > +      /* The other end had closed the socket, and we are notified only now. */
> > > > +      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
> > > > +      return true;
> > > > +    }
> > > 
> > > Sorry for not replying sooner.
> > > 
> > > This UDP socket is unconnected.  If asynchronous error notifications are
> > > received on it, this is arguably a TCP/IP stack bug.
> > 
> > Reception of "port unreachable" icmp packets can be asynchronous with
> > UDP too.
> 
> Stevens says that this can happen on connected sockets only.  Both TCPv2 and
> UNIX Network Programming have detailed explanations.  I can summarize those
> if you don't have access.
> 
> The only quoted exception is Linux, but the kernel behavior changed around
> 2.4.0.

Running Linux 4.13

$ strace nc localhost 12345 -u
...
sendmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="kljsdf\n", iov_len=7}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 7
select(5, [3 4], [], NULL, NULL)        = 1 (in [3])
recvmsg(3, {msg_namelen=0}, 0)          = -1 ECONNREFUSED (Connection refused)

Samuel
  
Florian Weimer Sept. 25, 2017, 8:33 a.m. UTC | #6
On 09/25/2017 10:30 AM, Samuel Thibault wrote:
> Florian Weimer, on lun. 25 sept. 2017 10:20:46 +0200, wrote:
>> On 09/25/2017 09:38 AM, Samuel Thibault wrote:
>>> Florian Weimer, on lun. 25 sept. 2017 09:31:02 +0200, wrote:
>>>> On 09/10/2017 04:38 PM, Samuel Thibault wrote:
>>>>> +  if (length < 0)
>>>>> +    {
>>>>> +      /* The other end had closed the socket, and we are notified only now. */
>>>>> +      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
>>>>> +      return true;
>>>>> +    }
>>>>
>>>> Sorry for not replying sooner.
>>>>
>>>> This UDP socket is unconnected.  If asynchronous error notifications are
>>>> received on it, this is arguably a TCP/IP stack bug.
>>>
>>> Reception of "port unreachable" icmp packets can be asynchronous with
>>> UDP too.
>>
>> Stevens says that this can happen on connected sockets only.  Both TCPv2 and
>> UNIX Network Programming have detailed explanations.  I can summarize those
>> if you don't have access.
>>
>> The only quoted exception is Linux, but the kernel behavior changed around
>> 2.4.0.
> 
> Running Linux 4.13
> 
> $ strace nc localhost 12345 -u
> ...
> sendmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="kljsdf\n", iov_len=7}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 7
> select(5, [3 4], [], NULL, NULL)        = 1 (in [3])
> recvmsg(3, {msg_namelen=0}, 0)          = -1 ECONNREFUSED (Connection refused)
> 
> Samuel

Surely this is a connected socket, otherwise the sendmsg call would fail 
due to the lack of an address to send the datagram to.

This entire discussion is specific to unconnected sockets, where the 
application has no way to match error responses to sent packets (unless 
IP_RECVERR is set and the information is received as ancillary data).

Thanks,
Florian
  
Samuel Thibault Sept. 25, 2017, 6:48 p.m. UTC | #7
Hello,

Florian Weimer, on lun. 25 sept. 2017 10:33:52 +0200, wrote:
> On 09/25/2017 10:30 AM, Samuel Thibault wrote:
> > Florian Weimer, on lun. 25 sept. 2017 10:20:46 +0200, wrote:
> > > On 09/25/2017 09:38 AM, Samuel Thibault wrote:
> > > > Florian Weimer, on lun. 25 sept. 2017 09:31:02 +0200, wrote:
> > > > > On 09/10/2017 04:38 PM, Samuel Thibault wrote:
> > > > > > +  if (length < 0)
> > > > > > +    {
> > > > > > +      /* The other end had closed the socket, and we are notified only now. */
> > > > > > +      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
> > > > > > +      return true;
> > > > > > +    }
> > > > > 
> > > > > Sorry for not replying sooner.
> > > > > 
> > > > > This UDP socket is unconnected.  If asynchronous error notifications are
> > > > > received on it, this is arguably a TCP/IP stack bug.
> > > > 
> > > > Reception of "port unreachable" icmp packets can be asynchronous with
> > > > UDP too.
> > > 
> > > Stevens says that this can happen on connected sockets only.  Both TCPv2 and
> > > UNIX Network Programming have detailed explanations.  I can summarize those
> > > if you don't have access.
> > > 
> > > The only quoted exception is Linux, but the kernel behavior changed around
> > > 2.4.0.
> > 
> > Running Linux 4.13
> > 
> > $ strace nc localhost 12345 -u
> > ...
> > sendmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="kljsdf\n", iov_len=7}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 7
> > select(5, [3 4], [], NULL, NULL)        = 1 (in [3])
> > recvmsg(3, {msg_namelen=0}, 0)          = -1 ECONNREFUSED (Connection refused)
> 
> Surely this is a connected socket, otherwise the sendmsg call would fail due
> to the lack of an address to send the datagram to.

Ah, ok, yes, I misunderstood "connected" above.

> This entire discussion is specific to unconnected sockets, where the
> application has no way to match error responses to sent packets (unless
> IP_RECVERR is set and the information is received as ancillary data).

Indeed.  I have reverted my commit.

Thanks,
Samuel
  

Patch

diff --git a/support/resolv_test.c b/support/resolv_test.c
index 1625dcf43a..c3325b89b1 100644
--- a/support/resolv_test.c
+++ b/support/resolv_test.c
@@ -600,7 +600,7 @@  server_thread_udp_process_one (struct resolv_test *obj, int server_index)
   unsigned char query[512];
   struct sockaddr_storage peer;
   socklen_t peerlen = sizeof (peer);
-  size_t length = xrecvfrom (obj->servers[server_index].socket_udp,
+  ssize_t length = recvfrom (obj->servers[server_index].socket_udp,
                              query, sizeof (query), 0,
                              (struct sockaddr *) &peer, &peerlen);
   /* Check for termination.  */
@@ -613,6 +613,12 @@  server_thread_udp_process_one (struct resolv_test *obj, int server_index)
       return false;
   }
 
+  if (length < 0)
+    {
+      /* The other end had closed the socket, and we are notified only now. */
+      TEST_VERIFY_EXIT (errno == ECONNREFUSED);
+      return true;
+    }
 
   struct query_info qinfo;
   parse_query (&qinfo, query, length);