/* 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 #include #include //const cpr_in6_addr_t in6addr_any = IN6ADDR_ANY_INIT; 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; cprAssert(addr != NULL, CPR_FAILURE); retval = connect(soc, (struct sockaddr *)addr, addr_len); while( retval == -1 && errno == EAGAIN && retry < MAX_RETRY_FOR_EAGAIN ) { cprSleep(100); retry++; retval = connect(soc, (struct sockaddr *)addr, addr_len); } return ((retval!=0) ? CPR_FAILURE : CPR_SUCCESS); } /** * 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 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 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); } /** * 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, using * the name and the pid to guarantee uniqueness * * @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_LOCAL; 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); }