Unix sockets: Fleshes out the recvfrom() logic

This commit is contained in:
Gregory Nutt 2015-01-27 11:51:24 -06:00
parent 120efa4717
commit 8f29039f42
6 changed files with 527 additions and 155 deletions

View File

@ -39,7 +39,7 @@ ifeq ($(CONFIG_NET_LOCAL),y)
NET_CSRCS += local_conn.c local_connect.c local_release.c local_bind.c
NET_CSRCS += local_listen.c local_accept.c local_fifo.c local_recvfrom.c
NET_CSRCS += local_send.c local_sendto.c local_utils.c
NET_CSRCS += local_send.c local_sendto.c local_sendpacket.c local_recvutils.c
# Include UDP build support

View File

@ -60,11 +60,10 @@
* 2. End/Start byte
* 3. 16-bit packet length (in host order)
* 4. Packet data (in host order)
* 5. 16-bit checksum (in host order, includes packet length)
*/
#define LOCAL_SYNC_BYTE 0x42 /* Byte in sync sequence */
#define LOCAL_END_BYTE 0xbd /* End of sync seqence */
#define LOCAL_SYNC_BYTE 0x42 /* Byte in sync sequence */
#define LOCAL_END_BYTE 0xbd /* End of sync seqence */
/****************************************************************************
* Public Type Definitions
@ -140,7 +139,9 @@ struct local_conn_s
sem_t lc_waitsem; /* Use to wait for a connection to be accepted */
/* Union of fields unique to SOCK_STREAM client and servers */
/* Union of fields unique to SOCK_STREAM client, server, and connected
* peers.
*/
union
{
@ -157,8 +158,16 @@ struct local_conn_s
struct
{
volatile int lc_result; /* Result of the connection operation */
uint16_t lc_remaining; /* (For binary compatibility with peer) */
volatile int lc_result; /* Result of the connection operation (client)*/
} client;
/* Fields common to connected peers (connected or accepted) */
struct
{
uint16_t lc_remaining; /* Bytes remaining in the incoming stream */
} peer;
} u;
};
@ -361,6 +370,25 @@ ssize_t psock_local_sendto(FAR struct socket *psock, FAR const void *buf,
size_t len, int flags, FAR const struct sockaddr *to,
socklen_t tolen);
/****************************************************************************
* Name: local_send_packet
*
* Description:
* Send a packet on the write-only FIFO.
*
* Parameters:
* fd File descriptor of write-only FIFO.
* buf Data to send
* len Length of data to send
*
* Return:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int local_send_packet(int fd, FAR const uint8_t *buf, size_t len);
/****************************************************************************
* Function: psock_recvfrom
*
@ -393,6 +421,65 @@ ssize_t psock_local_recvfrom(FAR struct socket *psock, FAR void *buf,
size_t len, int flags, FAR struct sockaddr *from,
FAR socklen_t *fromlen);
/****************************************************************************
* Name: local_fifo_read
*
* Description:
* Read a data from the read-only FIFO.
*
* Parameters:
* fd - File descriptor of read-only FIFO.
* buf - Local to store the received data
* len - Length of data to receive [in]
* Length of data actually received [out]
*
* Return:
* Zero is returned on success; a negated errno value is returned on any
* failure. If -ECONNRESET is received, then the sending side has closed
* the FIFO. In this case, the returned data may still be valid (if the
* returned len > 0).
*
****************************************************************************/
int local_fifo_read(int fd, FAR uint8_t *buf, size_t *len);
/****************************************************************************
* Name: local_getaddr
*
* Description:
* Return the Unix domain address of a connection.
*
* Parameters:
* conn - The connection
* addr - The location to return the address
* addrlen - The size of the memory allocat by the caller to receive the
* address.
*
* Return:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int local_getaddr(FAR struct local_conn_s *conn, FAR struct sockaddr *addr,
FAR socklen_t *addrlen);
/****************************************************************************
* Name: local_sync
*
* Description:
* Read a sync bytes until the start of the packet is found.
*
* Parameters:
* fd - File descriptor of read-only FIFO.
*
* Return:
* The non-zero size of the following packet is returned on success; a
* negated errno value is returned on any failure.
*
****************************************************************************/
int local_sync(int fd);
/****************************************************************************
* Name: local_create_fifos
*
@ -453,61 +540,6 @@ int local_open_server_rx(FAR struct local_conn_s *server);
int local_open_server_tx(FAR struct local_conn_s *server);
/****************************************************************************
* Name: local_chksum
*
* Description:
* Compute a simple checksum over the packet data
*
* Parameters:
* buf Data to send
* len Length of data to send
*
* Return:
* The 16-bit checksum (including the length)
*
****************************************************************************/
uint16_t local_chksum(FAR const uint8_t *buf, size_t len);
/****************************************************************************
* Name: local_write
*
* Description:
* Write a data on the write-only FIFO.
*
* Parameters:
* fd File descriptor or write-only FIFO.
* buf Data to send
* len Length of data to send
*
* Return:
* On success, returns the number of characters sent. On error, a
* negated errno value is returned.
*
****************************************************************************/
int local_write(int fd, FAR const uint8_t *buf, size_t len);
/****************************************************************************
* Name: local_send_packet
*
* Description:
* Write a packet on the write-only FIFO.
*
* Parameters:
* fd File descriptor or write-only FIFO.
* buf Data to send
* len Length of data to send
*
* Return:
* Zero is returned on success; a negated errno value is returned on any
* failure.
*
****************************************************************************/
int local_send_packet(int fd, FAR const uint8_t *buf, size_t len);
#undef EXTERN
#ifdef __cplusplus
}

