(Sorry if you are getting this message duplicated, I tried sending it to
tech@ before but it seems to get stuck somewhere along the way.)
I found this while looking at netcat and it seemed odd:
if (family == AF_UNIX && uflag) {
if (connect(s, NULL, 0) == -1)
err(1, "connect");
}
POSIX 2008 [0] says:
For SOCK_DGRAM sockets, [...] If the sa_family member of address is
AF_UNSPEC, the socket's peer address shall be reset.
It also says:
Issue 7
[...]
- Austin Group Interpretation 1003.1-2001 #188 is applied, changing
the method used to reset a peer address for a datagram socket.
[...]
Indeed, in POSIX 2004 [1] it said:
For SOCK_DGRAM sockets, [...] If address is a null address for the
protocol, the socket's peer address shall be reset.
Which is similar to connect(2):
Datagram sockets may dissolve the association by connecting to an
invalid address, such as a null address.
Now, I don't know if "null address for the protocol" was at any point in
time supposed to be interpreted as NULL-pointer/zero-size as opposed to
zeroed structure for example (notice that AF_UNSPEC is 0) or whether
such a thing ever worked in OpenBSD, but it certainly doesn't now and
hasn't for a while: it fails in sys_connect -> sockargs when checking
for the sockaddr size.
OpenBSD does support disassociating via a zeroed sockaddr, but the
syscall fails and it shouldn't; in fact, it supports disassociating by
using *any* sort of address as long as it's consistent in terms of size
because the disassociation (sodisconnect) happens before even looking at
the user-supplied sockaddr. I believe this should to be cleaned up but
it is a change in syscall semantics (according to CVS this code,
soconnect, mostly comes directly from NetBSD from 23 years ago).
I did not look at what other BSD do, but below is a possible minimal
patch that aims to retain most of the current behaviour, which might or
might not be TRTTD. It should apply to "current".
Cheers.
0: https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html
1: https://pubs.opengroup.org/onlinepubs/009695399/functions/connect.html
--- sys/kern/uipc_socket.c 2019-08-09 17:27:45 +0000
+++ sys/kern/uipc_socket.c 2019-08-10 17:41:07 +0000
@@ -66,6 +66,8 @@ void soreaper(void *);
void soput(void *);
int somove(struct socket *, int);
+int nam2sa(struct mbuf *, struct sockaddr **);
+
void filt_sordetach(struct knote *kn);
int filt_soread(struct knote *kn, long hint);
void filt_sowdetach(struct knote *kn);
@@ -344,7 +346,8 @@ soaccept(struct socket *so, struct mbuf
int
soconnect(struct socket *so, struct mbuf *nam)
{
- int error;
+ struct sockaddr *sa;
+ int error = 0;
soassertlocked(so);
@@ -353,14 +356,16 @@ soconnect(struct socket *so, struct mbuf
/*
* If protocol is connection-based, can only connect once.
* Otherwise, if connected, try to disconnect first.
- * This allows user to disconnect by connecting to, e.g.,
- * a null address.
+ * This allows the user to disconnect by connecting to an invalid
+ * address, although only a "zero" address (AF_UNSPEC) will not
+ * result in error.
*/
if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
(error = sodisconnect(so))))
error = EISCONN;
- else
+ else if (!(so->so_type == SOCK_DGRAM && nam2sa(nam, &sa) == 0 &&
+ sa->sa_family == AF_UNSPEC))
error = (*so->so_proto->pr_usrreq)(so, PRU_CONNECT,
NULL, nam, NULL, curproc);
return (error);
@@ -1965,6 +1970,19 @@ sohasoutofband(struct socket *so)
}
int
+nam2sa(struct mbuf *nam, struct sockaddr **soa)
+{
+ struct sockaddr *sa = mtod(nam, struct sockaddr *);
+
+ if (nam->m_len < offsetof(struct sockaddr, sa_data))
+ return EINVAL;
+ if (sa->sa_len != nam->m_len)
+ return EINVAL;
+ *soa = sa;
+ return 0;
+}
+
+int
soo_kqfilter(struct file *fp, struct knote *kn)
{
struct socket *so = kn->kn_fp->f_data;
--- lib/libc/sys/connect.2 2019-08-10 17:34:42 +0000
+++ lib/libc/sys/connect.2 2019-08-10 17:38:22 +0000
@@ -73,7 +73,7 @@ only once; datagram sockets may use
.Fn connect
multiple times to change their association.
Datagram sockets may dissolve the association
-by connecting to an invalid address, such as a null address.
+by connecting to an address of family AF_UNSPEC.
.Pp
If the socket is in non-blocking mode and the connection cannot be
completed immediately, or if it is interrupted by a signal,
--- usr.bin/nc/netcat.c 2019-08-09 17:30:13 +0000
+++ usr.bin/nc/netcat.c 2019-08-10 17:47:16 +0000
@@ -629,7 +629,9 @@ main(int argc, char *argv[])
tls_free(tls_cctx);
}
if (family == AF_UNIX && uflag) {
- if (connect(s, NULL, 0) == -1)
+ /* AF_UNSPEC == 0 */
+ if (connect(s, &(struct sockaddr){0},
+ sizeof(struct sockaddr)) == -1)
err(1, "connect");
}