Bug 245817 - sendto() can return ENOTCONN on SOCK_STREAM unix socket, which is not documented
Summary: sendto() can return ENOTCONN on SOCK_STREAM unix socket, which is not documented
Status: Closed FIXED
Alias: None
Product: Documentation
Classification: Unclassified
Component: Manual Pages (show other bugs)
Version: Latest
Hardware: Any Any
: --- Affects Only Me
Assignee: Mark Johnston
URL: https://reviews.freebsd.org/D24530
Keywords: patch
Depends on:
Blocks:
 
Reported: 2020-04-22 10:01 UTC by Erik Inge Bolsø
Modified: 2020-05-04 12:29 UTC (History)
4 users (show)

See Also:


Attachments
Minimal test program (1.05 KB, text/plain)
2020-04-22 10:01 UTC, Erik Inge Bolsø
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Erik Inge Bolsø 2020-04-22 10:01:25 UTC
Created attachment 213673 [details]
Minimal test program

send(2) manpage currently states "The send() function may be used only when the  socket is in a connected state, while sendto(), sendmsg() and sendmmsg() may be used at any time."

This seems inaccurate. Doing a sendto() on an unconnected SOCK_STREAM unix socket results in an ENOTCONN error. ENOTCONN is not a documented possible error from sendto.

Small c testcase showing the behaviour attached.

Suggested manpage patch submitted:
https://reviews.freebsd.org/D24530
Comment 1 Mark Johnston freebsd_committer freebsd_triage 2020-04-22 16:50:43 UTC
Hmm, I would expect this to work. Looking.
Comment 2 Mark Johnston freebsd_committer freebsd_triage 2020-04-22 17:32:21 UTC
I see.  The SOCK_STREAM/SEQPACKET unix socket protocol descriptions set PR_CONNREQUIRED, which effectively disables sendto()/sendmsg() on unconnected unix sockets.  That is surprising since uipc_send() has explicit handling for that:

1137         case SOCK_SEQPACKET:                                                                                                                             
1138         case SOCK_STREAM:                                                                                                                                
1139                 if ((so->so_state & SS_ISCONNECTED) == 0) {                                                                                              
1140                         if (nam != NULL) {                                                                                                               
1141                                 error = connect_internal(so, nam, td);                                                                                   
1142                                 if (error != 0)                                                                                                          
1143                                         break;                                                                                                           
1144                         } else {                                                                                                                         
1145                                 error = ENOTCONN;                                                                                                        
1146                                 break;                                                                                                                   
1147                         }                                                                                                                                
1148                 } else {                                                                                                                                 
1149                         UNP_PCB_LOCK(unp);                                                                                                               
1150                 }

I think we can probably just clear PR_CONNREQUIRED for unix sockets.  Though, I think we should indeed document ENOTCONN as a possible error for sendto() and sendmsg().  It is documented here for instance: https://pubs.opengroup.org/onlinepubs/009695399/functions/sendto.html

Also weird is this fragment in sosend_generic():

1626                         /*                                                                                                                               
1627                          * `sendto' and `sendmsg' is allowed on a connection-                                                                            
1628                          * based socket if it supports implied connect.                                                                                  
1629                          * Return ENOTCONN if not connected and no address is                                                                            
1630                          * supplied.                                                                                                                     
1631                          */                                                                                                                              
1632                         if ((so->so_proto->pr_flags & PR_CONNREQUIRED) &&                                                                                
1633                             (so->so_proto->pr_flags & PR_IMPLOPCL) == 0) {                                                                               
1634                                 if ((so->so_state & SS_ISCONFIRMING) == 0 &&                                                                             
1635                                     !(resid == 0 && clen != 0)) {                                                                                        
1636                                         SOCKBUF_UNLOCK(&so->so_snd);                                                                                     
1637                                         error = ENOTCONN;                                                                                                
1638                                         goto release;                                                                                                    
1639                                 }

Specifically, the !(resid == 0 && clen != 0) check: we only return an error if we are trying to send data and there is no control message. So, we *can* send control messages over unconnected stream sockets even if CONNREQUIRED is set.

Does any protocol except PF_LOCAL handle control messages to begin with?
Comment 3 commit-hook freebsd_committer freebsd_triage 2020-04-27 16:12:58 UTC
A commit references this bug:

Author: markj
Date: Mon Apr 27 16:12:33 UTC 2020
New revision: 360384
URL: https://svnweb.freebsd.org/changeset/base/360384

Log:
  Document handling of connection-mode sockets by sendto(2).

  sendto(2), sendmsg(2) and sendmmsg(2) return ENOTCONN if a destination
  address is specified and the socket is not connected and the socket
  protocol does not automatically connect ("implied connect").  Document
  that.  Also document the fact that the destination address is ignored
  for connection-mode sockets if the socket is already connected.

  PR:		245817
  Submitted by:	Erik Inge Bols? <knan-bfo@modirum.com>
  MFC after:	1 week
  Differential Revision:	https://reviews.freebsd.org/D24530

Changes:
  head/lib/libc/sys/send.2
Comment 4 Mark Johnston freebsd_committer freebsd_triage 2020-04-27 16:14:04 UTC
(In reply to Mark Johnston from comment #2)
PR_IMPLOPCL is the flag that the unix socket protocols should be setting, not PR_CONNREQUIRED.  In any case, the documentation at least covers what is supposed to happen in this case.  Thanks for the patch.
Comment 5 commit-hook freebsd_committer freebsd_triage 2020-05-04 12:28:45 UTC
A commit references this bug:

Author: markj
Date: Mon May  4 12:27:46 UTC 2020
New revision: 360627
URL: https://svnweb.freebsd.org/changeset/base/360627

Log:
  MFC r360384:
  Document handling of connection-mode sockets by sendto(2).

  PR:	245817

Changes:
_U  stable/12/
  stable/12/lib/libc/sys/send.2
Comment 6 Mark Johnston freebsd_committer freebsd_triage 2020-05-04 12:29:15 UTC
Thanks for the report.