View File

@ -180,46 +180,12 @@ int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr,
if (addr)
{
FAR struct sockaddr_un *unaddr;
int totlen;
int pathlen;
/* If an address is provided, then the length must also be
* provided.
*/
DEBUGASSERT(addrlen);
/* Get the length of the path (minus the NUL terminator)
* and the length of the whole client address.
*/
pathlen = strnlen(client->lc_path, UNIX_PATH_MAX-1);
totlen = sizeof(sa_family_t) + pathlen + 1;
/* If the length of the whole client address is larger
* than the buffer provided by the caller, then truncate
* the address to fit.
*/
if (totlen > *addrlen)
{
pathlen -= (totlen - *addrlen);
totlen = *addrlen;
}
/* Copy the Unix domain address */
unaddr = (FAR struct sockaddr_un *)addr;
unaddr->sun_family = AF_LOCAL;
memcpy(unaddr->sun_path, client->lc_path, pathlen);
unaddr->sun_path[pathlen] = '\0';
/* Return the Unix domain address size */
*addrlen = totlen;
ret = local_getaddr(client, addr, addrlen);
}
}
if (ret == OK)
{
/* Return the client connection structure */
*newconn = (FAR void *)conn;

View File

@ -43,11 +43,80 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/net/net.h>
#include "socket/socket.h"
#include "local/local.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef MIN
# define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: psock_fifo_read
*
* Description:
* A thin layer aroudn local_fifo_read that handles socket-related loss-of-
* connection events.
*
****************************************************************************/
static int psock_fifo_read(FAR struct socket *psock, FAR void *buf,
FAR size_t *readlen)
{
FAR struct local_conn_s *conn = (FAR struct local_conn_s *)psock->s_conn;
int ret;
ret = local_fifo_read(conn->lc_infd, buf, readlen);
if (ret < 0)
{
/* -ECONNRESET is a special case. We may or not have received
* data, then the peer closed the connection.
*/
if (ret == -ECONNRESET)
{
ndbg("ERROR: Lost connection: %d\n", ret);
/* Report an ungraceful loss of connection. This should
* eventually be reported as ENOTCONN.
*/
psock->s_flags &= ~(_SF_CONNECTED |_SF_CLOSED);
conn->lc_state = LOCAL_STATE_DISCONNECTED;
/* Did we receive any data? */
if (*readlen <= 0)
{
/* No.. return the ECONNRESET error now. Otherwise,
* process the received data and return ENOTCONN the
* next time that psock_recvfrom() is calle.
*/
return ret;
}
}
else
{
ndbg("ERROR: Failed to read packet: %d\n", ret);
return ret;
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
@ -76,7 +145,7 @@
* On success, returns the number of characters sent. If no data is
* available to be received and the peer has performed an orderly shutdown,
* recv() will return 0. Otherwise, on errors, -1 is returned, and errno
* is set appropriately (see receivefrom for the complete list).
* is set appropriately (see receive from for the complete list).
*
****************************************************************************/
@ -84,8 +153,104 @@ ssize_t psock_local_recvfrom(FAR struct socket *psock, FAR void *buf,
size_t len, int flags, FAR struct sockaddr *from,
FAR socklen_t *fromlen)
{
#warning Missing logic
return -ENOSYS;
FAR struct local_conn_s *conn;
size_t readlen;
int ret;
DEBUGASSERT(psock && psock->s_conn && buf);
conn = (FAR struct local_conn_s *)psock->s_conn;
/* Verify that this is a connected peer socket and that it has opened the
* incoming FIFO for read-only access.
*/
if (conn->lc_type != LOCAL_STATE_CONNECTED ||
conn->lc_infd < 0)
{
ndbg("ERROR: not connected\n");
return -ENOTCONN;
}
/* Are there still bytes in the FIFO from the last packet? */
if (conn->u.peer.lc_remaining == 0)
{
/* No.. Sync to the start of the next packet in the stream and get
* the size of the next packet.
*/
ret = local_sync(conn->lc_infd);
if (ret < 0)
{
ndbg("ERROR: Failed to get packet length: %d\n", ret);
return ret;
}
else if (ret > 0xffff)
{
ndbg("ERROR: Packet is too big: %d\n", ret);
return -E2BIG;
}
conn->u.peer.lc_remaining = (uint16_t)ret;
}
/* Read the packet */
/* REVISIT: Does this make sense if the socket is SOCK_DGRAM? */
readlen = MIN(conn->u.peer.lc_remaining, len);
ret = psock_fifo_read(psock, buf, &readlen);
if (ret < 0)
{
return ret;
}
/* Adjust the number of bytes remaining to be read from the packet */
DEBUGASSERT(readlen <= conn->u.peer.lc_remaining);
conn->u.peer.lc_remaining -= readlen;
/* If this is a SOCK_STREAM socket and there are unread bytes remaining
* in the packet, we will get those bytes the next time recv is called.
* What if this is a SOCK_DRAM? REVISIT: Here we flush the remainder of
* the packet to the bit bucket.
*/
if (psock->s_type == SOCK_DGRAM && conn->u.peer.lc_remaining > 0)
{
uint8_t bitbucket[32];
size_t tmplen;
do
{
/* Read 32 bytes into the bit bucket */
tmplen = MIN(conn->u.peer.lc_remaining, 32);
ret = psock_fifo_read(psock, bitbucket, &tmplen);
if (ret < 0)
{
return ret;
}
/* Adjust the number of bytes remaining to be read from the packet */
DEBUGASSERT(tmplen <= conn->u.peer.lc_remaining);
conn->u.peer.lc_remaining -= tmplen;
}
while (conn->u.peer.lc_remaining > 0);
}
/* Return the address family */
if (from)
{
ret = local_getaddr(conn, from, fromlen);
if (ret < 0)
{
return ret;
}
}
return readlen;
}
#endif /* CONFIG_NET && CONFIG_NET_LOCAL */

248
net/local/local_recvutils.c Normal file
View File

@ -0,0 +1,248 @@
/****************************************************************************
* net/local/local_recvpacket.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL)
#include <sys/types.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include "local/local.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: local_fifo_read
*
* Description:
* Read a data from the read-only FIFO.
*
* Parameters:
* fd - File descriptor of read-only FIFO.
* buf - Local to store the received data
* len - Length of data to receive [in]
* Length of data actually received [out]
*
* Return:
* Zero is returned on success; a negated errno value is returned on any
* failure. If -ECONNRESET is received, then the sending side has closed
* the FIFO. In this case, the returned data may still be valid (if the
* returned len > 0).
*
****************************************************************************/
int local_fifo_read(int fd, FAR uint8_t *buf, size_t *len)
{
ssize_t total;
ssize_t nread;
int ret;
DEBUGASSERT(buf && len);
total = 0;
while (len > 0)
{
nread = read(fd, buf, *len);
if (nread < 0)
{
int errcode = errno;
DEBUGASSERT(errcode > 0);
if (errcode != EINTR)
{
ndbg("ERROR: Read failed: %d\n", errcode);
ret = -errcode;
goto errout;
}
nvdbg("Ignoring signal\n");
}
else if (nread == 0)
{
/* The FIFO returns zero if the sending side of the connection
* has closed the FIFO.
*/
ret = -ECONNRESET;
goto errout;
}
else
{
DEBUGASSERT(nread <= len);
len -= nread;
buf += nread;
}
}
ret = OK;
errout:
*len = total;
return ret;
}
/****************************************************************************
* Name: local_sync
*
* Description:
* Read a sync bytes until the start of the packet is found.
*
* Parameters:
* fd - File descriptor of read-only FIFO.
*
* Return:
* The non-zero size of the following packet is returned on success; a
* negated errno value is returned on any failure.
*
****************************************************************************/
int local_sync(int fd)
{
size_t readlen;
uint16_t pktlen;
uint8_t sync;
int ret;
/* Loop until a valid pre-amble is encountered: SYNC bytes followed
* by one END byte.
*/
do
{
/* Read until we encounter a sync byte */
do
{
readlen = sizeof(uint8_t);
ret = local_fifo_read(fd, &sync, &readlen);
if (ret < 0)
{
ndbg("ERROR: Failed to read sync bytes: %d\n", ret);
return ret;
}
}
while (sync != LOCAL_SYNC_BYTE);
/* Then read to the end of the SYNC sequence */
do
{
readlen = sizeof(uint8_t);
ret = local_fifo_read(fd, &sync, &readlen);
if (ret < 0)
{
ndbg("ERROR: Failed to read sync bytes: %d\n", ret);
return ret;
}
}
while (sync == LOCAL_SYNC_BYTE);
}
while (sync != LOCAL_END_BYTE);
/* Then read the packet length */
readlen = sizeof(uint16_t);
ret = local_fifo_read(fd, (FAR uint8_t *)&pktlen, &readlen);
return ret < 0 ? ret : pktlen;
}
/****************************************************************************
* Name: local_getaddr
*
* Description:
* Return the Unix domain address of a connection.
*
* Parameters:
* conn - The connection
* addr - The location to return the address
* addrlen - The size of the memory allocated by the caller to receive the
* address.
*
* Return:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int local_getaddr(FAR struct local_conn_s *conn, FAR struct sockaddr *addr,
FAR socklen_t *addrlen)
{
FAR struct sockaddr_un *unaddr;
int totlen;
int pathlen;
DEBUGASSERT(conn && addr && addrlen && *addrlen >= sizeof(sa_family_t));
/* Get the length of the path (minus the NUL terminator) and the length
* of the whole Unix domain address.
*/
pathlen = strnlen(conn->lc_path, UNIX_PATH_MAX-1);
totlen = sizeof(sa_family_t) + pathlen + 1;
/* If the length of the whole Unix domain address is larger than the
* buffer provided by the caller, then truncate the address to fit.
*/
if (totlen > *addrlen)
{
pathlen -= (totlen - *addrlen);
totlen = *addrlen;
}
/* Copy the Unix domain address */
unaddr = (FAR struct sockaddr_un *)addr;
unaddr->sun_family = AF_LOCAL;
memcpy(unaddr->sun_path, conn->lc_path, pathlen);
unaddr->sun_path[pathlen] = '\0';
/* Return the Unix domain address size */
*addrlen = totlen;
return OK;
}
#endif /* CONFIG_NET && CONFIG_NET_LOCAL */

View File

@ -1,5 +1,5 @@
/****************************************************************************
* net/local/local_utils.c
* net/local/local_sendpacket.c
*
* Copyright (C) 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
@ -66,48 +66,17 @@ static const uint8_t g_preamble[LOCAL_PREAMBLE_SIZE] =
};
/****************************************************************************
* Public Functions
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: local_chksum
*
* Description:
* Compute a simple checksum over the packet data
*
* Parameters:
* buf Data to send
* len Length of data to send
*
* Return:
* The 16-bit checksum (including the length)
*
****************************************************************************/
uint16_t local_chksum(FAR const uint8_t *buf, size_t len)
{
uint16_t chksum;
int i;
DEBUGASSERT(buf && len <= 0xffff);
chksum = (len & 0xff) + ((len >> 8) & 0xff);
for (i = 0; i < len; i++)
{
chksum += *buf++;
}
return chksum;
}
/****************************************************************************
* Name: local_write
* Name: local_fifo_write
*
* Description:
* Write a data on the write-only FIFO.
*
* Parameters:
* fd File descriptor or write-only FIFO.
* fd File descriptor of write-only FIFO.
* buf Data to send
* len Length of data to send
*
@ -117,7 +86,7 @@ uint16_t local_chksum(FAR const uint8_t *buf, size_t len)
*
****************************************************************************/
int local_write(int fd, FAR const uint8_t *buf, size_t len)
static int local_fifo_write(int fd, FAR const uint8_t *buf, size_t len)
{
ssize_t nwritten;
@ -148,14 +117,18 @@ int local_write(int fd, FAR const uint8_t *buf, size_t len)
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: local_send_packet
*
* Description:
* Write a packet on the write-only FIFO.
* Send a packet on the write-only FIFO.
*
* Parameters:
* fd File descriptor or write-only FIFO.
* fd File descriptor of write-only FIFO.
* buf Data to send
* len Length of data to send
*
@ -168,34 +141,22 @@ int local_write(int fd, FAR const uint8_t *buf, size_t len)
int local_send_packet(int fd, FAR const uint8_t *buf, size_t len)
{
uint16_t len16;
uint16_t chksum;
int ret;
/* Compute the checksum over the packet data (including the length) */
chksum = local_chksum(buf, len);
/* Send the packet preamble */
ret = local_write(fd, g_preamble, LOCAL_PREAMBLE_SIZE);
ret = local_fifo_write(fd, g_preamble, LOCAL_PREAMBLE_SIZE);
if (ret == OK)
{
/* Send the packet length */
len16 = len;
ret = local_write(fd, (FAR const uint8_t *)&len16, sizeof(uint16_t));
ret = local_fifo_write(fd, (FAR const uint8_t *)&len16, sizeof(uint16_t));
if(ret == OK)
{
/* Send the packet data */
ret = local_write(fd, buf, len);
if (ret == OK)
{
/* Send the checksum */
ret = local_write(fd, (FAR const uint8_t *)&chksum,
sizeof(uint16_t));
}
ret = local_fifo_write(fd, buf, len);
}
}