Files
m241Neelima/components/ip_net2-6.9/iplite2/src/iplite_sock.c
T
2026-07-30 11:19:44 +05:30

3053 lines
96 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: iplite_sock.c,v $ $Revision: 1.120.10.5 $
* $Source: /home/interpeak/CVSRoot/iplite2/src/iplite_sock.c,v $
* $Author: hxie $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
* Functions for socket API.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#define IPCOM_SKIP_NATIVE_SOCK_API
#ifndef IPCOM_KERNEL
#define IPCOM_KERNEL
#endif
#include "iplite_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#if !defined(IPLITE_ONE_FILE) || defined(IPLITE_C)
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_err.h>
#include <ipcom_clib.h>
#include <ipcom_cstyle.h>
#include <ipcom_inet.h>
#include "iplite_h.h"
#include "iplite_loopback.h"
#include "iplite_sock.h"
#ifdef IPCOM_USE_TCP
#include "iplite_tcp.h"
#endif
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
#ifdef IP_PORT_OSE5_disabled_code
Ip_err ipcom_crt_block_wait(Ipcom_socket *sock, Ip_s32 milli_seconds, Ip_bool snd);
void ipcom_crt_block_post(Ipcom_socket *sock, Ip_bool snd);
#endif
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
IP_STATIC Ipcom_pkt *iplite_krecvfrom(Iplite_socket *sock, struct Ip_sockaddr *from, Ip_socklen_t *fromlen);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
/*
****************************************************************************
* 9 STATIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* iplite_bind_check_reuse
*===========================================================================
* Description: Checks if the source port is reused and if it is, whenever
* the reuse is ok or not.
* Parameters: sock - The socket for which to check port reuse.
* Returns: 0 = success, <0 = error code.
*
*/
IP_STATIC int
iplite_bind_check_reuse(Iplite_socket *sock)
{
/* Check if there is a socket bound to same local port&addr -> EADDRINUSE
* (Consider socket option SO_REUSEADDR and SO_REUSEPORT).
*/
Iplite_socket *cachesock = IP_NULL;
int reuse, last_sock = 0;
for (;;)
{
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
cachesock = iplite_ip4_socklookup(sock->proto,
IP_NULL, sock->sport,
IP_NULL, 0,
&last_sock);
#ifdef IPCOM_USE_INET6
if (cachesock && cachesock->ipcom.domain == IP_AF_INET6)
cachesock = IP_NULL;
#endif
}
#endif
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
cachesock = iplite_ip6_socklookup(sock->proto,
IP_NULL, sock->sport,
IP_NULL, 0,
&last_sock);
#endif
if (cachesock == IP_NULL)
break;
reuse = (sock->flags & cachesock->flags)
& (IPLITE_SOCKET_FLAG_REUSEPORT | IPLITE_SOCKET_FLAG_REUSEADDR);
/* Both sockets must have at least one of the two reuse flags set. */
if (reuse == 0)
return -IP_ERRNO_EADDRINUSE;
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
if (sock->d->ip4.saddr_n != IP_INADDR_ANY
&& iplite_ip4_get_addr_type(IP_NULL, sock->d->ip4.saddr_n) != IPLITE_ADDR_TYPE_UNICAST)
return 0;
if (sock->d->ip4.saddr_n != cachesock->d->ip4.saddr_n)
continue;
}
#endif
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
{
if (IP_IN6_IS_ADDR_MULTICAST(&sock->d->ip6.saddr))
return 0;
if (!IP_IN6_ARE_ADDR_EQUAL(&sock->d->ip6.saddr, &cachesock->d->ip6.saddr))
continue;
}
#endif
#ifdef IPCOM_USE_TCP
if (sock->tcb != IP_NULL
&& IP_BIT_ISSET(cachesock->flags, IPLITE_SOCKET_FLAG_CONNECTED))
return 0;
#endif
if (IP_BIT_ISFALSE(reuse, IPLITE_SOCKET_FLAG_REUSEPORT)
#ifdef IPCOM_USE_TCP
|| sock->tcb != IP_NULL
#endif
)
return -IP_ERRNO_EADDRINUSE;
}
/* bind ok. */
return 0;
}
/*
*===========================================================================
* iplite_do_shutdown
*===========================================================================
* Description: The actual ipcom_shutdown() implementation
* Parameters: fd - The file descriptor to shut down.
* how - How the socket should be shut down.
* Returns: 0 = success, -1 = error
*/
IP_STATIC int
iplite_do_shutdown(Ip_fd fd, int how)
{
Iplite_socket *sock;
int ret;
int ret2;
int so_errno = -1; /* Close event */
ip_assert2(fd >= IPLITE_SOCKET_STARTFD);
ip_assert2(iplite.sockets[fd] != IP_NULL);
sock = iplite.sockets[fd];
if (sock == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EBADF);
/* Set socket flags. */
switch (how)
{
case IP_SHUT_RDWR:
ret = iplite_do_shutdown(fd, IP_SHUT_RD);
ret2 = iplite_do_shutdown(fd, IP_SHUT_WR);
return ret == 0 ? ret2 : ret;
case IP_SHUT_RD:
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED))
SOCK_ERROUT(IP_ERRNO_ESHUTDOWN);
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED);
break;
case IP_SHUT_WR:
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED))
SOCK_ERROUT(IP_ERRNO_ESHUTDOWN);
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED);
if (sock->snd_poll_list.size)
so_errno = IP_ERRNO_EPIPE;
break;
default:
SOCK_ERROUT(IP_ERRNO_EINVAL);
}
#ifdef IPCOM_USE_TCP
if (sock->ipcom.type == IP_SOCK_STREAM)
iplite_tcp_shutdown(sock, how);
#endif
iplite_sock_data_avail(sock, so_errno, how);
return 0;
errout:
return IP_SOCKERR;
}
/*
*===========================================================================
* iplite_sock_is_readable
*===========================================================================
* Description: Returns if a socket is readable or not
* Parameters: sock - socket to check for readability.
* Returns: IP_TRUE - socket is readable
* IP_FALSE - socket is not readable
*
*/
IP_STATIC Ip_bool
iplite_sock_is_readable(Iplite_socket *sock)
{
if (sock->ipcom.so_errno)
return IP_TRUE;
if (sock->ipcom.rcv_bytes > 0)
return IP_TRUE;
#ifdef IPCOM_USE_TCP
if (sock->tcb)
{
Iplite_tcp_tcb *tcb = sock->tcb;
if (tcb->state == IPLITE_TCPS_LISTEN)
{
if (IP_BIT_ISSET(tcb->flags, IPLITE_TCPC_FLAG_CONNABORTED))
return IP_TRUE;
if (tcb->backlog_done[0] != IP_SOCKERR)
return IP_TRUE;
}
}
#endif /* IPCOM_USE_TCP */
return (IP_BIT_ISSET(sock->flags,
IPLITE_SOCKET_FLAG_READ_CLOSED
| IPLITE_SOCKET_FLAG_PEER_WRITE_CLOSED));
}
/*
*===========================================================================
* iplite_sock_is_writable
*===========================================================================
* Description: Returns if a socket is writable or not
* Parameters: sock - socket to check for writability.
* Returns: IP_TRUE - socket is writable
* IP_FALSE - socket is not writable
*
*/
IP_STATIC Ip_bool
iplite_sock_is_writable(Iplite_socket *sock)
{
#ifdef IPCOM_USE_TCP
if (sock->tcb)
{
Iplite_tcp_tcb *tcb = sock->tcb;
if (sock->ipcom.so_errno)
return IP_TRUE;
return (tcb->state >= IPLITE_TCPS_ESTABLISHED
&& sock->snd_bytes < sock->snd_max_bytes);
}
#endif /* IPCOM_USE_TCP */
return IP_TRUE;
}
/*
*===========================================================================
* iplite_sock_select_wait
*===========================================================================
* Description: Waits for sockets in a select to become ready.
* Parameters: sem - a valid semaphore with timeout or IP_NULL if the
* sockets should just be checked.
* timeout - maximum number of milliseconds to wait for
* sockets to become ready.
* Returns: 0
*
*/
IP_STATIC int
iplite_sock_select_wait(Ipcom_block sem, Ip_s32 msec)
{
if (sem != IPCOM_BLOCK_INVALID)
{
IPLITE_CODE_UNLOCK();
(void)ipcom_block_wait(sem, msec);
IPLITE_CODE_LOCK();
}
return 0;
}
/*
*===========================================================================
* iplite_sock_wr_select
*===========================================================================
* Description: Performs write select for sockets.
* Parameters: fd - first socket to consider
* nfds - maximum number of socket to consider
* writefds - set of sockets to check for write, will contain
* all sockets that are writable on return.
* sem - a valid semaphore with timeout or IP_NULL if the
* sockets should just be checked.
* timeout - maximum number of milliseconds to wait for
* sockets to become ready.
* Returns: <0 = error code
* ==0 = timeout
* >0 = number of ready sockets
*
*/
IP_STATIC int
iplite_sock_wr_select(int fd,
int nfds,
Ip_fd_set *writefds,
Ipcom_block *sem,
Ip_s32 msec)
{
int ready_socks;
Iplite_socket *sock;
for (;;fd++)
{
if (writefds == IP_NULL || fd == nfds)
return iplite_sock_select_wait(sem, msec);
if (IP_FD_ISSET(fd, writefds))
{
Iplite_poll_entry poll;
sock = iplite.sockets[fd];
if (sock == IP_NULL)
return -IP_ERRNO_EBADF;
if (iplite_sock_is_writable(sock))
/* Change operation to just calculate the number of
ready sockets */
sem = IP_NULL;
poll.sem = sem;
ipcom_list_insert_last(&iplite.sockets[fd]->snd_poll_list, &poll.list);
ready_socks = iplite_sock_wr_select(fd + 1, nfds, writefds, sem, msec);
ipcom_list_remove(&poll.list);
break;
}
}
if (ready_socks >= 0 && iplite_sock_is_writable(sock))
return ready_socks + 1;
IP_FD_CLR(fd, writefds);
return ready_socks;
}
/*
*===========================================================================
* iplite_sock_select
*===========================================================================
* Description: Performs read and write select for sockets.
* Parameters: fd - first socket to consider
* nfds - maximum number of socket to consider
* readfds - set of sockets to check for read, will contain
* all sockets that are readable on return.
* writefds - set of sockets to check for write, will contain
* all sockets that are writable on return.
* sem - a valid semaphore with timeout or IP_NULL if the
* sockets should just be checked.
* timeout - maximum number of milliseconds to wait for
* sockets to become ready.
* Returns: <0 = error code
* ==0 = timeout
* >0 = number of ready sockets
*
*/
IP_STATIC int
iplite_sock_select(int fd,
int nfds,
Ip_fd_set *readfds,
Ip_fd_set *writefds,
Ipcom_block *sem,
Ip_s32 msec)
{
int ready_socks;
Iplite_socket *sock;
for (;;fd++)
{
if (readfds == IP_NULL || fd == nfds)
return iplite_sock_wr_select(IPLITE_SOCKET_STARTFD,
nfds,
writefds,
sem,
msec);
if (IP_FD_ISSET(fd, readfds))
{
Iplite_poll_entry poll;
sock = iplite.sockets[fd];
if (sock == IP_NULL)
return -IP_ERRNO_EBADF;
if (iplite_sock_is_readable(sock))
/* Change operation to just calculate the number of
ready sockets */
sem = IP_NULL;
poll.sem = sem;
ipcom_list_insert_last(&sock->rcv_poll_list, &poll.list);
ready_socks = iplite_sock_select(fd + 1,
nfds,
readfds,
writefds,
sem,
msec);
ipcom_list_remove(&poll.list);
break;
}
}
if (ready_socks >= 0 && iplite_sock_is_readable(sock))
return ready_socks + 1;
IP_FD_CLR(fd, readfds);
return ready_socks;
}
/*
*===========================================================================
* iplite_sock_wakeup_poll_list
*===========================================================================
* Description: Wakes all processes polling for a socket
* Parameters: head - head of poll list
* Returns:
*
*/
IP_STATIC void
iplite_sock_wakeup_poll_list(Ipcom_list *head)
{
Iplite_poll_entry *poll_entry;
for (poll_entry = IPCOM_LIST_FIRST(head);
poll_entry != IP_NULL;
poll_entry = IPCOM_LIST_NEXT(&poll_entry->list))
ipcom_block_post(poll_entry->sem);
}
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* iplite_get_sock
*===========================================================================
* Description: Returns the socket descriptor that matches the
* specified file descriptor. The caller must
* call ipnet_release_sock() when done using the
* socket.
* Parameters: fd - a file descriptor.
* Returns: The matching socket descriptor or IP_NULL if no
* matching socket exists.
*
*/
IP_GLOBAL Iplite_socket *
iplite_get_sock(int fd)
{
Iplite_socket *sock;
IPLITE_CODE_LOCK();
sock = iplite.sockets[fd];
if (sock == IP_NULL)
{
IPLITE_CODE_UNLOCK();
return IP_NULL;
}
return sock;
}
/*
*===========================================================================
* iplite_release_sock
*===========================================================================
* Description:
* Parameters: sock - socket to release.
* Returns:
*
*/
IP_GLOBAL void
iplite_release_sock(Iplite_socket *sock)
{
IPCOM_UNUSED_ARG(sock);
IPLITE_CODE_UNLOCK();
}
/*
*===========================================================================
* iplite_is_sock_readable
*===========================================================================
* Description: Checks if the specified socket is readable.
* Parameters: sock - the socket to check.
* lowat - minimum number of bytes that must be present
* in the receive queue to cont the socket as
* readable. 0 means to use the socket lo
* watermask value.
* Returns: IP_TRUE = socket is readable
* IP_FALSE = socket is not readable
*
*/
IP_GLOBAL Ip_bool
iplite_is_sock_readable(Iplite_socket *sock, int lowat)
{
if (sock->ipcom.so_errno)
/* Sockets with pending error is always readable */
return IP_TRUE;
if (sock->ipcom.rcv_bytes >= IP_MAX(lowat, 1))
return IP_TRUE;
#ifdef IPCOM_USE_TCP
if (sock->tcb != IP_NULL)
{
if (sock->tcb->backlog_done != IP_NULL
&& sock->tcb->backlog_done[0] != IP_SOCKERR)
/* accept() would not block */
return IP_TRUE;
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED)
|| IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_PEER_WRITE_CLOSED))
/* recv() would return EOF */
return IP_TRUE;
}
#endif
return IP_FALSE;
}
/*
*===========================================================================
* iplite_is_sock_writable
*===========================================================================
* Description: Checks if the specified socket is writable.
* Parameters: sock - the socket to check.
* lowat - minimum number of bytes that must be available
* in the transmit queue to cont the socket as
* writable. 0 means to use the socket lo
* watermark value.
* Returns: IP_TRUE = socket is writable
* IP_FALSE = socket is not writable
*
*/
IP_GLOBAL Ip_bool
iplite_is_sock_writable(Iplite_socket *sock, int lowat)
{
if (sock->ipcom.so_errno
|| IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED))
return IP_TRUE;
if ((IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED)
|| sock->ipcom.type != IP_SOCK_STREAM)
&& sock->snd_max_bytes - sock->snd_bytes >= IP_MAX(lowat, 1))
return IP_TRUE;
return IP_FALSE;
}
/*
*===========================================================================
* iplite_is_sock_exceptional
*===========================================================================
* Description: Checks if the specified socket is exceptional.
* Parameters: sock - the socket to check.
* lowat - unused. Added so this function has the same
* signature as the other iplite_is_sock_xxx().
* Returns: IP_TRUE = socket is exceptional
* IP_FALSE = socket is not exceptional.
*
*/
IP_GLOBAL Ip_bool
iplite_is_sock_exceptional(Iplite_socket *sock, int lowat)
{
/* exeptional socket is currently not supported */
IPCOM_UNUSED_ARG(sock);
IPCOM_UNUSED_ARG(lowat);
return IP_FALSE;
}
/*
*===========================================================================
* iplite_next_ephemeral_port
*===========================================================================
* Description: Returns the next ephemeral port.
* Parameters:
* Returns: The next free ephemeral port or 0 if no port could be found.
* The port is retured in host byte order.
*
*/
IP_GLOBAL Ip_u16
iplite_next_ephemeral_port(void)
{
int i;
Ip_u16 firstport;
Ip_u16 eport;
Ip_u16 anonportmax;
Ip_u16 anonportmin;
anonportmax = IPLITE_CONF_SOCK_ANONPORTMAX;
anonportmin = IPLITE_CONF_SOCK_ANONPORTMIN;
firstport = anonportmin + ipcom_rand() % (1 + anonportmax - anonportmin);
eport = firstport;
do {
for (i = 0; i < IPLITE_CONF_MAX_SOCKETS; i++)
{
if (iplite.bound_sockets[i] == IP_NULL)
break;
if (iplite.bound_sockets[i]->sport == eport)
goto next_port;
}
return eport;
next_port:
if (eport++ == anonportmax)
eport = anonportmin;
} while (eport != firstport);
return 0;
}
/*
*===========================================================================
* iplite_queue_received_packet
*===========================================================================
* Description: Queues a packet on the passed socket. The start field of
* the packet should specify beginning of application data.
* Parameters:
* Returns: 0 = success, <0 = error code
*
*/
IP_GLOBAL int
iplite_queue_received_packet(Ipcom_pkt *pkt, Iplite_socket *sock)
{
if (sock->ipcom.rcv_bytes + pkt->end - pkt->start > (int) sock->rcv_max_bytes)
{
IPLITE_STATS(pkt_queue_emsgsize++);
return -IP_ERRNO_EMSGSIZE;
}
/* Add the packet to the socket receive queue */
sock->ipcom.rcv_bytes += pkt->end - pkt->start;
if (sock->rcv_head != IP_NULL)
sock->rcv_tail->next = pkt;
else
sock->rcv_head = pkt;
sock->rcv_tail = pkt;
pkt->next = IP_NULL;
IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_SOCKRECVQ);
iplite_sock_data_avail(sock, 0, IP_SHUT_RD);
return 0;
}
/*
*===========================================================================
* iplite_sock_data_avail
*===========================================================================
* Description: Is called each time data arrives to the socket so that
* ipcom_recvfrom() and ipcom_accept() can be called without
* blocking.
* Parameters: sock - The socket on which data arrived.
* so_errno: 0 = data, -1 = close else socket errno.
* Returns: 0 = success
*
*/
IP_GLOBAL void
iplite_sock_data_avail(Iplite_socket *sock, int so_errno, int how)
{
unsigned select_mask;
if (so_errno != 0)
{
/* so_errno == -1 means a close event. */
if (so_errno != -1)
{
sock->ipcom.so_errno = so_errno;
ip_assert(sock->ipcom.so_errno > 0);
}
if (sock->ipcom.event_cb != IP_NULL
&& IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_CLOSED))
{
(void)sock->ipcom.event_cb((Ipcom_socket *)sock, IP_NULL,
sock->ipcom.so_errno ? IP_SOEVENT_CB_ERRNO : IP_SOEVENT_CB_CLOSE);
/* There must be no more events after the close event. */
if (sock->ipcom.so_errno == 0)
sock->ipcom.event_cb = IP_NULL;
}
}
switch (how)
{
case IP_SHUT_RD:
select_mask = IPLITE_SELECT_RD;
break;
case IP_SHUT_WR:
select_mask = IPLITE_SELECT_WR;
break;
case IP_SHUT_RDWR:
select_mask = IPLITE_SELECT_RD | IPLITE_SELECT_WR;
break;
#ifdef IPCOM_USE_TCP
case IP_SHUT_EX:
select_mask = 0;
break;
#endif
default:
IP_PANIC2();
select_mask = 0;
break;
}
/* Socket blocked in recvfrom() */
if (IP_BIT_ISSET(select_mask, IPLITE_SELECT_RD) || sock->ipcom.so_errno)
{
/* Wake up the process that is sleeping on the socket */
if (sock->ipcom.event_cb != IP_NULL)
sock->ipcom.event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_SELREAD);
#ifdef IP_PORT_OSE5_disabled_code
if (sock->ipcom.descs)
ipcom_crt_block_post((Ipcom_socket *)sock, IP_FALSE);
else
#endif
iplite_sock_wakeup_poll_list(&sock->rcv_poll_list);
}
if (IP_BIT_ISSET(select_mask, IPLITE_SELECT_WR) || sock->ipcom.so_errno)
{
/* Wake up the process(es) that waiting to send on the socket */
if (sock->ipcom.event_cb != IP_NULL)
sock->ipcom.event_cb(&sock->ipcom, IP_NULL, IP_SOEVENT_CB_SELWRITE);
#ifdef IP_PORT_OSE5_disabled_code
if (sock->ipcom.descs)
ipcom_crt_block_post((Ipcom_socket *)sock, IP_TRUE);
else
#endif
if (iplite_sock_is_writable(sock))
iplite_sock_wakeup_poll_list(&sock->snd_poll_list);
}
}
/*
*===========================================================================
* iplite_do_getname
*===========================================================================
* Description: Returns the socket address of the local side (if bound) or
* the peer side (if connected).
* Parameters: fd - Socket descriptor.
* name - Will hold the socket address if the call succeeds.
* namelenp - The length of the 'name' buffer, will be updated
* to hold the actual size of 'name'.
* local - Set to IP_TRUE if the name of local size should be
* returned and IP_FALSE if the peer socket name should be returned.
* Returns: 0 = success, <0 negative error code.
*
*/
IP_GLOBAL int
iplite_do_getname(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelenp, Ip_bool local)
{
Iplite_socket *sock;
ip_assert(name != IP_NULL);
ip_assert(namelenp != IP_NULL);
if (fd >= IPLITE_CONF_MAX_SOCKETS || iplite.sockets[fd] == IP_NULL)
return -IP_ERRNO_EBADF;
sock = iplite.sockets[fd];
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
struct Ip_sockaddr_in *name_in;
if (*namelenp < sizeof(struct Ip_sockaddr_in))
return -IP_ERRNO_ENOBUFS;
*namelenp = sizeof(struct Ip_sockaddr_in);
name_in = (struct Ip_sockaddr_in *)name;
ipcom_memset(name_in->sin_zero, 0, sizeof(name_in->sin_zero));
name_in->sin_len = sizeof(struct Ip_sockaddr_in);
name_in->sin_family = IP_AF_INET;
if (local)
{
name_in->sin_addr.s_addr = sock->d->ip4.saddr_n;
name_in->sin_port = ip_htons(sock->sport);
}
else
{
name_in->sin_addr.s_addr = sock->d->ip4.daddr_n;
name_in->sin_port = ip_htons(sock->dport);
}
}
else
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
{
struct Ip_sockaddr_in6 *name_in6;
if (*namelenp < sizeof(struct Ip_sockaddr_in6))
return -IP_ERRNO_ENOBUFS;
*namelenp = sizeof(struct Ip_sockaddr_in6);
name_in6 = (struct Ip_sockaddr_in6 *) name;
name_in6->sin6_len = sizeof(struct Ip_sockaddr_in6);
name_in6->sin6_family = IP_AF_INET6;
name_in6->sin6_scope_id = sock->d->ip6.scope_id;
name_in6->sin6_flowinfo = sock->d->ip6.flowinfo_n;
if (local)
{
name_in6->sin6_addr = sock->d->ip6.saddr;
name_in6->sin6_port = ip_htons(sock->sport);
}
else
{
name_in6->sin6_addr = sock->d->ip6.daddr;
name_in6->sin6_port = ip_htons(sock->dport);
}
}
else
#endif /* IPCOM_USE_INET6 */
{
IP_PANIC();
return -IP_ERRNO_EPROTONOSUPPORT;
}
if (!local && IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED))
return -IP_ERRNO_ENOTCONN;
return 0;
}
/*
*===========================================================================
* iplite_do_bind
*===========================================================================
* Description: Bind a socket to the specified address and port.
* Parameters: sock - A pointer to the socket data.
* name - The socket name (address and port).
* namelen - The length of the name variable.
* check_reuse - Set to IP_TRUE if the name should be checked
* for address reuse.
* Returns: 0 = success, <0 = error code
*
*/
IP_GLOBAL int
iplite_do_bind(Iplite_socket *sock, IP_CONST struct Ip_sockaddr *name,
Ip_socklen_t namelen, Ip_bool check_reuse)
{
Ip_bool is_addr_any = IP_FALSE;
int insert_index;
int end_index;
int err;
ip_assert(!IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_BOUND));
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
{
struct Ip_sockaddr_in *name_in;
if (namelen < sizeof(struct Ip_sockaddr_in))
{
err = -IP_ERRNO_EINVAL;
goto errout;
}
name_in = (struct Ip_sockaddr_in*)name;
/* Verify that IP address is either ANY (0) or one of my addresses.
* Note: it is ok to bind to any of the multicastcast addresses
* or the broadcast address.
*/
if (iplite_ip4_get_addr_type(IP_NULL, name_in->sin_addr.s_addr) ==
IPLITE_ADDR_TYPE_NOT_LOCAL)
{
err = -IP_ERRNO_EADDRNOTAVAIL;
goto errout;
}
sock->d->ip4.saddr_n = name_in->sin_addr.s_addr;
is_addr_any = (sock->d->ip4.saddr_n == IP_INADDR_ANY);
sock->sport = ip_ntohs(name_in->sin_port);
}
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
{
struct Ip_sockaddr_in6 *name_in6;
Iplite_netif *netif;
if (namelen < sizeof(struct Ip_sockaddr_in6))
{
err = -IP_ERRNO_EINVAL;
goto errout;
}
name_in6 = (struct Ip_sockaddr_in6*) name;
if (iplite_ip6_is_scoped_addr(&name_in6->sin6_addr)
&& name_in6->sin6_scope_id != IPLITE_IF_LOOPBACK_INDEX)
{
netif = iplite_if_indextonetif(name_in6->sin6_scope_id);
if (netif == IP_NULL)
{
err = -IP_ERRNO_EADDRNOTAVAIL;
goto errout;
}
}
else
netif = IP_NULL;
/* Verify that IP address is either iplite_in6addr_any or one of my addresses.
* Note: it is ok to bind to any of the multicast addresses.
*/
if (iplite_ip6_get_addr_type(netif, &name_in6->sin6_addr) ==
IPLITE_ADDR_TYPE_NOT_LOCAL)
{
err = -IP_ERRNO_EADDRNOTAVAIL;
goto errout;
}
sock->d->ip6.saddr = name_in6->sin6_addr;
sock->d->ip6.scope_id = name_in6->sin6_scope_id;
if (IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr))
is_addr_any = IP_TRUE;
else
is_addr_any = IP_FALSE;
sock->sport = ip_ntohs(name_in6->sin6_port);
}
#endif /* IPCOM_USE_INET6 */
if (sock->proto == IP_IPPROTO_TCP || sock->proto == IP_IPPROTO_UDP)
{
/* If port 0, pick an ephemereal port */
if (sock->sport == 0)
{
sock->sport = iplite_next_ephemeral_port();
if (sock->sport == 0)
{
err = -IP_ERRNO_EADDRINUSE;
goto errout;
}
}
if (check_reuse && sock->ipcom.type != IP_SOCK_RAW)
{
err = iplite_bind_check_reuse(sock);
if (err < 0)
goto errout;
}
}
/* Insert socket in socket port cache (iplite.bound_sockets) */
ip_assert(iplite.bound_sockets[IPLITE_CONF_MAX_SOCKETS - 1] == IP_NULL);
for (end_index = 0; iplite.bound_sockets[end_index] != IP_NULL; end_index++)
/* Empty block*/;
for (insert_index = 0; iplite.bound_sockets[insert_index] != IP_NULL; insert_index++)
{
if (iplite.bound_sockets[insert_index]->sport > sock->sport)
break;
if (iplite.bound_sockets[insert_index]->sport == sock->sport)
{
if (iplite.bound_sockets[insert_index]->ipcom.domain > sock->ipcom.domain)
break;
if (!is_addr_any)
break;
}
}
/* insert_index contains the index of the first entry to move */
while (end_index != insert_index)
{
iplite.bound_sockets[end_index] = iplite.bound_sockets[end_index - 1];
end_index--;
}
iplite.bound_sockets[insert_index] = sock;
#ifndef IPLITE_MEM_OPT
iplite.socket_last = IP_NULL;
#endif
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_BOUND);
return 0;
errout:
if (sock != IP_NULL)
sock->sport = 0;
return err;
}
/*
*===========================================================================
* iplite_do_socket
*===========================================================================
* Description: Creates a new socket.
* Parameters: domain - The address domain.
* type - The socket type.
* protcol - The protocol the stack must use for communication
* when using this socket.
* Returns: 0 >= The new socket descriptor.
* 0 < error, error code can be read from ipcom_errno.
*
*/
IP_GLOBAL Ip_fd
iplite_do_socket(int domain, int type, int protocol, Ip_bool user_context)
{
Ip_fd fd;
int i;
Iplite_socket *sock = IP_NULL;
Ip_size_t size, offset;
/* Raw IP socket. */
if (type == IP_SOCK_RAW)
goto type_ok;
#ifdef IPCOM_USE_PACKET
if (domain == IP_AF_PACKET
&& (type == IP_SOCK_DGRAM || type == IP_SOCK_RAW))
goto type_ok;
#endif
if (domain == IP_AF_INET || domain == IP_AF_INET6)
{
if (type == IP_SOCK_DGRAM)
{
/* UDP socket. */
if (protocol == 0)
protocol = IP_IPPROTO_UDP;
if (protocol == IP_IPPROTO_UDP)
goto type_ok;
}
#ifdef IPCOM_USE_TCP
if (type == IP_SOCK_STREAM)
{
if (protocol == 0)
protocol = IP_IPPROTO_TCP;
if (protocol == IP_IPPROTO_TCP)
goto type_ok;
}
#endif /* IPCOM_USE_TCP */
}
/* Bad type or protocol. */
SOCK_ERROUT(IP_ERRNO_EPROTONOSUPPORT);
type_ok:
#ifdef IPCOM_USE_INET
if (domain == IP_AF_INET)
goto domain_ok;
#endif
#ifdef IPCOM_USE_INET6
if (domain == IP_AF_INET6)
goto domain_ok;
#endif
#ifdef IPIPSEC2
if (domain == IP_AF_KEY)
goto domain_ok;
#endif
#ifdef IPCOM_USE_PACKET
if (domain == IP_AF_PACKET)
goto domain_ok;
#endif
/* Invalid domain. */
SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT);
/* Find an available socket descriptor slot. */
domain_ok:
for (i = 1; i <= IPLITE_CONF_MAX_SOCKETS - IPLITE_SOCKET_STARTFD; i++)
{
Ip_size_t max_hdrspace;
fd = (iplite.last_fd - IPLITE_SOCKET_STARTFD + i)
% (IPLITE_CONF_MAX_SOCKETS - IPLITE_SOCKET_STARTFD) + IPLITE_SOCKET_STARTFD;
if (iplite.sockets[fd] != IP_NULL)
continue;
max_hdrspace = 0;
size = sizeof(Iplite_socket);
#ifdef IPCOM_USE_INET
if (domain == IP_AF_INET)
{
max_hdrspace = IPLITE_IP_HDR_SIZE;
size += sizeof(Iplite_ip4_socket);
}
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (domain == IP_AF_INET6)
{
max_hdrspace = IPLITE_IP6_HDR_SIZE;
size += sizeof(Iplite_ip6_socket);
}
#endif /* IPCOM_USE_INET6 */
#ifdef IPCOM_USE_TCP
if (protocol == IP_IPPROTO_TCP)
size += sizeof(Iplite_tcp_tcb);
#endif /* IPCOM_USE_TCP */
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
size += ipcom_windnet_socklib_sock_size();
#endif
#ifdef IPIPSEC2
if (domain == IP_AF_KEY)
max_hdrspace = 8; /* to get 64 bit alignment. */
#endif /* IPIPSEC2 */
sock = iplite_kmalloc(size);
if (sock == IP_NULL)
SOCK_ERROUT(IP_ERRNO_ENOMEM);
/* Init socket */
ipcom_list_init(&sock->rcv_poll_list);
ipcom_list_init(&sock->snd_poll_list);
sock->ref_count = 1;
sock->ipcom.fd = (Ip_fd)fd;
sock->ipcom.domain = (Ip_u16)domain;
sock->proto = (Ip_u16)protocol;
sock->ipcom.type = (Ip_u16)type;
sock->max_hdrspace = (Ip_u16)max_hdrspace;
sock->uni_hop_limit = IPLITE_CONF_SOCK_BASE_HOP_LIMIT;
sock->multi_hop_limit = 1;
sock->ipcom.pid = ipcom_getpid();
sock->snd_max_bytes = IPLITE_CONF_SOCK_DEFAULT_SEND_BUF_SIZE;
sock->rcv_max_bytes = IPLITE_CONF_SOCK_DEFAULT_RECV_BUF_SIZE;
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_LOOP_MULTICAST);
sock->transport_output = iplite_raw_output;
offset = sizeof(Iplite_socket);
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_SOCK_COMPAT)
if (size > offset)
{
sock->select_ctx = (void *)((Ip_u8 *)sock + offset);
offset += ipcom_windnet_socklib_sock_size();
}
#endif
if (size > offset)
sock->d = (Iplite_socket_domain *) ((Ip_u8 *)sock + offset);
if (domain == IP_AF_INET || domain == IP_AF_INET6)
{
switch (protocol)
{
case IP_IPPROTO_UDP:
sock->transport_output = iplite_udp_output;
sock->max_hdrspace += IPLITE_UDP_HDR_SIZE;
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM);
sock->chk_offset = 6;
break;
#ifdef IPCOM_USE_TCP
case IP_IPPROTO_TCP:
sock->tcb = (Iplite_tcp_tcb *) ((Ip_u8 *)sock + offset);
sock->d = (Iplite_socket_domain *) (sock->tcb + 1);
iplite_tcp_sock_init(sock);
if (user_context)
/* Socket create in user-context, add an extra reference for the TCP module */
IPLITE_SOCK_ADD_REF(sock);
/* else: the TCP module will add an reference for the user when returning
the socket in accept() */
sock->max_hdrspace += IPLITE_TCP_HDR_SIZE;
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM);
sock->chk_offset = 16;
break;
#endif /* IPCOM_USE_TCP */
case IP_IPPROTO_ICMPV6:
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM);
sock->chk_offset = 2;
break;
default:
break;
}
}
/* Success. */
iplite.last_fd = fd;
iplite.sockets[fd] = sock;
#ifdef IPCOM_USE_PACKET
if (sock->ipcom.domain == IP_AF_PACKET)
{
sock->transport_output = iplite_packet_output;
iplite.num_packet_sock++;
}
#endif
return fd;
}
/* No free socket slot found. */
(void)ipcom_errno_set(IP_ERRNO_EMFILE);
errout:
if (sock != IP_NULL)
iplite_kfree(sock);
return IP_SOCKERR;
}
/*
*===========================================================================
* iplite_sock_drop_all_multicast_memberships
*===========================================================================
* Description: Drops all multicast memberships this socket has.
* Parameters: sock - a socket.
* netif - IP_NULL to dropp all multicast memberships or the
* interface for which all multicast memberships should be dropped.
* Returns: Number of multicast memberships that were dropped.
*/
IP_GLOBAL int
iplite_sock_drop_all_multicast_memberships(Iplite_socket *sock, Iplite_netif *netif, Ip_bool drop_ip4, Ip_bool drop_ip6)
{
int drop_count = 0;
#ifdef IPCOM_USE_INET
if (drop_ip4 && sock->ipcom.domain == IP_AF_INET)
{
struct Iplite_ip4_socket_multicast_data **mlt_it;
struct Iplite_ip4_socket_multicast_data *mlt;
mlt_it = &sock->d->ip4.multicast_addr_list;
while (*mlt_it != IP_NULL)
{
mlt = *mlt_it;
if (netif == IP_NULL || mlt->netif == netif)
{
drop_count++;
*mlt_it = mlt->next;
(void) iplite_ip4_remove_addr(mlt->netif, mlt->addr.s_addr);
iplite_kfree(mlt);
}
else
mlt_it = &mlt->next;
}
}
#else
(void)drop_ip4;
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (drop_ip6 && sock->ipcom.domain == IP_AF_INET6)
{
struct Iplite_ip6_socket_multicast_data **mlt_it;
struct Iplite_ip6_socket_multicast_data *mlt;
mlt_it = &sock->d->ip6.multicast_addr_list;
while (*mlt_it != IP_NULL)
{
mlt = *mlt_it;
if (netif == IP_NULL || mlt->netif == netif)
{
drop_count++;
*mlt_it = mlt->next;
(void) iplite_ip6_remove_addr(mlt->netif, &mlt->addr);
iplite_kfree(mlt);
}
else
mlt_it = &mlt->next;
}
}
#else
(void)drop_ip6;
#endif /* IPCOM_USE_INET6 */
return drop_count;
}
/*
*===========================================================================
* iplite_do_socketselect
*===========================================================================
* Description: Checks if any of the sockets in a set is ready for reading,
* writing or have an exceptional condition.
* Parameters: ndfs - The first 'ndfs' descriptors are checked in each set
* i.e., the descriptors from 0 through nfds-1 in the descriptor
* sets are examined.
* readfds - The set of descriptors that should be checked for
* reading.
* writefds - The set of descriptors that should be checked for
* writing (sockets are always writeable)
* exceptfds - The set of descriptors that should be checked for
* exceptions (not supported, must be IP_NULL).
* Returns: The total number of ready descriptors in each set.
*
*/
IP_GLOBAL int
iplite_do_socketselect(int nfds,
Ip_fd_set *readfds,
Ip_fd_set *writefds,
Ip_fd_set *exceptfds,
struct Ip_timeval *timeout)
{
int num;
Ip_s32 msec;
Ipcom_block select_event = IPCOM_BLOCK_INVALID;
(void)exceptfds;
ip_assert2(nfds > IPLITE_SOCKET_STARTFD);
IPLITE_CODE_ISLOCKED_ASSERT();
msec = iplite_timeval_to_msec(timeout);
if (msec < -1)
SOCK_ERROUT(IP_ERRNO_EINVAL);
if (msec != 0)
{
if (ipcom_block_create(&select_event) != IPCOM_SUCCESS)
SOCK_ERROUT(IP_ERRNO_ENOMEM);
}
num = iplite_sock_select(IPLITE_SOCKET_STARTFD,
nfds,
readfds,
writefds,
select_event, msec);
if (select_event != IPCOM_BLOCK_INVALID)
(void)ipcom_block_delete(&select_event);
if (num < 0)
SOCK_ERROUT(-num);
return num;
errout:
return IP_SOCKERR;
}
/*
*===========================================================================
* iplite_do_close
*===========================================================================
* Description: Closes the socket (tear down connections).
* Parameters: fd - A valid socket desriptor.
* Returns: 0 = success
*/
IP_GLOBAL int
iplite_do_close(Iplite_socket *sock)
{
ip_assert(sock != IP_NULL);
IPLITE_SOCK_ADD_REF(sock);
(void) iplite_do_shutdown(sock->ipcom.fd, IP_SHUT_RDWR);
#ifdef IPCOM_USE_TCP
if (sock->tcb != IP_NULL)
(void)iplite_tcp_close(sock);
#endif /* IPCOM_USE_TCP */
--sock->ref_count;
IPLITE_SOCK_DEL_REF(sock);
return 0;
}
/*
*===========================================================================
* iplite_sock_free
*===========================================================================
* Description: Frees all memory allocated by a socket.
* Parameters: sock - The socket to free.
* Returns:
*/
IP_PUBLIC void
iplite_sock_free(Iplite_socket *sock)
{
Ipcom_pkt *pkt;
ip_assert(sock != IP_NULL);
ip_assert(sock->ipcom.fd < IPLITE_CONF_MAX_SOCKETS);
ip_assert(sock->ipcom.fd >= IPLITE_SOCKET_STARTFD);
ip_assert(iplite.sockets[sock->ipcom.fd] == sock);
if (sock->ipcom.event_cb != IP_NULL)
(void)sock->ipcom.event_cb((Ipcom_socket *)sock, IP_NULL, IP_SOEVENT_CB_CLOSE);
iplite_sock_unbind(sock);
(void) iplite_sock_drop_all_multicast_memberships(sock, IP_NULL, IP_TRUE, IP_TRUE);
#ifdef IPCOM_USE_TCP
if (sock->tcb != IP_NULL)
iplite_tcp_sock_free(sock);
#endif /* IPCOM_USE_TCP */
/* Free any pending packets */
while (sock->rcv_head != IP_NULL)
{
pkt = sock->rcv_head;
sock->rcv_head = pkt->next;
ipcom_pkt_free(pkt);
}
#ifndef IPLITE_MEM_OPT
iplite.socket_last = IP_NULL;
#endif
iplite.sockets[sock->ipcom.fd] = IP_NULL;
#ifdef IPCOM_USE_PACKET
if (sock->ipcom.domain == IP_AF_PACKET)
iplite.num_packet_sock--;
#endif
iplite_kfree(sock);
}
/*
*===========================================================================
* iplite_ip4_socklookup
*===========================================================================
* Description: Returns the IPv4 socket matching the parameters.
* Parameters: proto - The protocol the returned socket should be using.
* local_addr_ptr - The IPv4 address in network order of the
* local address to match or IP_NULL if any address should match.
* local_port - The source port in HOST byte order or 0 if any
* source port should match.
* peer_addr_n - The IPv4 address in network order of the
* peer address to match or IP_NULL if any address should match.
* peer_port - The destination port in HOST byte order or 0 if any
* destination port should match.
* Returns: A pointer to the matching socket or IP_NULL if no socket match.
*
*/
#ifdef IPCOM_USE_INET
IP_GLOBAL Iplite_socket *
iplite_ip4_socklookup(int proto, Ip_u32 *local_addr_ptr, Ip_u16 local_port,
Ip_u32 *peer_addr_ptr, Ip_u16 peer_port, int *next)
{
int i;
Ip_u32 local_addr_n = (local_addr_ptr == IP_NULL ? IP_INADDR_ANY : *local_addr_ptr);
Ip_u32 peer_addr_n = (peer_addr_ptr == IP_NULL ? IP_INADDR_ANY : *peer_addr_ptr);
Iplite_socket *sock;
#ifndef IPLITE_MEM_OPT
if (next == IP_NULL && iplite.socket_last != IP_NULL &&
iplite.socket_last->ipcom.domain == IP_AF_INET &&
iplite.socket_last->proto == proto &&
iplite.socket_last->sport == local_port &&
iplite.socket_last->dport == peer_port &&
local_addr_n == iplite.socket_last->d->ip4.saddr_n &&
peer_addr_n == iplite.socket_last->d->ip4.daddr_n)
{
/* The socket that matched the last time also matched this packet */
return iplite.socket_last;
}
#endif
for (i = (next == IP_NULL ? 0 : *next); i < IPLITE_CONF_MAX_SOCKETS; i++)
{
if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP)
sock = iplite.bound_sockets[i];
else
sock = iplite.sockets[i];
if (sock == IP_NULL)
continue;
if (sock->ipcom.domain != IP_AF_INET)
continue;
if (sock->proto && sock->proto != proto)
continue;
if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP)
{
if (local_port != 0 && sock->sport != local_port)
continue;
if (peer_port != 0 && sock->dport != 0 && sock->dport != peer_port)
continue;
if (local_addr_n != IP_INADDR_ANY
&& sock->d->ip4.saddr_n != IP_INADDR_ANY
&& local_addr_n != sock->d->ip4.saddr_n)
continue;
if (peer_addr_n != IP_INADDR_ANY
&& sock->d->ip4.daddr_n != IP_INADDR_ANY
&& peer_addr_n != sock->d->ip4.daddr_n)
continue;
}
#ifndef IPLITE_MEM_OPT
if (!(sock->flags & (IPLITE_SOCKET_FLAG_REUSEADDR | IPLITE_SOCKET_FLAG_REUSEPORT)))
iplite.socket_last = sock;
#endif
if (next != IP_NULL)
*next = i + 1;
return sock;
}
return IP_NULL;
}
#endif /* IPCOM_USE_INET */
/*
*===========================================================================
* iplite_ip6_socklookup
*===========================================================================
* Description: Returns the IPv6 socket matching the parameters. Source is
* always this host and the destination is the peer.
* Parameters: proto - The protocol the returned socket should be using.
* src_addr - The IPv6 source address in NETWORK byte order or
* the undefined address (all zeros) if all addresses should
* match. IP_NULL is will also be interpreted as the undefined
* address.
* local_port - The source port in HOST byte order or 0 if any
* source port should match.
* dst_addr - The IPv6 destination address in NETWORK byte order
* or the undefined address (all zeros) if all addresses should
* match. IP_NULL is will also be interpreted as the undefined
* address.
* peer_port - The destination port in HOST byte order or 0 if any
* destination port should match.
* next - Index of the next socket to test. Set to IP_NULL if
* only the first match is interesting.
* Returns: A pointer to the matching socket or IP_NULL if no socket match.
*
*/
#ifdef IPCOM_USE_INET6
IP_GLOBAL Iplite_socket*
iplite_ip6_socklookup(int proto, struct Ip_in6_addr *local_addr, Ip_u16 local_port,
struct Ip_in6_addr *peer_addr, Ip_u16 peer_port, int *next)
{
int i;
Iplite_socket *sock;
if (local_addr != IP_NULL && IP_IN6_IS_ADDR_UNSPECIFIED(local_addr))
local_addr = IP_NULL;
if (peer_addr != IP_NULL && IP_IN6_IS_ADDR_UNSPECIFIED(peer_addr))
peer_addr = IP_NULL;
#ifndef IPLITE_MEM_OPT
if (next == IP_NULL && iplite.socket_last != IP_NULL &&
iplite.socket_last->ipcom.domain == IP_AF_INET6 &&
iplite.socket_last->proto == proto &&
iplite.socket_last->sport == local_port &&
iplite.socket_last->dport == peer_port &&
local_addr != IP_NULL && IP_IN6_ARE_ADDR_EQUAL(local_addr, &iplite.socket_last->d->ip6.saddr) &&
peer_addr != IP_NULL && IP_IN6_ARE_ADDR_EQUAL(peer_addr, &iplite.socket_last->d->ip6.daddr))
{
/* The socket that matched the last time also matched this packet */
return iplite.socket_last;
}
#endif
for (i = (next == IP_NULL ? 0 : *next); i < IPLITE_CONF_MAX_SOCKETS; i++)
{
if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP)
sock = iplite.bound_sockets[i];
else
sock = iplite.sockets[i];
if (sock == IP_NULL)
continue;
if (sock->ipcom.domain != IP_AF_INET6)
continue;
if (sock->proto && sock->proto != proto)
continue;
if (proto == IP_IPPROTO_TCP || proto == IP_IPPROTO_UDP)
{
if (local_port != 0 && sock->sport != local_port)
continue;
if (peer_port != 0 && sock->dport != 0 && sock->dport != peer_port)
continue;
if (local_addr != IP_NULL &&
!IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr) &&
!IP_IN6_ARE_ADDR_EQUAL(local_addr, &sock->d->ip6.saddr))
continue;
if (peer_addr != IP_NULL &&
!IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.daddr) &&
!IP_IN6_ARE_ADDR_EQUAL(peer_addr, &sock->d->ip6.daddr))
continue;
}
#ifndef IPLITE_MEM_OPT
if (!(sock->flags & (IPLITE_SOCKET_FLAG_REUSEADDR | IPLITE_SOCKET_FLAG_REUSEPORT)))
iplite.socket_last = sock;
#endif
if (next != IP_NULL)
*next = i + 1;
return sock;
}
return IP_NULL;
}
#endif /* IPCOM_USE_INET6 */
/*
*===========================================================================
* iplite_sock_unbind
*===========================================================================
* Description: Unbind the specific UDP or TCP socket.
* Parameters: sock - The socket to unbind.
* Returns:
*
*/
IP_GLOBAL void
iplite_sock_unbind(Iplite_socket *sock)
{
int r; /* The index of the socket to remove */
if (!IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_BOUND))
return;
/* Unbind the socket */
for (r = 0; r < IPLITE_CONF_MAX_SOCKETS && iplite.bound_sockets[r] != IP_NULL; r++)
{
if (iplite.bound_sockets[r] == sock)
{
int i;
for (i = r; i < IPLITE_CONF_MAX_SOCKETS && iplite.bound_sockets[i] != IP_NULL; i++)
{
if (i + 1 < IPLITE_CONF_MAX_SOCKETS)
iplite.bound_sockets[i] = iplite.bound_sockets[i + 1];
else
iplite.bound_sockets[i] = IP_NULL;
}
break;
}
}
ip_assert(r < IPLITE_CONF_MAX_SOCKETS);
sock->sport = 0;
IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_BOUND);
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipcom_socket
*===========================================================================
* Description: Creates a new socket.
* Parameters: domain - The address domain for this socket (IP_AF_xxx).
* type - The type of socket (IPLITE_SOCK_xxx).
* protocol - The protocol to use or 0 to select the default
* protocol for the selected socket type.
* Returns: >=0 the socket descriptor, -1 = error.
*
*/
IP_PUBLIC Ip_fd
ipcom_socket(int domain, int type, int protocol)
{
Ip_fd fd;
/* Block all ipcom_socket() calls until the stack is configured */
while (IP_BIT_ISFALSE(iplite_flags, IPLITE_FLAG_CONFIGURED))
ipcom_millisleep(10);
IPLITE_CODE_LOCK();
fd = iplite_do_socket(domain, type, protocol, IP_TRUE);
IPLITE_CODE_UNLOCK();
return fd;
}
/*
*===========================================================================
* ipcom_shutdown
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC int
ipcom_shutdown(Ip_fd fd, int how)
{
int ret;
IPLITE_CODE_LOCK();
ret = iplite_do_shutdown(fd, how);
IPLITE_CODE_UNLOCK();
return ret;
}
/*
*===========================================================================
* ipcom_socketclose
*===========================================================================
* Description: Closes a socket.
* Parameters: fd - The socket to close.
* Returns: 0= success, -1 = error
*
*/
IP_PUBLIC int
ipcom_socketclose(Ip_fd fd)
{
int ret;
Iplite_socket *sock;
ip_assert2(fd >= IPLITE_SOCKET_STARTFD);
ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS);
ip_assert2(iplite.sockets[fd] != IP_NULL);
IPLITE_CODE_LOCK();
if (fd < IPLITE_SOCKET_STARTFD
|| fd >= IPLITE_CONF_MAX_SOCKETS
|| IP_NULL == (sock = iplite.sockets[fd])
|| IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CLOSED))
{
ret = -IP_ERRNO_EBADF;
goto errout;
}
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_CLOSED);
(void)iplite_do_close(sock);
IPLITE_CODE_UNLOCK();
return 0;
errout:
ip_assert(ret < 0);
(void)ipcom_errno_set(-ret);
IPLITE_CODE_UNLOCK();
return IP_SOCKERR;
}
/*
*===========================================================================
* ipcom_socketselect
*===========================================================================
* Description: Checks if any of the sockets in a set is ready for reading,
* writing or have an exceptional condition.
* Parameters: ndfs - The first 'ndfs' descriptors are checked in each set
* i.e., the descriptors from 0 through nfds-1 in the descriptor
* sets are examined.
* readfds - The set of descriptors that should be checked for
* reading.
* writefds - The set of descriptors that should be checked for
* writing (sockets are always writeable)
* exceptfds - The set of descriptors that should be checked for
* exceptions (not supported, must be IP_NULL).
* Returns: The total number of ready descriptors in each set.
*
*/
IP_PUBLIC int
ipcom_socketselect(int nfds,
Ip_fd_set *readfds,
Ip_fd_set *writefds,
Ip_fd_set *exceptfds,
struct Ip_timeval *timeout)
{
int num;
IPLITE_CODE_LOCK();
num = iplite_do_socketselect(nfds, readfds, writefds, exceptfds, timeout);
IPLITE_CODE_UNLOCK();
return num;
}
#ifdef IPCOM_USE_TCP
/*
*===========================================================================
* ipcom_listen
*===========================================================================
* Description: Places a socket a state where it is listening for incoming
* connection(s).
* Parameters: fd - a bound, unconnected socket.
* backlog - The maximum length of the queue of pending
* connections. Use IPLITE_SOMAXCONN to get a maximum reasonable
* value
* Returns: 0 = success, -1 = error.
*
*/
IP_PUBLIC int
ipcom_listen(Ip_fd fd, int backlog)
{
Iplite_socket *sock;
int err;
ip_assert2(fd >= IPLITE_SOCKET_STARTFD);
ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS);
ip_assert2(iplite.sockets[fd] != IP_NULL);
IPLITE_CODE_LOCK();
sock = iplite.sockets[fd];
if (sock == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EBADF);
err = iplite_tcp_listen(sock, backlog);
if (err < 0)
SOCK_ERROUT(-err);
IPLITE_CODE_UNLOCK();
return err;
errout:
IPLITE_CODE_UNLOCK();
return IP_SOCKERR;
}
/*
*===========================================================================
* ipcom_accept
*===========================================================================
* Description: Accepts an incoming connection attempt on a socket.
* Parameters: fd - A socket that is in listen state (ipcom_listen()) has
* been called.
* name - Will hold the address of the peer (set to IP_NULL if
* that information is not wanted).
* addrlen - Must be initialized to the length of the 'name'
* buffer, will hold the actual 'name' length upon return.
* Set to IP_NULL if 'name' is IP_NULL.
* Returns: 0>= the socket to use for communication (success), -1 = error.
*
*/
IP_PUBLIC int
ipcom_accept(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelen)
{
Iplite_socket *sock;
Ip_fd new_fd;
ip_assert2(fd >= IPLITE_SOCKET_STARTFD);
ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS);
ip_assert2(iplite.sockets[fd] != IP_NULL);
IPLITE_CODE_LOCK();
sock = iplite.sockets[fd];
if (sock == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EBADF);
if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING))
{
Ip_fd_set rd_set;
IP_FD_ZERO(&rd_set);
IP_FD_SET(fd, &rd_set);
if (iplite_do_socketselect(fd + 1,
&rd_set,
IP_NULL,
IP_NULL,
IP_NULL) < 0)
/* errno already set */
goto errout;
}
new_fd = iplite_tcp_accept(sock, name, namelen);
if (new_fd < 0)
SOCK_ERROUT(-new_fd);
IPLITE_CODE_UNLOCK();
return new_fd;
errout:
IPLITE_CODE_UNLOCK();
return IP_SOCKERR;
}
#endif /* IPCOM_USE_TCP */
/*
*===========================================================================
* ipcom_bind
*===========================================================================
* Description: Assigns a name to a unnamed socket.
* Parameters: fd - The socket to name.
* name - The socket name.
* namelen - The length of the name buffer.
* Returns: 0 = success, -1 = error
*
*/
IP_PUBLIC int
ipcom_bind(Ip_fd fd, IP_CONST struct Ip_sockaddr *name, Ip_socklen_t namelen)
{
Iplite_socket *sock;
int err;
ip_assert2(fd >= IPLITE_SOCKET_STARTFD);
ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS);
ip_assert2(iplite.sockets[fd] != IP_NULL);
ip_assert2(name != IP_NULL);
ip_assert2(namelen > 0);
IPLITE_CODE_LOCK();
sock = iplite.sockets[fd];
if (sock == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EBADF);
#ifdef IPLITE_COMPAT_OLDSOCK
if (name->sa_family == 0 && name->sa_len < IP_AF_MAX)
((struct Ip_sockaddr *)name)->sa_family = name->sa_len;
((struct Ip_sockaddr *)name)->sa_len = (Ip_u8) namelen;
#endif
ip_assert(name->sa_len > 0);
if (name->sa_family != sock->ipcom.domain)
/* Wrong domain in socket name */
SOCK_ERROUT(IP_ERRNO_EINVAL);
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_BOUND))
/* Socket already bound. */
SOCK_ERROUT(IP_ERRNO_EINVAL);
err = iplite_do_bind(sock, name, namelen, IP_TRUE);
if (err < 0)
SOCK_ERROUT(-err);
/* success. */
IPLITE_CODE_UNLOCK();
return 0;
errout:
IPLITE_CODE_UNLOCK();
return (int)IP_SOCKERR;
}
/*
*===========================================================================
* ipcom_connect
*===========================================================================
* Description: Connects the socket to a specified peer.
* Parameters: fd - The socket to connect.
* to - The address of the peer.
* tolen - The length of the to buffer.
* Returns: 0= connection was successful, -1 = error.
*
*/
IP_PUBLIC int
ipcom_connect(Ip_fd fd, IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen)
{
Iplite_socket *sock = IP_NULL;
int retval;
struct Ip_sockaddr *name = IP_NULL;
#ifdef IPCOM_USE_INET
struct Ip_sockaddr_in in_name;
#endif
#ifdef IPCOM_USE_INET6
struct Ip_sockaddr_in6 in6_name;
#endif
Iplite_netif *netif;
ip_assert2(fd >= IPLITE_SOCKET_STARTFD);
ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS);
ip_assert2(iplite.sockets[fd] != IP_NULL);
IPLITE_CODE_LOCK();
if (to == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EINVAL);
sock = iplite.sockets[fd];
if (sock == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EBADF);
if (sock->ipcom.so_errno != 0)
SOCK_ERROUT(sock->ipcom.so_errno);
if (sock->ipcom.type == IP_SOCK_STREAM && IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED))
SOCK_ERROUT(IP_ERRNO_EISCONN);
IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED);
#ifdef IPLITE_COMPAT_OLDSOCK
if (to->sa_family == 0 && to->sa_len < IP_AF_MAX && to->sa_len != tolen)
((struct Ip_sockaddr *)to)->sa_family = to->sa_len;
((struct Ip_sockaddr *)to)->sa_len = (Ip_u8) tolen;
#endif /* IPLITE_COMPAT_OLDSOCK */
if (to->sa_family != sock->ipcom.domain && to->sa_family != IP_AF_UNSPEC)
SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT);
/* Determine the destination address */
#ifdef IPCOM_USE_INET
if (to->sa_family == IP_AF_INET)
{
/* Setup the IPv4 destination (peer) address */
struct Ip_sockaddr_in *in_to;
struct Ip_in_addr src_addr;
if (tolen < sizeof(struct Ip_sockaddr_in))
SOCK_ERROUT(IP_ERRNO_EINVAL);
in_to = (struct Ip_sockaddr_in*) to;
if (in_to->sin_addr.s_addr == IP_INADDR_ANY)
goto disconnect;
sock->d->ip4.daddr_n = in_to->sin_addr.s_addr;
sock->dport = ip_ntohs(in_to->sin_port);
if (sock->dport == 0)
SOCK_ERROUT(IP_ERRNO_EINVAL);
netif = iplite_ip4_get_netif(in_to->sin_addr.s_addr, IP_NULL);
if (netif == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH);
if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND)
|| sock->d->ip4.saddr_n == IP_INADDR_ANY)
{
if (sock->d->ip4.saddr_n == IP_INADDR_ANY)
{
/* No address specified when the socket was bound, need to rebind */
in_name.sin_port = ip_htons(sock->sport);
iplite_sock_unbind(sock);
}
else
/* Not bound before, bind to an ephemeral port */
in_name.sin_port = 0;
in_name.sin_family = IP_AF_INET;
in_name.sin_len = sizeof(in_name);
src_addr.s_addr = iplite_ip4_get_src_addr(netif, in_to->sin_addr.s_addr);
if (src_addr.s_addr == 0)
SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH);
in_name.sin_addr.s_addr = src_addr.s_addr;
name = (struct Ip_sockaddr *) &in_name;
}
/* else: already bound to an address != IP_INADDR_ANY */
}
else
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
if (to->sa_family == IP_AF_INET6)
{
/* Setup the IPv6 destination (peer) address */
struct Ip_sockaddr_in6 *in6_to;
IP_CONST struct Ip_in6_addr* src_addr;
Ip_u32 flowinfo = 0;
if (tolen < sizeof(struct Ip_sockaddr_in6))
SOCK_ERROUT(IP_ERRNO_EINVAL);
in6_to = (struct Ip_sockaddr_in6 *)to;
if (IP_IN6_IS_ADDR_UNSPECIFIED(&in6_to->sin6_addr))
goto disconnect;
flowinfo = in6_to->sin6_flowinfo;
if (ip_ntohl(flowinfo) & 0xff000000)
SOCK_ERROUT(IP_ERRNO_EINVAL);
netif = iplite_ip6_get_netif(in6_to->sin6_scope_id, &in6_to->sin6_addr, IP_NULL, IP_TRUE);
if (netif == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH);
sock->d->ip6.daddr = in6_to->sin6_addr;
sock->d->ip6.flowinfo_n = flowinfo;
sock->dport = ip_ntohs(in6_to->sin6_port);
sock->d->ip6.scope_id = in6_to->sin6_scope_id;
if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND)
|| IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr))
{
if (IP_IN6_IS_ADDR_UNSPECIFIED(&sock->d->ip6.saddr))
{
/* No address specified when the socket was bound, need to rebind */
in6_name.sin6_port = ip_htons(sock->sport);
iplite_sock_unbind(sock);
}
else
/* Not bound before, bind to an ephemeral port */
in6_name.sin6_port = 0;
in6_name.sin6_family = IP_AF_INET6;
in6_name.sin6_len = sizeof(in6_name);
src_addr = iplite_ip6_get_src_addr(netif, &sock->d->ip6.daddr, 0);
if (src_addr == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EHOSTUNREACH);
IPLITE_IP6_SET_ADDR(&in6_name.sin6_addr, src_addr);
in6_name.sin6_flowinfo = flowinfo;
in6_name.sin6_scope_id = in6_to->sin6_scope_id;
name = (struct Ip_sockaddr *) &in6_name;
}
/* else: already bound to an address != unspecified address */
}
else
#endif /* IPCOM_USE_INET6 */
if (to->sa_family == IP_AF_UNSPEC)
{
disconnect:
#ifdef IPCOM_USE_TCP
if (sock->tcb)
SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT);
#endif
#ifdef IPCOM_USE_INET
if (sock->ipcom.domain == IP_AF_INET)
sock->d->ip4.daddr_n = IP_INADDR_ANY;
#endif
#ifdef IPCOM_USE_INET6
if (sock->ipcom.domain == IP_AF_INET6)
sock->d->ip6.daddr = ip_in6addr_any;
#endif
IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED);
sock->dport = 0;
goto done;
}
else
SOCK_ERROUT(IP_ERRNO_EAFNOSUPPORT);
/* Make sure that the socket is bound so packet sent from the peer
socket is accepted */
if (IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND))
{
retval = iplite_do_bind(sock, name, name->sa_len, IP_TRUE); /*lint !e644 */
if (retval < 0)
SOCK_ERROUT(-retval);
}
#ifdef IPCOM_USE_TCP
if (sock->tcb != IP_NULL)
{
/* The socket is a TCP socket, do the TCP connect */
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTING))
/* Asynchronous connect is still not finished */
SOCK_ERROUT(IP_ERRNO_EALREADY);
retval = iplite_tcp_connect(sock); /*lint !e644 */
if (retval < 0)
SOCK_ERROUT(-retval);
}
else
#endif /* IPCOM_USE_TCP */
{
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED);
}
done:
/* success. */
IPLITE_CODE_UNLOCK();
return 0;
errout:
if (sock && (sock->ipcom.type != IP_SOCK_STREAM || ipcom_errno != IP_ERRNO_EISCONN))
IP_BIT_CLR(sock->flags, IPLITE_SOCKET_FLAG_CONNECTED);
IPLITE_CODE_UNLOCK();
return (int)IP_SOCKERR;
}
/*
*===========================================================================
* ipcom_socketwrite
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_socketwrite(Ip_fd sock, IP_CONST void *buf, Ip_size_t nbytes)
{
return ipcom_sendto(sock, buf, nbytes, 0, IP_NULL, 0);
}
/*
*===========================================================================
* ipcom_send
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_send(Ip_fd sock, IP_CONST void *msg, Ip_size_t len, int flags)
{
return ipcom_sendto(sock, msg, len, flags, IP_NULL, IP_NULL);
}
/*
*===========================================================================
* ipcom_sendto
*===========================================================================
* Description: Sends a packet to the address to which the socket is connected
* or to the 'to_param' address if the socket is not connected.
* Parameters: fd - The descriptor for the socket to use.
* buf - The buffer to send.
* len - The length of the buffer in octets.
* flags - Unused by IPLITE.
* to - The destination for the data if the socket is
* unconnected, ignored for connected sockets.
* tolen - The size of the 'to_param' buffer in octets.
* Returns: >=0 = number of bytes sent, IP_SOCKERR = error code is
* retrivered by using ipcom_errno.
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_sendto(Ip_fd fd, IP_CONST void *buf, Ip_size_t len, int flags,
IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen)
{
return ipcom_sendto_pktinfo(fd, buf, len, flags, to, tolen, IP_NULL);
}
/*
*===========================================================================
* ipcom_sendto_pktinfo
*===========================================================================
* Description: Sends a packet to the address to which the socket is connected
* or to the 'to_param' address if the socket is not connected.
* Parameters: fd - The descriptor for the socket to use.
* buf - The buffer to send.
* len - The length of the buffer in octets.
* flags - Unused by IPLITE.
* to - The destination for the data if the socket is
* unconnected, ignored for connected sockets.
* tolen - The size of the 'to_param' buffer in octets.
* Returns: >=0 = number of bytes sent, IP_SOCKERR = error code is
* retrivered by using ipcom_errno.
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_sendto_pktinfo(Ip_fd fd, IP_CONST void *buf, Ip_size_t len, int flags,
IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen,
IP_CONST struct Ip_msghdr *msg)
{
Ipcom_pkt *pkt;
Iplite_socket *sock;
int ret;
Ip_bool non_blocking;
ip_assert2(fd >= IPLITE_SOCKET_STARTFD);
ip_assert2(fd < IPLITE_CONF_MAX_SOCKETS);
ip_assert2(iplite.sockets[fd] != IP_NULL);
ip_assert2(buf != IP_NULL);
IPLITE_STATS(sock_sendto++);
IPLITE_CODE_LOCK();
if (fd >= IPLITE_CONF_MAX_SOCKETS || iplite.sockets[fd] == IP_NULL)
{
IPLITE_STATS(sock_sendto_ebadf++);
sock = IP_NULL;
SOCK_ERROUT(IP_ERRNO_EBADF);
}
sock = iplite.sockets[fd];
ip_assert(sock != IP_NULL);
IPLITE_SOCK_ADD_REF(sock);
if (sock->ipcom.so_errno != 0)
{
ip_assert(sock->ipcom.so_errno > 0);
SOCK_ERROUT(sock->ipcom.so_errno);
}
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_WRITE_CLOSED))
SOCK_ERROUT(IP_ERRNO_ESHUTDOWN);
#ifdef IPCOM_USE_TCP
if (sock->tcb != IP_NULL)
{
ret = iplite_tcp_usr_send(sock, buf, len, flags);
if (ret < 0)
{
IPLITE_STATS(sock_sendto_tcp_err++);
SOCK_ERROUT(-ret);
}
goto leave;
}
#endif /* IPCOM_USE_TCP */
/* Check if the packet is small enough to be sent
* (this value can be increased using IP_SO_SNDBUF socket option)
*/
if (len > (Ip_size_t) sock->snd_max_bytes)
{
IPLITE_STATS(sock_sendto_emsgsize++);
SOCK_ERROUT(IP_ERRNO_EMSGSIZE);
}
non_blocking = IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING)
|| IP_BIT_ISSET(flags, IP_MSG_DONTWAIT);
pkt = ipcom_pkt_malloc(len + sock->max_hdrspace,
IP_FLAG_FC_STACKCONTEXT | (non_blocking ? 0 : IP_FLAG_FC_BLOCKOK));
if (pkt == IP_NULL)
{
IPLITE_STATS(sock_sendto_nomem++);
if (non_blocking)
SOCK_ERROUT(IP_ERRNO_EWOULDBLOCK);
SOCK_ERROUT(IP_ERRNO_ENOMEM);
}
pkt->fd = sock->ipcom.fd;
pkt->start = ipcom_conf_max_link_hdr_size + sock->max_hdrspace;
#ifdef IPIPSEC2
if (sock->ipcom.domain == IP_AF_KEY)
{
while(((Ip_u32)&pkt->data[pkt->start] & 0x7) != 0)
pkt->start++;
}
#endif
#if defined(IPCOM_PKT_TX_ALIGN)
if (sock->ipcom.domain != IP_AF_KEY)
{
/* Optimize alignment for Ethernet header */
IPCOM_PKT_TX_ALIGN(pkt, sock->max_hdrspace + IP_ETH_HDR_SIZE, 1);
}
#endif /* IPCOM_PKT_TX_ALIGN */
/* Copy user data into pkt->data with enough space for anything. 0 bytes is valid. */
if (len > 0)
{
#if !defined(IPCOM_PKT_TX_ALIGN)
if (((int)&pkt->data[pkt->start] ^ (int)buf) & 0x3)
pkt->start += 2; /* Align user buffer with pkt buffer */
#endif /* IPCOM_PKT_TX_ALIGN */
#ifdef IPCOM_USE_ASM
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM))
pkt->chk = ipcom_in_checksum_memcpy(&pkt->data[pkt->start], (void *)buf, len);
else
ipcom_memcpy(&pkt->data[pkt->start], buf, len);
#else
ipcom_memcpy(&pkt->data[pkt->start], buf, len);
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_ADDCHECKSUM))
pkt->chk = ipcom_in_checksum_update(&pkt->data[pkt->start], len);
#endif
}
pkt->end = pkt->start + (int)len;
/* Send the data using UDP or Raw IP. */
if (non_blocking)
IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_NONBLOCKING);
#ifdef IPLITE_COMPAT_OLDSOCK
if (to != IP_NULL)
{
if (to->sa_family == 0 && to->sa_len < IP_AF_MAX)
((struct Ip_sockaddr *)to)->sa_family = to->sa_len;
((struct Ip_sockaddr *)to)->sa_len = (Ip_u8) tolen;
}
#else
(void)tolen;
#endif
ip_assert(to == IP_NULL || to->sa_len > 0);
if (sock->ipcom.type == IP_SOCK_DGRAM
&& IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_BOUND))
{
union Ip_sockaddr_union name;
/* Do an implicit bind */
ipcom_memset(&name, 0, sizeof(name));
name.sa.sa_family = (Ip_sa_family_t)sock->ipcom.domain;
ret = iplite_do_bind(sock, &name.sa, sizeof(name), IP_TRUE);
if (ret < 0)
SOCK_ERROUT(-ret);
}
ip_assert(sock->transport_output != IP_NULL);
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_INET6)
if ((msg != IP_NULL)
&& (msg->msg_control != IP_NULL)
&& (msg->msg_controllen >= sizeof(struct Ip_in6_pktinfo)))
{
struct Ip_in6_pktinfo *info = (struct Ip_in6_pktinfo *)IP_CMSG_DATA(msg->msg_control);
struct Ip_in6_pktinfo *info_pkt = IP_NULL;
ip_assert(info != IP_NULL);
pkt->ifindex = info->ipi6_ifindex;
info_pkt = (struct Ip_in6_pktinfo *)ipcom_pkt_alloc_info(pkt,
IPLITE_PKT_INFO_PKTINFO, sizeof(struct Ip_in6_pktinfo));
if (info_pkt != IP_NULL)
{
ipcom_memcpy(info_pkt, info, sizeof(struct Ip_in6_pktinfo));
}
}
#else
(void)msg;
#endif
ret = sock->transport_output(sock, to, pkt);
if (ret == 0)
ret = (int)len;
else if (ret < 0)
SOCK_ERROUT(-ret);
leave:
if (sock)
IPLITE_SOCK_DEL_REF(sock);
IPLITE_CODE_UNLOCK();
return (Ip_ssize_t)ret;
errout:
/* EWOULDBLOCK can occur due to two reasons here. Out of packet memory
* or due to interface output queue full. In either case, the application
* will get a PKTAVAIL callback event to notify when it can retry send.
* Note: This behaviour is required for socket server to function!
*/
if (sock != IP_NULL && sock->ipcom.event_cb != IP_NULL && ipcom_errno == IP_ERRNO_EWOULDBLOCK)
{
IP_BIT_SET(iplite_flags, IPLITE_FLAG_PKTAVAIL_CB_PENDING);
IP_BIT_SET(sock->flags, IPLITE_SOCKET_FLAG_PKTAVAIL_CB_PENDING);
}
ret = IP_SOCKERR;
goto leave;
}
/*
*===========================================================================
* ipcom_recv
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_recv(Ip_fd sock, void *buf, Ip_size_t len, int flags)
{
return ipcom_recvfrom(sock, buf, len, flags, IP_NULL, IP_NULL);
}
/*
*===========================================================================
* ipcom_socketread
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_PUBLIC Ip_ssize_t
ipcom_socketread(Ip_fd sock, void *buf, Ip_size_t nbytes)
{
return ipcom_recvfrom(sock, buf, nbytes, 0, IP_NULL, IP_NULL);
}
/*
*===========================================================================
* iplite_recvfrom_waitall
*===========================================================================
* Description: IPLITE does not have the required features to implement
* the "low watermark" feature. This makes implementing
* MSG_WAITALL a bit awkward. This helper function implements
* the correct behavior of MSG_WAITALL, but with a far from
* optimal solution.
* Parameters: fd - a file descriptor
* buf - buffer where the data will be stored
* len - length of 'buf'
* flags - flags with MSG_WAITALL set
* from - buffer where the address of the sender should be
* stored or IP_NULL if that information is not relevant
* fromlen - initially the length of 'from', at return the
* number octets written to 'from'
* Returns: Number of octets received or negative error code.
*
*/
#ifndef IP_PORT_OSE5
IP_STATIC Ip_ssize_t
iplite_recvfrom_waitall(Ip_fd fd, void *buf, Ip_size_t len, int flags,
struct Ip_sockaddr *from, Ip_socklen_t *fromlen)
{
int is_nonblock = IP_BIT_ISSET(flags, IP_MSG_DONTWAIT);
int ret;
struct Ip_timeval now;
struct Ip_timeval end;
Ip_socklen_t tv_len = sizeof(end);
Ip_bool has_timeout = IP_FALSE;
if (ipcom_getsockopt(fd, IP_SOL_SOCKET, IP_SO_RCVTIMEO, &end, &tv_len) == 0
&& (end.tv_sec > 0 || end.tv_usec > 0))
{
/* Receive timeout != infinite */
ipcom_microtime(&now);
ip_timeval_add(&end, &now, &end);
has_timeout = IP_TRUE;
}
if (!is_nonblock)
ipcom_socketioctl(fd, IP_X_FIOGNBIO, &is_nonblock);
flags &= ~IP_MSG_WAITALL;
while (len > (ret = ipcom_recvfrom(fd,
buf,
len,
flags | IP_MSG_PEEK,
from,
fromlen)))
{
/*
* Not the best of solutions, but have to do for now...
*/
if (is_nonblock)
return ret;
if (has_timeout)
{
ipcom_microtime(&now);
if (ip_timeval_cmp(&now, &end, >))
return -IP_ERRNO_EWOULDBLOCK;
}
ipcom_millisleep(100);
}
if (IP_BIT_ISFALSE(flags, IP_MSG_PEEK))
return ipcom_recvfrom(fd, buf, len, flags, from, fromlen);
return ret;
}
#endif /* IP_PORT_OSE5 */
/*
*===========================================================================
* ipcom_recvfrom
*===========================================================================
* Description: Receives data from a socket.
* Parameters: fd - A socket descriptor.
* buf - A buffer to where the data should be copied.
* len - The length of buf buffer.
* flags - Currently unused (set to 0).
* from - The sender of the data.
* fromlen - The length of the from buffer, will be updated to
* contain the number of octets copied to the from buffer.
* Returns: >=0 = the number of bytes copied to buf, -1 = error.
*
*/
/*lint -efunc(429,ipcom_recvfrom) Custodial pointer 'ptr' (line X) has not been freed or returned*/
IP_PUBLIC Ip_ssize_t
ipcom_recvfrom(Ip_fd fd, void *buf, Ip_size_t len, int flags,
struct Ip_sockaddr *from, Ip_socklen_t *fromlen)
{
return ipcom_recvfrom_pktinfo(fd, buf, len, flags, from, fromlen, IP_NULL);
}
/*
*===========================================================================
* ipcom_recvfrom_pktinfo
*===========================================================================
* Description: Receives data from a socket, at the same time, return pkt info.
* Parameters: fd - A socket descriptor.
* buf - A buffer to where the data should be copied.
* len - The length of buf buffer.
* flags - Currently unused (set to 0).
* from - The sender of the data.
* fromlen - The length of the from buffer, will be updated to
* contain the number of octets copied to the from buffer.
* msg - Ip_msgdhr, used to get packet information
* Returns: >=0 = the number of bytes copied to buf, -1 = error.
*
*/
/*lint -efunc(429,ipcom_recvfrom) Custodial pointer 'ptr' (line X) has not been freed or returned*/
IP_PUBLIC Ip_ssize_t
ipcom_recvfrom_pktinfo(Ip_fd fd, void *buf, Ip_size_t len, int flags,
struct Ip_sockaddr *from, Ip_socklen_t *fromlen, struct Ip_msghdr *msg)
{
int read_bytes = -1;
Iplite_socket *sock = IP_NULL;
Ipcom_pkt *pkt;
#ifndef IP_PORT_OSE5
/* Note: OSE5 always read in non-blocking mode which will make below not work */
if (IP_BIT_ISSET(flags, IP_MSG_WAITALL))
{
read_bytes = iplite_recvfrom_waitall(fd, buf, len, flags, from, fromlen);
if (read_bytes >= 0)
return (Ip_ssize_t) read_bytes;
ipcom_errno_set(-read_bytes);
return IP_SOCKERR;
}
#endif /* IP_PORT_OSE5 */
IPLITE_CODE_LOCK();
if (fd >= IPLITE_CONF_MAX_SOCKETS || iplite.sockets[fd] == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EBADF);
sock = iplite.sockets[fd];
ip_assert(sock != IP_NULL);
IPLITE_SOCK_ADD_REF(sock);
if (sock->ipcom.so_errno)
SOCK_ERROUT(sock->ipcom.so_errno);
if (IP_BIT_ISSET(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED))
{
read_bytes = 0;
goto errout;
}
/* Check if in blocking mode an no bytes to read, wait for data to
arrive in that case */
if (sock->ipcom.rcv_bytes == 0
&& IP_BIT_ISFALSE(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING)
&& IP_BIT_ISFALSE(flags, IP_MSG_DONTWAIT))
{
Ip_fd_set rd_set;
struct Ip_timeval start; /* Time */
struct Ip_timeval tv_buf;
struct Ip_timeval *tv = IP_NULL;
if (sock->rcv_timeout.tv_sec != 0
|| sock->rcv_timeout.tv_usec != 0)
{
/* Receive timeout != infinite */
ipcom_microtime(&start);
tv = &tv_buf;
*tv = sock->rcv_timeout;
}
IP_FD_ZERO(&rd_set);
for (;;)
{
IP_FD_SET(fd, &rd_set);
switch (iplite_do_socketselect(fd + 1,
&rd_set,
IP_NULL,
IP_NULL,
tv))
{
case 1: /* Socket ready */
break;
case 0: /* Timeout */
SOCK_ERROUT(IP_ERRNO_EWOULDBLOCK);
break;
default: /* Error */
/* errno has already been set */
goto errout;
}
if (iplite_sock_is_readable(sock))
break;
if (tv != IP_NULL)
{
struct Ip_timeval elapsed;
/* Another process grabbed the data before this
process got hold of the lock. */
ipcom_microtime(&elapsed);
ip_timeval_sub(&elapsed, &elapsed, &start);
*tv = sock->rcv_timeout;
if (ip_timeval_cmp(&elapsed, tv, <))
ip_timeval_sub(tv, tv, &elapsed);
else
{
tv->tv_sec = 0;
tv->tv_usec = 0;
}
}
}
}
if (sock->ipcom.rcv_bytes == 0
&& IP_UNLIKELY(IP_BIT_ISTRUE(sock->flags, IPLITE_SOCKET_FLAG_READ_CLOSED)))
{
/* the socket has been closed */
read_bytes = 0;
goto errout;
}
#ifdef IPCOM_USE_TCP
if (sock->tcb != IP_NULL)
{
#ifdef IP_PORT_VXWORKS
if (msg != IP_NULL)
{
msg->msg_controllen = 0;
}
#endif
read_bytes = iplite_tcp_usr_recv(sock, buf, len, flags);
if (read_bytes < 0)
{
IPLITE_STATS(sock_recvfrom_tcp_err++);
SOCK_ERROUT(-read_bytes);
}
}
else
#endif /* IPCOM_USE_TCP */
{
if (sock->ipcom.rcv_bytes == 0)
{
ip_assert(IP_BIT_ISTRUE(sock->flags, IPLITE_SOCKET_FLAG_NONBLOCKING)
|| IP_BIT_ISTRUE(flags, IP_MSG_DONTWAIT));
SOCK_ERROUT(IP_ERRNO_EWOULDBLOCK);
}
pkt = iplite_krecvfrom(sock, from, fromlen);
if (pkt == IP_NULL)
{
IPLITE_STATS(sock_recvfrom_err++);
SOCK_ERROUT(IP_ERRNO_EAGAIN);
}
read_bytes = IP_MIN((int)len, pkt->end - pkt->start);
if (read_bytes > 0)
ipcom_memcpy(buf, &pkt->data[pkt->start], read_bytes);
#if defined(IP_PORT_VXWORKS) && defined(IPCOM_USE_INET6)
if ((read_bytes > 0)
&& ((sock->flags & IPLITE_SOCKET_FLAG_RECVPKTINFO) != 0)
&& (msg != IP_NULL)
&& (msg->msg_control != IP_NULL)
&& (msg->msg_controllen > 0))
{
struct Ip_in6_pktinfo *pktinfo = IP_NULL;
Iplite_netif *netif = IP_NULL;
int cmsg_len = 0;
int pktinfo_len = 0;
struct Ip_cmsghdr *cmsg = (struct Ip_cmsghdr *)IP_CMSG_FIRSTHDR(msg);
pktinfo_len = sizeof(struct Ip_in6_pktinfo);
cmsg_len = IP_CMSG_SPACE(pktinfo_len);
if (cmsg_len <= msg->msg_controllen)
{
cmsg->cmsg_len = IP_CMSG_LEN(pktinfo_len);
cmsg->cmsg_level = IP_IPPROTO_IPV6;
cmsg->cmsg_type = IP_IPV6_PKTINFO;
pktinfo = (struct Ip_in6_pktinfo *)IP_CMSG_DATA(cmsg);
IPLITE_IP6_SET_ADDR(&pktinfo->ipi6_addr,
&((struct Ip_sockaddr_in6 *)from)->sin6_addr);
netif = iplite_if_indextonetif(pkt->ifindex);
if (netif != IP_NULL)
pktinfo->ipi6_ifindex = netif->ipcom.ifindex;
else
pktinfo->ipi6_ifindex = 0;
msg->msg_controllen = cmsg_len;
}
else
{
msg->msg_controllen = 0;
}
}
else if (msg != IP_NULL)
{
msg->msg_controllen = 0;
}
#else
if (msg != IP_NULL)
{
msg->msg_controllen = 0;
}
#endif
ipcom_pkt_free(pkt);
}
errout:
if (sock)
IPLITE_SOCK_DEL_REF(sock);
IPLITE_CODE_UNLOCK();
return (Ip_ssize_t) (read_bytes < 0 ? IP_SOCKERR : read_bytes);
}
/*
*===========================================================================
* iplite_krecvfrom
*===========================================================================
* Description: Returns the first packet in the in queue of the socket.
* Parameters: sock - The socket on which the packet was queued.
* from - The address of the sender of the packet.
* fromlen - The length of the buffer pointed to by from.
* Returns: The oldest recieved packet.
*
*/
IP_STATIC Ipcom_pkt *
iplite_krecvfrom(Iplite_socket *sock, struct Ip_sockaddr *from, Ip_socklen_t *fromlen)
{
Ipcom_pkt *pkt;
ip_assert(sock != IP_NULL);
ip_assert(sock->rcv_head != IP_NULL);
IPLITE_STATS(sock_krecvfrom++);
/* Unlink the first packet from the queue head. */
pkt = sock->rcv_head;
sock->ipcom.rcv_bytes -= pkt->end - pkt->start;
sock->rcv_head = pkt->next;
if (sock->rcv_head == IP_NULL)
sock->rcv_tail = IP_NULL;
/* Copy of sender info. */
if (from != IP_NULL)
{
Ip_u16 sport_n = 0;
ip_assert2(fromlen != IP_NULL);
if (fromlen == IP_NULL)
SOCK_ERROUT(IP_ERRNO_EINVAL);
if (sock->proto == IP_IPPROTO_UDP)
{
Iplite_pkt_udp *udp;
udp = (Iplite_pkt_udp*) &pkt->data[pkt->start - IPLITE_UDP_HDR_SIZE];
sport_n = udp->sport;
}
switch(sock->ipcom.domain)
{
#ifdef IPCOM_USE_INET
case IP_AF_INET:
if (*fromlen >= sizeof(struct Ip_sockaddr_in))
{
Iplite_pkt_ip *ip4;
struct Ip_sockaddr_in *from_in = (struct Ip_sockaddr_in *)from;
ip4 = (Iplite_pkt_ip *) &pkt->data[pkt->ipstart];
from_in->sin_len = sizeof(struct Ip_sockaddr_in);
from_in->sin_family = IP_AF_INET;
ipcom_memcpy(&from_in->sin_addr.s_addr, ip4->src, sizeof(Ip_u32));
from_in->sin_port = sport_n;
*fromlen = sizeof(struct Ip_sockaddr_in);
}
break;
#endif /* IPCOM_USE_INET */
#ifdef IPCOM_USE_INET6
case IP_AF_INET6:
if (*fromlen >= sizeof(struct Ip_sockaddr_in6))
{
Iplite_pkt_ip6 *ip6;
struct Ip_sockaddr_in6 *from_in6 = (struct Ip_sockaddr_in6 *)from;
ip6 = (Iplite_pkt_ip6*) &pkt->data[pkt->ipstart];
from_in6->sin6_len = sizeof(struct Ip_sockaddr_in6);
from_in6->sin6_family = IP_AF_INET6;
IPLITE_IP6_SET_ADDR(&from_in6->sin6_addr, &ip6->src);
from_in6->sin6_flowinfo = ip_htonl(IPLITE_IP6_GET_FLOW(ip6));
from_in6->sin6_scope_id = pkt->ifindex;
from_in6->sin6_port = sport_n;
*fromlen = sizeof(struct Ip_sockaddr_in6);
}
break;
#endif /* IPCOM_USE_INET6 */
#ifdef IPCOM_USE_PACKET
case IP_AF_PACKET:
{
Iplite_netif *netif;
struct Ip_sockaddr_ll *from_ll = (struct Ip_sockaddr_ll *)from;
netif = iplite_if_indextonetif(pkt->ifindex);
ip_assert2(netif != IP_NULL);
if (netif != IP_NULL && *fromlen >= sizeof(*from_ll))
{
netif->link_get_frame_info(netif, pkt, &from_ll->sll_pkttype,
&from_ll->sll_protocol, from_ll->sll_addr, IP_NULL);
from_ll->sll_len = sizeof(*from_ll);
from_ll->sll_family = IP_AF_PACKET;
from_ll->sll_ifindex = netif->ipcom.ifindex;
from_ll->sll_hatype = netif->ipcom.type;
from_ll->sll_halen = (Ip_u8) netif->ipcom.link_addr_size;
*fromlen = sizeof(*from_ll);
}
}
break;
#endif /* IPCOM_USE_PACKET */
default:
/* Domain not implemented (bug if this point is reached) */
IP_PANIC();
break;
}
}
#ifdef IPCOM_USE_PACKET
if (sock->ipcom.domain == IP_AF_PACKET && sock->ipcom.type == IP_SOCK_DGRAM)
{
/* The link header should not be included when the type is IP_SOCK_DGRAM */
Iplite_netif *netif = iplite_if_indextonetif(pkt->ifindex);
if (netif != IP_NULL)
pkt->start += netif->ipcom.link_hdr_size;
}
#endif /* IPCOM_USE_PACKET */
return pkt;
errout:
return IP_NULL;
}
/*
*===========================================================================
* ipcom_getsockname
*===========================================================================
* Description: Returns the name of the specified socket.
* Parameters: fd - A socket descriptor.
* name - A buffer that will receive the name of the socket.
* namelenp - A pointer to the length of name, will be updated
* to contain the actual number of octets copied to name.
* Returns: 0 = success, -1 = error
*
*/
IP_PUBLIC int
ipcom_getsockname(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelenp)
{
int ret;
ip_assert2(name);
ip_assert2(namelenp);
IPLITE_CODE_LOCK();
ret = iplite_do_getname(fd, name, namelenp, IP_TRUE);
IPLITE_CODE_UNLOCK();
if (ret < 0)
SOCK_ERROUT(-ret);
return 0;
errout:
return (int)IP_SOCKERR;
}
/*
*===========================================================================
* ipcom_getpeername
*===========================================================================
* Description: Returns the name of the peer connected to the specified socket.
* Parameters: fd - A socket descriptor.
* name - A buffer that will receive the name of the peer.
* namelenp - A pointer to the length of name, will be updated
* to contain the actual number of octets copied to name.
* Returns: 0 = success, -1 = error
*
*/
IP_PUBLIC int
ipcom_getpeername(Ip_fd fd, struct Ip_sockaddr *name, Ip_socklen_t *namelenp)
{
int ret;
ip_assert2(name);
ip_assert2(namelenp);
IPLITE_CODE_LOCK();
ret = iplite_do_getname(fd, name, namelenp, IP_FALSE);
IPLITE_CODE_UNLOCK();
if (ret < 0)
SOCK_ERROUT(-ret);
return 0;
errout:
return (int)IP_SOCKERR;
}
#endif /* !defined(IPLITE_ONE_FILE) || defined(IPLITE_C) */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/