991 lines
36 KiB
C
991 lines
36 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
#include "cpr_types.h"
|
|
#include "cpr_stdio.h"
|
|
#include "cpr_assert.h"
|
|
#include "cpr_socket.h"
|
|
#include "cpr_debug.h"
|
|
#include "cpr_rand.h"
|
|
#include "cpr_timers.h"
|
|
#include "cpr_errno.h"
|
|
#include "cpr_stdlib.h"
|
|
#include "cpr_string.h"
|
|
#include <sys/syslog.h>
|
|
#include <sys/fcntl.h>
|
|
#include <ctype.h>
|
|
|
|
|
|
const cpr_ip_addr_t ip_addr_invalid = {0};
|
|
|
|
#define IN6ADDRSZ 16
|
|
#define INT16SZ 2
|
|
#define INADDRSZ 4
|
|
|
|
#define MAX_RETRY_FOR_EAGAIN 10
|
|
|
|
/* Forward declarations of internal (helper) functions */
|
|
static int cpr_inet_pton4(const char *src, uint8_t *dst, int pton);
|
|
static int cpr_inet_pton6(const char *src, uint8_t *dst);
|
|
|
|
/**
|
|
* cprBind
|
|
*
|
|
* @brief The cprBind function is the CPR wrapper for the "Bind" socket call.
|
|
*
|
|
* The cprBind() function shall assign a local socket address address to a
|
|
* socket identified by descriptor socket that has no local socket address
|
|
* assigned. Sockets created with the cprSocket() function are initially
|
|
* unnamed; they are identified only by their address family.
|
|
*
|
|
* @param[in] soc - The socket previously created using cprAccept that is to be
|
|
* bound
|
|
* @param[out] addr - The address of the socket that is to be bound
|
|
* @param[in] addr_len Points to a cpr_socklen_t structure which on specifies the length
|
|
* of the supplied cpr_sockaddr_t structure.
|
|
*
|
|
* @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] socket is not a valid socket descriptor.
|
|
* @li [CPR_ENOTSOCK] The descriptor references a file, not a socket
|
|
*
|
|
*/
|
|
cpr_status_e
|
|
cprBind (cpr_socket_t soc,
|
|
CONST cpr_sockaddr_t * RESTRICT addr,
|
|
cpr_socklen_t addr_len)
|
|
{
|
|
cprAssert(addr != NULL, CPR_FAILURE);
|
|
|
|
return ((bind(soc, (struct sockaddr *)addr, addr_len) != 0) ?
|
|
CPR_FAILURE : CPR_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* cprCloseSocket
|
|
*
|
|
* @brief The cprCloseSocket function shall destroy the socket
|
|
*
|
|
* The cprCloseSocket() function shall destroy the socket descriptor indicated
|
|
* by socket. The descriptor may be made available for return by subsequent
|
|
* calls to cprSocket(). If the linger option is set with a non-zero timeout
|
|
* and the socket has untransmitted data, then cprCloseSocket() shall block for
|
|
* up to the current linger interval until all data is transmitted.
|
|
*
|
|
* @param[in] soc - The socket that needs to be destroyed
|
|
*
|
|
* @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] socket is not a valid socket descriptor.
|
|
*/
|
|
cpr_status_e
|
|
cprCloseSocket (cpr_socket_t soc)
|
|
{
|
|
return ((close(soc) != 0) ? CPR_FAILURE : CPR_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* cprConnect
|
|
*
|
|
* @brief The cprConnect function is the wrapper for the "connect" socket API
|
|
*
|
|
* The cprConnect() function shall attempt to make a connection on a socket.
|
|
* If the connection cannot be established immediately and non-blocking is set for
|
|
* the socket, cprConnect() shall fail and set cpr_errno to [CPR_EINPROGRESS], but
|
|
* the connection request shall not be aborted, and the connection shall be
|
|
* established asynchronously. When the connection has been established
|
|
* asynchronously, cprSelect() shall indicate that the file descriptor for the
|
|
* socket is ready for writing.
|
|
* If the initiating socket is connection-mode, then cprConnect() shall attempt to
|
|
* establish a connection to the address specified by the address argument.If the
|
|
* connection cannot be established immediately and non-blocking is not set for
|
|
* the socket, cprConnect() shall block for up to an unspecified timeout interval
|
|
* until the connection is established. If the timeout interval expires before the
|
|
* connection is established, cprConnect() shall fail and the connection attempt
|
|
* shall be aborted.
|
|
* If the initiating socket is connectionless (i.e. SOCK_DGRAM), then cprConnect()
|
|
* shall set the socket's peer address, and no connection is made. The peeraddress
|
|
* identifies where all datagrams are sent on subsequent cprSend() functions, and
|
|
* limits the remote sender for subsequent cprRecv() functions. If address is a
|
|
* null address for the protocol, the socket's peer address shall be reset.
|
|
*
|
|
* @param[in] soc - Specifies the socket created with cprSocket() to connect
|
|
* @param[in] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
|
|
* @param[in] addr_len - Points to a cpr_socklen_t structure which specifies
|
|
* the length of the supplied cpr_sockaddr_t structure.
|
|
*
|
|
* @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] socket is not a valid socket descriptor.
|
|
*/
|
|
cpr_status_e
|
|
cprConnect (cpr_socket_t soc,
|
|
SUPPORT_CONNECT_CONST cpr_sockaddr_t * RESTRICT addr,
|
|
cpr_socklen_t addr_len)
|
|
{
|
|
int retry = 0, retval;
|
|
cpr_status_e returnValue = CPR_FAILURE;
|
|
|
|
cprAssert(addr != NULL, CPR_FAILURE);
|
|
|
|
retval = connect(soc, (struct sockaddr *)addr, addr_len);
|
|
|
|
while( retval == -1 && ((errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN) || errno == EINPROGRESS || errno == EALREADY) ) {
|
|
cprSleep(100);
|
|
retry++;
|
|
retval = connect(soc, (struct sockaddr *)addr, addr_len);
|
|
}
|
|
|
|
if ( retval == 0 || (retval == -1 && errno == EISCONN))
|
|
{
|
|
returnValue = CPR_SUCCESS;
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/**
|
|
* cprGetSockName
|
|
*
|
|
* @brief The cprGetSockName retrieves the locally-bound name of the socket
|
|
*
|
|
* The cprGetSockName() function shall retrieve the locally-bound name of the
|
|
* specified socket, store this address in the cpr_sockaddr_t struct
|
|
* structure pointed to by the "addr" argument, and store the length of this address in
|
|
* the object pointed to by the "addr_len" argument. If the actual length
|
|
* of the address is greater than the length of the supplied cpr_sockaddr_t
|
|
* structure, the stored address shall be truncated. If the socket has not been
|
|
* bound to a local name, the value stored in the object pointed to by address is
|
|
* unspecified.
|
|
*
|
|
* @param[in] soc - Specifies the socket to get the peer address from
|
|
* @param[out] addr - A pointer to a cpr_sockaddr_t structure containing the peer address.
|
|
* @param[out] addr_len - Points to a cpr_socklen_t structure which specifies
|
|
* the length of the supplied cpr_sockaddr_t structure.
|
|
* @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
|
|
*
|
|
* @note If successful, the address argument shall point to the address of the socket
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
|
|
* @li [CPR_ENOTCONN] The socket is not connected
|
|
* @li [CPR_ENOTSOCK] The descriptor references a file, not a socket.
|
|
* @li [CPR_OPNOTSUPP] The operation is not supported for the socket
|
|
*/
|
|
cpr_status_e
|
|
cprGetSockName (cpr_socket_t soc,
|
|
cpr_sockaddr_t * RESTRICT addr,
|
|
cpr_socklen_t * RESTRICT addr_len)
|
|
{
|
|
cprAssert(addr != NULL, CPR_FAILURE);
|
|
cprAssert(addr_len != NULL, CPR_FAILURE);
|
|
|
|
return ((getsockname(soc, (struct sockaddr *)addr, addr_len) != 0) ?
|
|
CPR_FAILURE : CPR_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* cprListen
|
|
*
|
|
* @brief The cprListen is the CPR wrapper for the "listen" socket API.
|
|
*
|
|
* The cprListen() function shall mark a connection-mode socket, specified by
|
|
* the "soc" argument, as accepting connections. The "backlog" argument
|
|
* provides a hint to the implementation which the implementation shall use to
|
|
* limit the number of outstanding connections in the socket's listen queue.
|
|
* Implementations may impose a limit on backlog and silently reduce the
|
|
* specified value. Implementations shall support values of backlog up to
|
|
* SOMAXCONN.
|
|
* If listen() is called with a backlog argument value that is less than zero
|
|
* (0), the function behaves as if it had been called with a backlog argument
|
|
* value of 0. A backlog argument of zero (0) may allow the socket to accept
|
|
* connections, in which case the length of the listen queue may be set to an
|
|
* implementation-defined minimum value.
|
|
*
|
|
* @param[in] soc - Specifies the socket to get the peer address
|
|
* @param[in] backlog - The limit on the number of outstanding connections
|
|
*
|
|
* @return CPR_SUCCESS on success otherwise, CPR_FAILURE. cpr_errno needs to be set in this case.
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_EINVAL] cprListen() has not been called on the socket descriptor.
|
|
* @li [CPR_ENOTSOCK] The descriptor references a file, not a socket.
|
|
* @li [CPR_EDESTADDRREQ] The socket is not bound to a local address
|
|
*/
|
|
cpr_status_e
|
|
cprListen (cpr_socket_t soc,
|
|
uint16_t backlog)
|
|
{
|
|
return ((listen(soc, backlog) != 0) ? CPR_FAILURE : CPR_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* cprRecv
|
|
*
|
|
* @brief The cprRecv() function shall receive a message from a socket.
|
|
*
|
|
* This function is normally used with connected sockets because it does not permit
|
|
* the application to retrieve the source address of received data. The cprRecv()
|
|
* function shall return the length of the message written to the buffer pointed
|
|
* to by the "buf" argument.
|
|
* For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
|
|
* a single operation. If the message is too long to fit in the supplied buffer
|
|
* and the "flags" argument does not have MSG_PEEK set, the excess bytes are
|
|
* discarded. If the MSG_WAITALL flag is not set, data shall be returned onlyup
|
|
* to the end of the first message.
|
|
* For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
|
|
* data is returned as it becomes available; therefore, no data is discarded.
|
|
* If no messages are available at the socket and non-blocking is not set on the
|
|
* socket's file descriptor, cprRecv() shall block until a message arrives. If no
|
|
* messages are available at the socket and non-blocking is set on the socket's
|
|
* file descriptor, cprRecv() shall fail and set cpr_errno to [CPR_EAGAIN]or
|
|
* [CPR_EWOULDBLOCK].
|
|
* The cprSelect() function can be used to determine when data is available to be
|
|
* received. The cprRecv() function is the same as cprRecvFrom() with a zero
|
|
* address_len argument.
|
|
*
|
|
* @param[in] soc - Specifies the socket to receive data
|
|
* @param[out] buf - Contains the data received
|
|
* @param[out] len - The length of the data received
|
|
* @param[in] flags - The options used for the recv.
|
|
*
|
|
* @return On success the length of the message in bytes (including zero).
|
|
* On failure SOCKET_ERROR shall be returned and cpr_errno set to
|
|
* indicate the error.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_ENOTSOCK] The descriptor references a file, not a socket.
|
|
* @li [CPR_EAGAIN] The socket is marked non-blocking and no data is
|
|
* waiting to be received.
|
|
* @li [CPR_EWOULDBLOCK] Same as CPR_EAGAIN
|
|
* @li [CPR_ENOTCONN] A receive attempt is made on a connection-mode socket that is not connected
|
|
* @li [CPR_ENOTSUPP] The specified flags are not supported for this type of socket or protocol
|
|
*
|
|
*/
|
|
ssize_t
|
|
cprRecv (cpr_socket_t soc,
|
|
void * RESTRICT buf,
|
|
size_t len,
|
|
int32_t flags)
|
|
{
|
|
ssize_t rc;
|
|
int retry = 0;
|
|
|
|
cprAssert(buf != NULL, CPR_FAILURE);
|
|
|
|
rc = recv(soc, buf, len, flags);
|
|
while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
|
|
cprSleep(100);
|
|
retry++;
|
|
rc = recv(soc, buf, len, flags);
|
|
}
|
|
if (rc == -1) {
|
|
return SOCKET_ERROR;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* cprRecvFrom
|
|
*
|
|
* @brief The cprRecvFrom() function shall receive a message from a specific socket.
|
|
*
|
|
* The cprRecvFrom() function shall receive a message from a socket and is
|
|
* normally used with connectionless-mode sockets because it permits the
|
|
* application to retrieve the source address of received data. This function
|
|
* shall return the length of the message written to the buffer pointed to bythe
|
|
* buffer argument.
|
|
* For message-based sockets, e.g. SOCK_DGRAM, the entire message shall be read in
|
|
* a single operation. If the message is too long to fit in the supplied buffer
|
|
* and the flags argument does not have MSG_PEEK set, the excess bytes are
|
|
* discarded. If the MSG_WAITALL flag is not set, data shall be returned onlyup
|
|
* to the end of the first message.
|
|
* For stream-based sockets, e.g. SOCK_STREAM, message boundaries are ignored and
|
|
* data is returned as it becomes available; therefore, no data is discarded.
|
|
* If no messages are available at the socket and non-blocking is not set on the
|
|
* socket's file descriptor, cprRecvFrom() shall block until a message arrives. If no
|
|
* messages are available at the socket and non-blocking is set on the socket's
|
|
* file descriptor, cprRecvFrom() shall fail and set cpr_errno to [CPR_EAGAIN]or
|
|
* [CPR_EWOULDBLOCK].
|
|
*
|
|
* @param[in] soc - Specifies the socket to receive data
|
|
* @param[out] buf - Contains the data received
|
|
* @param[out] len - The length of the data received
|
|
* @param[in] flags - The options used for the recvFrom
|
|
* @param[out] from - A null pointer or pointer to a cpr_sockaddr_t structure in
|
|
* which the sending address is to be stored.
|
|
* @param[out] fromlen - The length of the cpr_sockaddr_t structure pointed to by
|
|
* the "from" argument.
|
|
*
|
|
* @return On success the length of the message in bytes (including zero).
|
|
* On failure SOCKET_ERROR shall be returned and cpr_errno set to
|
|
* indicate the error.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_ENOTSOCK] The descriptor references a file, not a socket.
|
|
* @li [CPR_EAGAIN] The socket is marked non-blocking and no data is
|
|
* waiting to be received.
|
|
* @li [CPR_EWOULDBLOCK] Same as CPR_EAGAIN
|
|
* @li [CPR_ENOTCONN] A receive attempt is made on a connection-mode socket that is not connected
|
|
* @li [CPR_ENOTSUPP] The specified flags are not supported for this type of socket or protocol
|
|
*
|
|
*/
|
|
ssize_t
|
|
cprRecvFrom (cpr_socket_t soc,
|
|
void * RESTRICT buf,
|
|
size_t len,
|
|
int32_t flags,
|
|
cpr_sockaddr_t * RESTRICT from,
|
|
cpr_socklen_t * RESTRICT fromlen)
|
|
{
|
|
ssize_t rc;
|
|
int retry = 0;
|
|
|
|
cprAssert(buf != NULL, CPR_FAILURE);
|
|
cprAssert(from != NULL, CPR_FAILURE);
|
|
cprAssert(fromlen != NULL, CPR_FAILURE);
|
|
|
|
rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
|
|
while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
|
|
cprSleep(100);
|
|
retry++;
|
|
rc = recvfrom(soc, buf, len, flags, (struct sockaddr *)from, fromlen);
|
|
}
|
|
|
|
if (rc == -1) {
|
|
CPR_INFO("error in recvfrom buf=%x fromlen=%d\n", buf, *fromlen);
|
|
return SOCKET_ERROR;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* cprSelect
|
|
*
|
|
* @brief The cprSelect() function is the CPR wrapper for the "select" socket API.
|
|
*
|
|
* The cprSelect() function returns which of the specified file descriptors is ready for
|
|
* reading, ready for writing, or has an exception pending. The function will
|
|
* block up to the specified timeout interval for one of the conditions to be
|
|
* true or until interrupted by a signal.
|
|
*
|
|
* File descriptor masks of type fd_set can be initialized and tested with
|
|
* FD_CLR(), FD_ISSET(), FD_SET(), and FD_ZERO(). The OS-implementation may
|
|
* implement these calls either as a macro definition or an actual function.
|
|
* void FD_CLR(cpr_socket_t, fd_set *) shall remove the file descriptor fd from the
|
|
* set. If fd is not a member of this set, there shall be no effect on theset.
|
|
* int FD_ISSET(cpr_socket_t, fd_set *) shall evaluate to non-zero if the file
|
|
* descriptor fd is a member of the set, and shall evaluate to zero otherwise.
|
|
* void FD_SET(cpr_socket_t, fd_set *) shall add the file descriptor fd to the set.
|
|
* If the file descriptor fd is already in this set, there shall be no effect on
|
|
* the set.
|
|
* void FD_ZERO(fd_set *) shall initialize the descriptor set to the null set.
|
|
*
|
|
* @param[in] nfds Specifies the argument range of file descriptors to be tested. The
|
|
* descriptors from zero through nfds-1 in the descriptor sets shall be examined.
|
|
* @param[in] read_fds If not a null pointer, this is a pointer to an fd_set object.
|
|
* @li On input this specifies the file descriptors to be checked for being ready to read.
|
|
* @li On output this specifies the file descriptors that are ready to read.
|
|
* @param[in] write_fds If not a null pointer, this is a pointer to an fd_set object.
|
|
* @li On input this specifies the file descriptors to be checked for being ready to write.
|
|
* @li On output this specifies the file descriptors that are ready to write.
|
|
* @param[in] except_fds If not a null pointer, this is a pointer to an fd_set object.
|
|
* @li On input this specifies the file descriptors to be checked for errors/exceptions pending.
|
|
* @li On output this specifies the file descriptors that have errors/exceptions pending.
|
|
* @param[in] timeout If not a null pointer, this points to an object of type struct cpr_timeval
|
|
* that specifies the maximum time interval to wait for the selection to complete.
|
|
* If timeout expires, the function shall return. If the parameter is a null pointer, the function
|
|
* will block indefinitely until at least one file descriptor meets the criteria.
|
|
*
|
|
* @note While this function supports multiple file descriptor types, only file descriptors referring to a
|
|
* socket are guaranteed to be supported.
|
|
* @note Note that the "nfds" parameter is not used in Windows.
|
|
*
|
|
* @return Upon successful completion, cprSelect() shall return the number of file descriptors ready.
|
|
* Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to indicate the error where read_fds,
|
|
* write_fds and error_fds are not modified.
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_INTR] The function was interrupted before an event or
|
|
* timeout occurred
|
|
* @li [CPR_INVAL] An invalid timeout was specified or nfds is less
|
|
* than 0 or greater than FD_SETSIZE
|
|
*
|
|
*/
|
|
int16_t
|
|
cprSelect (uint32_t nfds,
|
|
fd_set * RESTRICT read_fds,
|
|
fd_set * RESTRICT write_fds,
|
|
fd_set * RESTRICT except_fds,
|
|
struct cpr_timeval * RESTRICT timeout)
|
|
{
|
|
int16_t rc;
|
|
struct timeval t, *t_p;
|
|
|
|
if (timeout != NULL) {
|
|
t.tv_sec = timeout->tv_sec;
|
|
t.tv_usec = timeout->tv_usec;
|
|
t_p = &t;
|
|
} else {
|
|
t_p = NULL;
|
|
}
|
|
|
|
rc = (int16_t) select(nfds, read_fds, write_fds, except_fds, t_p);
|
|
if (rc == -1) {
|
|
return SOCKET_ERROR;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* cprSend
|
|
*
|
|
* @brief The cprSend() function is the CPR wrapper for the "send" socket API.
|
|
*
|
|
* The cprSend() function shall transmit a message from the specified socket to
|
|
* its peer. The cprSend() function shall send a message only when the socket is
|
|
* connected. The length of the message to be sent is specified by the length
|
|
* argument. If the message is too long to pass through the underlying protocol,
|
|
* cprSend() shall fail and no data is transmitted. Delivery of the message is
|
|
* not guaranteed.
|
|
* If space is not available at the sending socket to hold the message to be
|
|
* transmitted, and the socket does not have non-blocking set, cprSend() shall
|
|
* block until space is available; otherwise, if non-blocking is set, the
|
|
* cprSend() call shall fail.
|
|
*
|
|
* @param[in] soc Specifies the socket created with cprSocket() to send
|
|
* @param[in] buf A pointer to the buffer of the message to send.
|
|
* @param[in] len Specifies the length in bytes of the message pointed to by the buffer argument.
|
|
* @param[in] flags The socket options
|
|
*
|
|
* @return Upon successful completion, cprSend() shall return the number of
|
|
* bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
|
|
* indicate the error.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_ENOTSOCK] socket does not refer to a socket descriptor
|
|
* @li [CPR_EAGAIN] The socket is marked non-blocking and no data can
|
|
* be sent
|
|
* @li [CPR_EWOULDBLOCK] Same as CPR_EAGAIN
|
|
* @li [CPR_ENOTCONN] A connection-mode socket that is not connected
|
|
* @li [CPR_ENOTSUPP] The specified flags are not supported for this
|
|
* type of socket or protocol.
|
|
* @li [CPR_EMSGSIZE] The message is too large to be sent all at once
|
|
* @li [CPR_EDESTADDRREQ] The socket has no peer address set
|
|
*
|
|
*/
|
|
ssize_t
|
|
cprSend (cpr_socket_t soc,
|
|
CONST void *buf,
|
|
size_t len,
|
|
int32_t flags)
|
|
{
|
|
ssize_t rc;
|
|
int retry = 0;
|
|
|
|
cprAssert(buf != NULL, CPR_FAILURE);
|
|
|
|
rc = send(soc, buf, len, flags);
|
|
while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
|
|
cprSleep(100);
|
|
retry++;
|
|
rc = send(soc, buf, len, flags);
|
|
}
|
|
|
|
if (rc == -1) {
|
|
return SOCKET_ERROR;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* cprSendTo
|
|
*
|
|
* @brief The cprSendTo() function is the CPR wrapper for the "send" socket API.
|
|
*
|
|
* The cprSendTo() function shall send a message through a socket. If the socket
|
|
* is connectionless-mode, the message shall be sent to the address specified by
|
|
* address. If the socket is connection-mode, address shall be ignored.
|
|
* Delivery of the message is not guaranteed.
|
|
* If space is not available at the sending socket to hold the message to be
|
|
* transmitted, and the socket does not have non-blocking set, cprSendTo() shall
|
|
* block until space is available; otherwise, if non-blocking is set, the
|
|
* cprSendTo() call shall fail.
|
|
* The cprSelect() function can be used to determine when it is possible to send
|
|
* more data.
|
|
*
|
|
* @param[in] soc Specifies the socket created with cprSocket() to send
|
|
* @param[in] msg A pointer to the buffer of the message to send.
|
|
* @param[in] len Specifies the length in bytes of the message pointed to by the buffer argument.
|
|
* @param[in] flags The socket options
|
|
* @param[in] dest_addr Points to a cpr_sockaddr_t structure containing the destination
|
|
* address.
|
|
* @param[in] dest_len Specifies the length of the cpr_sockaddr_t structure pointed to by
|
|
* the "dest_addr" argument.
|
|
*
|
|
* @return Upon successful completion, cprSend() shall return the number of
|
|
* bytes sent. Otherwise, SOCKET_ERROR shall be returned and cpr_errno set to
|
|
* indicate the error.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_ENOTSOCK] socket does not refer to a socket descriptor
|
|
* @li [CPR_EAGAIN] The socket is marked non-blocking and no data can
|
|
* be sent
|
|
* @li [CPR_EWOULDBLOCK] Same as CPR_EAGAIN
|
|
* @li [CPR_ENOTCONN] A connection-mode socket that is not connected
|
|
* @li [CPR_ENOTSUPP] The specified flags are not supported for this
|
|
* type of socket or protocol.
|
|
* @li [CPR_EMSGSIZE] The message is too large to be sent all at once
|
|
* @li [CPR_EDESTADDRREQ] The socket has no peer address set
|
|
*
|
|
*/
|
|
ssize_t
|
|
cprSendTo (cpr_socket_t soc,
|
|
CONST void *msg,
|
|
size_t len,
|
|
int32_t flags,
|
|
CONST cpr_sockaddr_t *dest_addr,
|
|
cpr_socklen_t dest_len)
|
|
{
|
|
ssize_t rc;
|
|
int retry = 0;
|
|
|
|
cprAssert(msg != NULL, CPR_FAILURE);
|
|
cprAssert(dest_addr != NULL, CPR_FAILURE);
|
|
|
|
rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
|
|
while( rc == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) {
|
|
cprSleep(100);
|
|
retry++;
|
|
rc = sendto(soc, msg, len, flags, (struct sockaddr *)dest_addr, dest_len);
|
|
}
|
|
|
|
if (rc == -1) {
|
|
return SOCKET_ERROR;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* cprSetSockOpt
|
|
*
|
|
* @brief The cprSetSockOpt() function is used to set the socket options
|
|
*
|
|
* The cprSetSockOpt() function shall set the option specified by the
|
|
* option_name argument, at the protocol level specified by the "level" argument,
|
|
* to the value pointed to by the "opt_val" argument for the socket specified
|
|
* by the "soc" argument.
|
|
* The level argument specifies the protocol level at which the option resides. To
|
|
* set options at the socket level, specify the level argument as SOL_SOCKET. To
|
|
* set options at other levels, supply the appropriate level identifier for the
|
|
* protocol controlling the option. For example, to indicate that an option is
|
|
* interpreted by the TCP (Transport Control Protocol), set level to IPPROTO_TCP
|
|
* as defined in the <cpr_in.h> header.
|
|
* The opt_name argument specifies a single option to set. The option_name
|
|
* argument and any specified options are passed uninterpreted to the appropriate
|
|
* protocol module. The <cpr_socket.h> header defines the socket-level options.
|
|
*
|
|
* @param[in] soc The socket on which the options need to be set
|
|
* @param[in] level The protocol level at which the option resides
|
|
* @param[in] opt_name This specifies the single option that is being set
|
|
* @param[in] opt_val The values for the option
|
|
* @param[in] opt_len The length field for the option values
|
|
*
|
|
* @return Upon successful completion, CPR_SUCCESS shall be returned;
|
|
* otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
|
|
* error.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_ENOTSOCK] socket does not refer to a socket descriptor
|
|
* @li [CPR_EINVAL] The specified option is invalid or the socket is
|
|
* shut down
|
|
* @li [CPR_EISCONN] The specified socket is already connected and can
|
|
* not be changed
|
|
* @li [CPR_ENOPROTOOPT] The option is not supported by the protocol
|
|
*
|
|
*/
|
|
cpr_status_e
|
|
cprSetSockOpt (cpr_socket_t soc,
|
|
uint32_t level,
|
|
uint32_t opt_name,
|
|
CONST void *opt_val,
|
|
cpr_socklen_t opt_len)
|
|
{
|
|
cprAssert(opt_val != NULL, CPR_FAILURE);
|
|
|
|
return ((setsockopt(soc, (int)level, (int)opt_name, opt_val, opt_len) != 0)
|
|
? CPR_FAILURE : CPR_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* cprSetSockNonBlock
|
|
*
|
|
* @brief The cprSetSockNonBlock() function is used to set the socket options
|
|
*
|
|
* The cprSetSockNonBlock() function shall set a socket to be non blocking. It
|
|
* uses the fcntl function on the socket desriptor to achieve this. If the fcntl
|
|
* operation fails, a CPR_FAILURE is returned and errno is set by the OS
|
|
* implementation.
|
|
*
|
|
* @param[in] soc The socket that needs to be set to non-blocking
|
|
*
|
|
* @return Upon successful completion, CPR_SUCCESS shall be returned;
|
|
* otherwise, CPR_FAILURE shall be returned and cpr_errno set to indicate the
|
|
* error.
|
|
*/
|
|
cpr_status_e
|
|
cprSetSockNonBlock (cpr_socket_t soc)
|
|
{
|
|
return ((fcntl(soc, F_SETFL, O_NONBLOCK) != 0) ? CPR_FAILURE : CPR_SUCCESS);
|
|
}
|
|
|
|
cpr_status_e
|
|
cprShutDown (cpr_socket_t soc,
|
|
cpr_shutdown_e how)
|
|
{
|
|
int os_how;
|
|
|
|
switch (how) {
|
|
case CPR_SHUTDOWN_RECEIVE:
|
|
os_how = SHUT_RD;
|
|
break;
|
|
case CPR_SHUTDOWN_SEND:
|
|
os_how = SHUT_WR;
|
|
break;
|
|
case CPR_SHUTDOWN_BOTH:
|
|
default:
|
|
os_how = SHUT_RDWR;
|
|
break;
|
|
}
|
|
|
|
return ((shutdown(soc, os_how) != 0) ? CPR_FAILURE : CPR_SUCCESS);
|
|
}
|
|
|
|
/**
|
|
* cprSocket
|
|
*
|
|
* @brief The cprSocket() is the CPR wrapper for the "socket" API
|
|
*
|
|
* The cprSocket() function shall create an unbound socket in a
|
|
* communications domain, and return a file descriptor that can be used
|
|
* in later function calls that operate on sockets.
|
|
*
|
|
* @param[in] domain The communications domain, i.e. address family, in which a socket is to
|
|
* be created
|
|
* @param[in] type The type of socket to be created. The following types must
|
|
* be supported:
|
|
* @li SOCK_STREAM Provides sequenced, reliable, bidirectional, connection-mode
|
|
* byte streams, i.e. TCP
|
|
* @li SOCK_DGRAM Provides connectionless-mode, unreliable
|
|
* datagrams of fixed maximum length, i.e. UDP
|
|
* @li SOCK_SEQPACKET Provides sequenced, reliable, bidirectional, connection-mode
|
|
* transmission paths for records. A single operation never transfers part of
|
|
* more than one record. Record boundaries are visible to the receiver via the
|
|
* MSG_EOR flag.
|
|
* @param[in] protocol The protocol to be used with the socket.
|
|
*
|
|
* @return Upon successful completion, a socket handle defined by
|
|
* cpr_socket_t shall be returned; otherwise, INVALID_SOCKET shall be returned and
|
|
* cpr_errno set to indicate the error.
|
|
*
|
|
* @note The possible error values this function should return are
|
|
* @li [CPR_EBADF] The socket argument is not a valid file descriptor
|
|
* @li [CPR_ENOTSOCK] socket does not refer to a socket descriptor
|
|
* @li [CPR_EINVAL] The specified option is invalid or the socket is
|
|
* shut down
|
|
* @li [CPR_EMFILE] No more socket descriptors available for the
|
|
* process
|
|
* @li [CPR_ENFILE] No more socket descriptors available for the
|
|
* system
|
|
* @li [CPR_EPROTOTYPE] The socket type is not supported by the
|
|
* protocol
|
|
* @li [CPR_EPROTONOSUPPORT] The protocol is not supported for the
|
|
* domain
|
|
*
|
|
*/
|
|
cpr_socket_t
|
|
cprSocket (uint32_t domain,
|
|
uint32_t type,
|
|
uint32_t protocol)
|
|
{
|
|
cpr_socket_t s;
|
|
|
|
s = socket((int)domain, (int)type, (int)protocol);
|
|
if (s == -1) {
|
|
return INVALID_SOCKET;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
|
|
|
|
/* cpr_inet_pton
|
|
* Convert from presentation format (which usually means ASCII printable)
|
|
* to network format (which is usually some kind of binary format).
|
|
* @param[in] af The address family IPv4 or IPv6
|
|
* @param[in] src The address that needs to be converted
|
|
* @param[out] dst The address after the conversion
|
|
* @return
|
|
* 1 if the address was valid for the specified address family
|
|
* 0 if the address wasn't valid (`dst' is untouched in this case)
|
|
* -1 if some other error occurred (`dst' is untouched in this case, too)
|
|
*/
|
|
int
|
|
cpr_inet_pton (int af, const char *src, void *dst)
|
|
{
|
|
|
|
switch (af) {
|
|
case AF_INET:
|
|
return (cpr_inet_pton4(src, dst, 1));
|
|
case AF_INET6:
|
|
return (cpr_inet_pton6(src, dst));
|
|
default:
|
|
return (-1);
|
|
}
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
|
|
/**
|
|
* Utility function that sets up the socket address
|
|
*
|
|
* @param[in] addr - socket fd to bind with the IPC address.
|
|
* @param[in] name - pointer to the name of socket to bind to.
|
|
*
|
|
*
|
|
* @pre (name != NULL)
|
|
*/
|
|
void cpr_set_sockun_addr (cpr_sockaddr_un_t *addr, const char *name, pid_t pid)
|
|
{
|
|
/* Bind to the local socket */
|
|
memset(addr, 0, sizeof(cpr_sockaddr_un_t));
|
|
addr->sun_family = AF_UNIX;
|
|
snprintf((char *) addr->sun_path, sizeof(addr->sun_path), "%s_%d", name, pid);
|
|
}
|
|
|
|
/* int
|
|
* inet_pton4(src, dst, pton)
|
|
* when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand.
|
|
* when last arg is 1: inet_pton(). decimal dotted-quad only.
|
|
* return:
|
|
* 1 if `src' is a valid input, else 0.
|
|
* notice:
|
|
* does not touch `dst' unless it's returning 1.
|
|
*/
|
|
static int
|
|
cpr_inet_pton4(const char *src, uint8_t *dst, int pton)
|
|
{
|
|
uint32_t val;
|
|
uint32_t digit;
|
|
int base, n;
|
|
unsigned char c;
|
|
uint32_t parts[4];
|
|
uint32_t *pp = parts;
|
|
|
|
c = *src;
|
|
for (;;) {
|
|
/*
|
|
* Collect number up to ``.''.
|
|
* Values are specified as for C:
|
|
* 0x=hex, 0=octal, isdigit=decimal.
|
|
*/
|
|
if (!isdigit(c))
|
|
return (0);
|
|
val = 0; base = 10;
|
|
if (c == '0') {
|
|
c = *++src;
|
|
if (c == 'x' || c == 'X')
|
|
base = 16, c = *++src;
|
|
else if (isdigit(c) && c != '9')
|
|
base = 8;
|
|
}
|
|
/* inet_pton() takes decimal only */
|
|
if (pton && base != 10)
|
|
return (0);
|
|
for (;;) {
|
|
if (isdigit(c)) {
|
|
digit = c - '0';
|
|
if (digit >= (uint16_t)base)
|
|
break;
|
|
val = (val * base) + digit;
|
|
c = *++src;
|
|
} else if (base == 16 && isxdigit(c)) {
|
|
digit = c + 10 - (islower(c) ? 'a' : 'A');
|
|
if (digit >= 16)
|
|
break;
|
|
val = (val << 4) | digit;
|
|
c = *++src;
|
|
} else
|
|
break;
|
|
}
|
|
if (c == '.') {
|
|
/*
|
|
* Internet format:
|
|
* a.b.c.d
|
|
* a.b.c (with c treated as 16 bits)
|
|
* a.b (with b treated as 24 bits)
|
|
* a (with a treated as 32 bits)
|
|
*/
|
|
if (pp >= parts + 3)
|
|
return (0);
|
|
*pp++ = val;
|
|
c = *++src;
|
|
} else
|
|
break;
|
|
}
|
|
/*
|
|
* Check for trailing characters.
|
|
*/
|
|
if (c != '\0' && !isspace(c))
|
|
return (0);
|
|
/*
|
|
* Concoct the address according to
|
|
* the number of parts specified.
|
|
*/
|
|
n = pp - parts + 1;
|
|
/* inet_pton() takes dotted-quad only. it does not take shorthand. */
|
|
if (pton && n != 4)
|
|
return (0);
|
|
switch (n) {
|
|
|
|
case 0:
|
|
return (0); /* initial nondigit */
|
|
|
|
case 1: /* a -- 32 bits */
|
|
break;
|
|
|
|
case 2: /* a.b -- 8.24 bits */
|
|
if (parts[0] > 0xff || val > 0xffffff)
|
|
return (0);
|
|
val |= parts[0] << 24;
|
|
break;
|
|
|
|
case 3: /* a.b.c -- 8.8.16 bits */
|
|
if ((parts[0] | parts[1]) > 0xff || val > 0xffff)
|
|
return (0);
|
|
val |= (parts[0] << 24) | (parts[1] << 16);
|
|
break;
|
|
|
|
case 4: /* a.b.c.d -- 8.8.8.8 bits */
|
|
if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
|
|
return (0);
|
|
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
|
|
break;
|
|
}
|
|
if (dst) {
|
|
val = htonl(val);
|
|
memcpy(dst, &val, INADDRSZ);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
/* int
|
|
* inet_pton6(src, dst)
|
|
* convert presentation level address to network order binary form.
|
|
* return:
|
|
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
|
|
* notice:
|
|
* (1) does not touch `dst' unless it's returning 1.
|
|
* (2) :: in a full address is silently ignored.
|
|
*/
|
|
static int
|
|
cpr_inet_pton6(const char *src, uint8_t *dst)
|
|
{
|
|
static const char xdigits_l[] = "0123456789abcdef",
|
|
xdigits_u[] = "0123456789ABCDEF";
|
|
uint8_t tmp[IN6ADDRSZ], *tp, *endp, *colonp;
|
|
const char *xdigits, *curtok;
|
|
int ch, saw_xdigit;
|
|
unsigned int val;
|
|
|
|
memset((tp = tmp), '\0', IN6ADDRSZ);
|
|
endp = tp + IN6ADDRSZ;
|
|
colonp = NULL;
|
|
/* Leading :: requires some special handling. */
|
|
if (*src == ':')
|
|
if (*++src != ':')
|
|
return (0);
|
|
curtok = src;
|
|
saw_xdigit = 0;
|
|
val = 0;
|
|
while ((ch = *src++) != '\0') {
|
|
const char *pch;
|
|
|
|
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
|
|
pch = strchr((xdigits = xdigits_u), ch);
|
|
if (pch != NULL) {
|
|
val <<= 4;
|
|
val |= (pch - xdigits);
|
|
if (val > 0xffff)
|
|
return (0);
|
|
saw_xdigit = 1;
|
|
continue;
|
|
}
|
|
if (ch == ':') {
|
|
curtok = src;
|
|
if (!saw_xdigit) {
|
|
if (colonp)
|
|
return (0);
|
|
colonp = tp;
|
|
continue;
|
|
} else if (*src == '\0')
|
|
return (0);
|
|
if (tp + INT16SZ > endp)
|
|
return (0);
|
|
*tp++ = (uint8_t) (val >> 8) & 0xff;
|
|
*tp++ = (uint8_t) val & 0xff;
|
|
saw_xdigit = 0;
|
|
val = 0;
|
|
continue;
|
|
}
|
|
if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
|
|
cpr_inet_pton4(curtok, tp, 1) > 0) {
|
|
tp += INADDRSZ;
|
|
saw_xdigit = 0;
|
|
break; /* '\0' was seen by inet_pton4(). */
|
|
}
|
|
return (0);
|
|
}
|
|
if (saw_xdigit) {
|
|
if (tp + INT16SZ > endp)
|
|
return (0);
|
|
*tp++ = (uint8_t) (val >> 8) & 0xff;
|
|
*tp++ = (uint8_t) val & 0xff;
|
|
}
|
|
if (colonp != NULL) {
|
|
/*
|
|
* Since some memmove()'s erroneously fail to handle
|
|
* overlapping regions, we'll do the shift by hand.
|
|
*/
|
|
const int n = tp - colonp;
|
|
int i;
|
|
|
|
if (tp == endp)
|
|
return (0);
|
|
for (i = 1; i <= n; i++) {
|
|
endp[- i] = colonp[n - i];
|
|
colonp[n - i] = 0;
|
|
}
|
|
tp = endp;
|
|
}
|
|
if (tp != endp)
|
|
return (0);
|
|
memcpy(dst, tmp, IN6ADDRSZ);
|
|
return (1);
|
|
}
|
|
|