diff --git a/net/local/Kconfig b/net/local/Kconfig index e32c0b32e5..158282bbef 100644 --- a/net/local/Kconfig +++ b/net/local/Kconfig @@ -33,6 +33,12 @@ config NET_LOCAL_DGRAM ---help--- Enable support for Unix domain SOCK_DGRAM type sockets +config NET_LOCAL_SCM + bool "Unix domain socket control message" + default n + ---help--- + Enable support for Unix domain socket control message + endif # NET_LOCAL endmenu # Unix Domain Sockets diff --git a/net/local/local.h b/net/local/local.h index c746b51fc0..df0777d0bb 100644 --- a/net/local/local.h +++ b/net/local/local.h @@ -46,6 +46,7 @@ ****************************************************************************/ #define LOCAL_NPOLLWAITERS 2 +#define LOCAL_NCONTROLFDS 4 /* Packet format in FIFO: * @@ -136,15 +137,22 @@ struct local_conn_s /* Fields common to SOCK_STREAM and SOCK_DGRAM */ - uint8_t lc_crefs; /* Reference counts on this instance */ - uint8_t lc_proto; /* SOCK_STREAM or SOCK_DGRAM */ - uint8_t lc_type; /* See enum local_type_e */ - uint8_t lc_state; /* See enum local_state_e */ - struct file lc_infile; /* File for read-only FIFO (peers) */ - struct file lc_outfile; /* File descriptor of write-only FIFO (peers) */ - char lc_path[UNIX_PATH_MAX]; /* Path assigned by bind() */ - int32_t lc_instance_id; /* Connection instance ID for stream - * server<->client connection pair */ + uint8_t lc_crefs; /* Reference counts on this instance */ + uint8_t lc_proto; /* SOCK_STREAM or SOCK_DGRAM */ + uint8_t lc_type; /* See enum local_type_e */ + uint8_t lc_state; /* See enum local_state_e */ + struct file lc_infile; /* File for read-only FIFO (peers) */ + struct file lc_outfile; /* File descriptor of write-only FIFO (peers) */ + char lc_path[UNIX_PATH_MAX]; /* Path assigned by bind() */ + int32_t lc_instance_id; /* Connection instance ID for stream + * server<->client connection pair */ +#ifdef CONFIG_NET_LOCAL_SCM + FAR struct local_conn_s * + lc_peer; /* Peer connection instance */ + uint16_t lc_cfpcount; /* Control file pointer counter */ + FAR struct file * + lc_cfps[LOCAL_NCONTROLFDS]; /* Socket message control filep */ +#endif /* CONFIG_NET_LOCAL_SCM */ #ifdef CONFIG_NET_LOCAL_STREAM /* SOCK_STREAM fields common to both client and server */ diff --git a/net/local/local_accept.c b/net/local/local_accept.c index 4529dabb15..8d4bec5e8f 100644 --- a/net/local/local_accept.c +++ b/net/local/local_accept.c @@ -168,6 +168,9 @@ int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, conn->lc_type = LOCAL_TYPE_PATHNAME; conn->lc_state = LOCAL_STATE_CONNECTED; conn->lc_psock = psock; +#ifdef CONFIG_NET_LOCAL_SCM + conn->lc_peer = client; +#endif /* CONFIG_NET_LOCAL_SCM */ strncpy(conn->lc_path, client->lc_path, UNIX_PATH_MAX - 1); conn->lc_path[UNIX_PATH_MAX - 1] = '\0'; diff --git a/net/local/local_conn.c b/net/local/local_conn.c index 3b27400d78..c968cca9e0 100644 --- a/net/local/local_conn.c +++ b/net/local/local_conn.c @@ -161,12 +161,24 @@ FAR struct local_conn_s *local_alloc(void) void local_free(FAR struct local_conn_s *conn) { +#ifdef CONFIG_NET_LOCAL_SCM + int i; +#endif /* CONFIG_NET_LOCAL_SCM */ + DEBUGASSERT(conn != NULL); /* Remove the server from the list of listeners. */ net_lock(); dq_rem(&conn->lc_node, &g_local_connections); + +#ifdef CONFIG_NET_LOCAL_SCM + if (local_peerconn(conn) && conn->lc_peer) + { + conn->lc_peer->lc_peer = NULL; + } +#endif /* CONFIG_NET_LOCAL_SCM */ + net_unlock(); /* Make sure that the read-only FIFO is closed */ @@ -185,6 +197,20 @@ void local_free(FAR struct local_conn_s *conn) conn->lc_outfile.f_inode = NULL; } +#ifdef CONFIG_NET_LOCAL_SCM + /* Free the pending control file pointer */ + + for (i = 0; i < conn->lc_cfpcount; i++) + { + if (conn->lc_cfps[i]) + { + file_close(conn->lc_cfps[i]); + kmm_free(conn->lc_cfps[i]); + conn->lc_cfps[i] = NULL; + } + } +#endif /* CONFIG_NET_LOCAL_SCM */ + #ifdef CONFIG_NET_LOCAL_STREAM /* Destroy all FIFOs associted with the connection */ diff --git a/net/local/local_connect.c b/net/local/local_connect.c index b3aff310a1..1c3f407ae6 100644 --- a/net/local/local_connect.c +++ b/net/local/local_connect.c @@ -301,6 +301,9 @@ int psock_local_connect(FAR struct socket *psock, UNIX_PATH_MAX - 1); client->lc_path[UNIX_PATH_MAX - 1] = '\0'; client->lc_instance_id = local_generate_instance_id(); +#ifdef CONFIG_NET_LOCAL_SCM + client->lc_peer = conn; +#endif /* CONFIG_NET_LOCAL_SCM */ /* The client is now bound to an address */ diff --git a/net/local/local_recvmsg.c b/net/local/local_recvmsg.c index 6cc1237d68..9ef2287879 100644 --- a/net/local/local_recvmsg.c +++ b/net/local/local_recvmsg.c @@ -33,6 +33,8 @@ #include #include +#include +#include #include #include "socket/socket.h" @@ -105,6 +107,93 @@ static int psock_fifo_read(FAR struct socket *psock, FAR void *buf, return OK; } +/**************************************************************************** + * Name: local_recvctl + * + * Description: + * Handle the socket message conntrol field + * + * Input Parameters: + * conn Local connection instance + * msg Message to send + * + ****************************************************************************/ + +#ifdef CONFIG_NET_LOCAL_SCM +static void local_recvctl(FAR struct local_conn_s *conn, + FAR struct msghdr *msg) +{ + FAR struct local_conn_s *peer; + struct cmsghdr *cmsg; + int count; + int *fds; + int i; + + net_lock(); + + cmsg = CMSG_FIRSTHDR(msg); + count = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int); + cmsg->cmsg_len = 0; + + if (count == 0) + { + goto out; + } + + if (conn->lc_peer == NULL) + { + peer = local_peerconn(conn); + if (peer == NULL) + { + goto out; + } + } + else + { + peer = conn; + } + + if (peer->lc_cfpcount == 0) + { + goto out; + } + + fds = (int *)CMSG_DATA(cmsg); + + count = count > peer->lc_cfpcount ? + peer->lc_cfpcount : count; + for (i = 0; i < count; i++) + { + fds[i] = file_dup(peer->lc_cfps[i], 0); + file_close(peer->lc_cfps[i]); + kmm_free(peer->lc_cfps[i]); + peer->lc_cfps[i] = NULL; + peer->lc_cfpcount--; + if (fds[i] < 0) + { + i++; + break; + } + } + + if (i > 0) + { + if (peer->lc_cfpcount) + { + memmove(peer->lc_cfps[0], peer->lc_cfps[i], + sizeof(FAR void *) * peer->lc_cfpcount); + } + + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * i); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + } + +out: + net_unlock(); +} +#endif /* CONFIG_NET_LOCAL_SCM */ + /**************************************************************************** * Name: psock_stream_recvfrom * @@ -382,10 +471,10 @@ errout_with_halfduplex: ssize_t local_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, int flags) { + FAR socklen_t *fromlen = &msg->msg_namelen; + FAR struct sockaddr *from = msg->msg_name; FAR void *buf = msg->msg_iov->iov_base; size_t len = msg->msg_iov->iov_len; - FAR struct sockaddr *from = msg->msg_name; - FAR socklen_t *fromlen = &msg->msg_namelen; DEBUGASSERT(psock && psock->s_conn && buf); @@ -394,7 +483,7 @@ ssize_t local_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, #ifdef CONFIG_NET_LOCAL_STREAM if (psock->s_type == SOCK_STREAM) { - return psock_stream_recvfrom(psock, buf, len, flags, from, fromlen); + len = psock_stream_recvfrom(psock, buf, len, flags, from, fromlen); } else #endif @@ -402,15 +491,27 @@ ssize_t local_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, #ifdef CONFIG_NET_LOCAL_DGRAM if (psock->s_type == SOCK_DGRAM) { - return psock_dgram_recvfrom(psock, buf, len, flags, from, fromlen); + len = psock_dgram_recvfrom(psock, buf, len, flags, from, fromlen); } else #endif { DEBUGPANIC(); nerr("ERROR: Unrecognized socket type: %" PRIu8 "\n", psock->s_type); - return -EINVAL; + len = -EINVAL; } + +#ifdef CONFIG_NET_LOCAL_SCM + /* Receive the control message */ + + if (len >= 0 && msg->msg_control && + msg->msg_controllen > sizeof(struct cmsghdr)) + { + local_recvctl(psock->s_conn, msg); + } +#endif /* CONFIG_NET_LOCAL_SCM */ + + return len; } #endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/net/local/local_sendmsg.c b/net/local/local_sendmsg.c index dd50f40391..31c211415c 100644 --- a/net/local/local_sendmsg.c +++ b/net/local/local_sendmsg.c @@ -32,6 +32,7 @@ #include #include +#include #include #include "socket/socket.h" @@ -41,6 +42,105 @@ * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: local_sendctl + * + * Description: + * Handle the socket message conntrol field + * + * Input Parameters: + * conn Local connection instance + * msg Message to send + * + * Returned Value: + * On any failure, a negated errno value is returned + * + ****************************************************************************/ + +#ifdef CONFIG_NET_LOCAL_SCM +static int local_sendctl(FAR struct local_conn_s *conn, + FAR struct msghdr *msg) +{ + FAR struct local_conn_s *peer; + FAR struct file *filep2; + FAR struct file *filep; + struct cmsghdr *cmsg; + int count; + int *fds; + int ret; + int i; + + net_lock(); + + peer = conn->lc_peer; + if (peer == NULL) + { + peer = conn; + } + + for_each_cmsghdr(cmsg, msg) + { + if (!CMSG_OK(msg, cmsg) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) + { + ret = -EOPNOTSUPP; + goto fail; + } + + fds = (int *)CMSG_DATA(cmsg); + count = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int); + + if (count + peer->lc_cfpcount > LOCAL_NCONTROLFDS) + { + ret = -EMFILE; + goto fail; + } + + for (i = 0; i < count; i++) + { + ret = fs_getfilep(fds[i], &filep); + if (ret < 0) + { + goto fail; + } + + filep2 = (FAR struct file *)kmm_zalloc(sizeof(*filep2)); + if (!filep2) + { + ret = -ENOMEM; + goto fail; + } + + ret = file_dup2(filep, filep2); + if (ret < 0) + { + kmm_free(filep2); + goto fail; + } + + peer->lc_cfps[peer->lc_cfpcount++] = filep2; + } + } + + net_unlock(); + + return count; + +fail: + while (i-- > 0) + { + file_close(peer->lc_cfps[--peer->lc_cfpcount]); + kmm_free(peer->lc_cfps[peer->lc_cfpcount]); + peer->lc_cfps[peer->lc_cfpcount] = NULL; + } + + net_unlock(); + + return ret; +} +#endif /* CONFIG_NET_LOCAL_SCM */ + /**************************************************************************** * Name: local_send * @@ -284,13 +384,44 @@ errout_with_halfduplex: ssize_t local_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, int flags) { - FAR const struct iovec *buf = msg->msg_iov; - size_t len = msg->msg_iovlen; FAR const struct sockaddr *to = msg->msg_name; + FAR const struct iovec *buf = msg->msg_iov; socklen_t tolen = msg->msg_namelen; + size_t len = msg->msg_iovlen; +#ifdef CONFIG_NET_LOCAL_SCM + FAR struct local_conn_s *conn = psock->s_conn; + int count; - return to ? local_sendto(psock, buf, len, flags, to, tolen) : - local_send(psock, buf, len, flags); + if (msg->msg_control && + msg->msg_controllen > sizeof(struct cmsghdr)) + { + count = local_sendctl(conn, msg); + if (count < 0) + { + return count; + } + } +#endif /* CONFIG_NET_LOCAL_SCM */ + + len = to ? local_sendto(psock, buf, len, flags, to, tolen) : + local_send(psock, buf, len, flags); +#ifdef CONFIG_NET_LOCAL_SCM + if (len < 0 && count > 0) + { + net_lock(); + + while (count-- > 0) + { + file_close(conn->lc_cfps[--conn->lc_cfpcount]); + kmm_free(conn->lc_cfps[conn->lc_cfpcount]); + conn->lc_cfps[conn->lc_cfpcount] = NULL; + } + + net_unlock(); + } +#endif + + return len; } #endif /* CONFIG_NET && CONFIG_NET_LOCAL */