/************************************************************************** * Copyright (c) 2014 by Michael Fischer (www.emb4fun.de). * All rights reserved. * * Some parts are from the original BSD source, therefor: * * Partial Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *************************************************************************** * History: * * 26.01.2014 mifi First Version. * 27.01.2014 mifi More comments and connect added. * 28.01.2014 mifi Backlog functionality added. * 02.02.2014 mifi - Backlog does not work correct. * - Corrected sendto. * - Added support for setsockopt IP_ADD_MEMBERSHIP and * IP_DROP_MEMBERSHIP. * 04.02.2014 mifi Corrected backlog functionality. **************************************************************************/ #define __BSD_SOCKET_C__ /*=======================================================================*/ /* Include */ /*=======================================================================*/ #include #include #include #include #include #include #include /*=======================================================================*/ /* All Structures and Common Constants */ /*=======================================================================*/ /* * Wrapper for the BSD socket */ typedef struct _bsd_socket_ { int type; /* Used to distinguish TCP and UDP */ uint16_t port; /* Used by accept */ SEM backlog_sem; /* Used by accept and listen */ int is_listen_sock; /* Used by accept */ uint32_t udp_rcv_timeo; /* Used by NutUdpReceiveFrom */ struct _bsd_socket_ *listen; /* Used by accept */ TCPSOCKET *nut_tcp_sock; /* Handle to the NutNET TCP socket */ UDPSOCKET *nut_udp_sock; /* Handle to the NutNET USP socket */ } bsd_socket_t; /*=======================================================================*/ /* Definition of all local Data */ /*=======================================================================*/ /*=======================================================================*/ /* Definition of all local Procedures */ /*=======================================================================*/ /*=======================================================================*/ /* All code exported */ /*=======================================================================*/ /*************************************************************************/ /* socket */ /* */ /* Create a new unnamed socket for the given domain and type. */ /* */ /* In : domain, type, proto */ /* Out : none */ /* Return: Socket on success / -1 otherwise */ /*************************************************************************/ socket_t socket (int domain, int type, int proto) { bsd_socket_t *bsd_sock = (bsd_socket_t*)-1; TCPSOCKET *tcp_sock; UDPSOCKET *udp_sock; (void)proto; /* Not used */ /* Check for the correct domain */ if (AF_INET == domain) { /* Select the type */ switch (type) { case SOCK_STREAM: { /* Create a NutNET socket */ tcp_sock = NutTcpCreateSocket(); if (tcp_sock != NULL) { /* Get memory for the wrapper */ bsd_sock = (bsd_socket_t*)malloc(sizeof(bsd_socket_t)); if (bsd_sock != NULL) { /* Clear the memory first */ memset(bsd_sock, 0x00, sizeof(bsd_socket_t)); /* Setup wrapper */ bsd_sock->type = type; bsd_sock->nut_tcp_sock = tcp_sock; bsd_sock->nut_udp_sock = NULL; } else { /* No memory for the wrapper available, close NutNET socket */ NutTcpCloseSocket(tcp_sock); } } break; } /* SOCK_STREAM */ case SOCK_DGRAM: { /* Create a NutNET socket withg port 0 first, will be changed by bind */ udp_sock = NutUdpCreateSocket(0); if (udp_sock != NULL) { /* Get memory for the wrapper */ bsd_sock = (bsd_socket_t*)malloc(sizeof(bsd_socket_t)); if (bsd_sock != NULL) { /* Clear the memory first */ memset(bsd_sock, 0x00, sizeof(bsd_socket_t)); /* Setup wrapper */ bsd_sock->type = type; bsd_sock->nut_udp_sock = udp_sock; bsd_sock->nut_tcp_sock = NULL; } else { /* No memory for the wrapper available, close NutNET socket */ NutUdpDestroySocket(udp_sock); } } break; } /* SOCK_DGRAM */ default: { /* Wrong type */ bsd_sock = (bsd_socket_t*)-1; break; } } /* end switch (type) */ } /* end if (AF_INET == domain) */ return( (socket_t)bsd_sock ); } /* socket */ /*************************************************************************/ /* closesocket */ /* */ /* Close the socket and release the memory for the wrapper. */ /* */ /* Note: The socket may not be immediately destroyed after calling this */ /* function. However, the application must not use the socket */ /* after this call. */ /* */ /* In : sock */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int closesocket (socket_t sock) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; /* Select the type */ switch (bsd_sock->type) { case SOCK_STREAM: { /* Check if the socket was not closed before */ if (bsd_sock->nut_tcp_sock != NULL) { /* Close NutNET socket */ rc = NutTcpCloseSocket(bsd_sock->nut_tcp_sock); } /* * Check if a listen socket is available, this is the case * if this socket was created by an accept. Now the backlog * must be handled too. The listen socket will be closed separately. */ if (bsd_sock->listen) { /* Handle backlog */ NutSemPost(&bsd_sock->listen->backlog_sem); /* Clear listen socket info */ bsd_sock->listen = NULL; } /* * Check if this is the listen socket itself. In this * case the backlog semaphore must be deleted too. */ if (bsd_sock->is_listen_sock) { NutSemDestroy(&bsd_sock->backlog_sem); } /* Clear socket info */ bsd_sock->nut_tcp_sock = NULL; /* Free the wrapper memory */ free(bsd_sock); break; } /* SOCK_STREAM */ case SOCK_DGRAM: { /* Check if the socket was not closed before */ if (bsd_sock->nut_udp_sock != NULL) { /* Close NutNET socket */ rc = NutUdpDestroySocket(bsd_sock->nut_udp_sock); } /* Free the wrapper memory */ free(bsd_sock); break; } /* SOCK_DGRAM */ default: { /* Wrong type */ rc = -1; break; } } /* end switch (bsd_sock->type) */ return(rc); } /* closesocket */ /*************************************************************************/ /* shutdown */ /* */ /* Close one or both direction of the socket. */ /* */ /* Note: The socket itself must be still closed with closesocket. */ /* */ /* In : sock, how */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int shutdown (socket_t sock, int how) { (void)sock; (void)how; /* * Functionality not supported. * But we need the function to setup * the linker if the application use it. */ return(0); } /* shutdown */ /*************************************************************************/ /* bind */ /* */ /* Bind the local address to the socket. */ /* */ /* In : sock, addr, addr_len */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int bind (socket_t sock, struct sockaddr *addr, int addr_len) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; struct sockaddr_in *addr_in = (struct sockaddr_in*)addr; /* Check for correct length */ if (sizeof(struct sockaddr_in) == addr_len) { /* Store port info for later use, e.g. accept */ bsd_sock->port = ntohs(addr_in->sin_port); /* Correct port in case of UDP */ if (SOCK_DGRAM == bsd_sock->type) { /* * The UDP socket was created with port 0, * set now the correct port. */ bsd_sock->nut_udp_sock->so_local_port = addr_in->sin_port; } rc = 0; } return(rc); } /* bind */ /*************************************************************************/ /* listen */ /* */ /* Prepare a socket to accept incomming connections. */ /* */ /* In : sock, backlog */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int listen (socket_t sock, int backlog) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; /* listen is not supported for UDP */ if (SOCK_DGRAM == bsd_sock->type) { return(-1); } if (backlog > 0) { /* Mark socket as listen */ bsd_sock->is_listen_sock = 1; /* Create backlog semaphore */ NutSemInit(&bsd_sock->backlog_sem, (short)backlog); rc = 0; } return(rc); } /* listen */ /*************************************************************************/ /* accept */ /* */ /* Wait for incoming connect from a remote socket. */ /* */ /* In : sock, addr, addr_len */ /* Out : none */ /* Return: socket on success / -1 otherwise */ /*************************************************************************/ socket_t accept (socket_t sock, struct sockaddr *addr, int *addr_len) { int rc; socket_t ClientSocket = -1; bsd_socket_t *bsd_sock_listen = (bsd_socket_t*)sock; struct sockaddr_in *addr_in = (struct sockaddr_in*)addr; bsd_socket_t *bsd_sock_client; /* accept is not supported for UDP */ if (SOCK_DGRAM == bsd_sock_listen->type) { return(-1); } /* Check for correct length */ if (sizeof(struct sockaddr_in) == *addr_len) { /* Clear addr, and set default values */ memset(addr, 0x00, *addr_len); addr_in->sin_family = AF_INET; addr_in->sin_len = sizeof(struct sockaddr_in); /* Check if we could create a new socket */ NutSemWait(&bsd_sock_listen->backlog_sem); /* Create a new TCP socket */ ClientSocket = socket(AF_INET, SOCK_STREAM, 0); if (ClientSocket != -1) { /* Convert to BSD socket */ bsd_sock_client = (bsd_socket_t *)ClientSocket; /* Set listen information */ bsd_sock_client->listen = bsd_sock_listen; /* Wait for incoming connect from a remote socket */ rc = NutTcpAccept(bsd_sock_client->nut_tcp_sock, bsd_sock_listen->port); if (0 == rc) { /* Set address information of the remote side */ addr_in->sin_port = bsd_sock_listen->nut_tcp_sock->so_remote_port; addr_in->sin_addr.s_addr = bsd_sock_listen->nut_tcp_sock->so_remote_addr; } else { /* Error, This should not happen */ NutSemPost(&bsd_sock_listen->backlog_sem); NutTcpCloseSocket(bsd_sock_client->nut_tcp_sock); ClientSocket = -1; } } } /* end if (sizeof(struct sockaddr_in) == *addr_len) */ return(ClientSocket); } /* accept */ /*************************************************************************/ /* connect */ /* */ /* Connect to a remote socket. */ /* */ /* In : sock, addr, addr_len */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int connect (socket_t sock, struct sockaddr *addr, int addr_len) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; struct sockaddr_in *addr_in = (struct sockaddr_in*)addr; uint32_t remote_addr; uint16_t remote_port; /* accept is not supported for UDP */ if (SOCK_DGRAM == bsd_sock->type) { return(-1); } /* Check for correct length */ if (sizeof(struct sockaddr_in) == addr_len) { remote_addr = addr_in->sin_addr.s_addr; remote_port = htons(addr_in->sin_port); /* Connect to a remote socket */ rc = NutTcpConnect(bsd_sock->nut_tcp_sock, remote_addr, remote_port); } return(rc); } /* connect */ /*************************************************************************/ /* recv */ /* */ /* Receive data on a connected TCP socket. */ /* */ /* In : sock, data, len, flags */ /* Out : none */ /* Return: 0 or > 0 on success / -1 otherwise */ /*************************************************************************/ int recv (socket_t sock, void *data, size_t len, int flags) { int rc; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; (void)flags; /* recv is not supported for UDP */ if (SOCK_DGRAM == bsd_sock->type) { return(-1); } /* Receive data on a connected TCP socket */ rc = NutTcpReceive(bsd_sock->nut_tcp_sock, data, (int)len); return(rc); } /* recv */ /*************************************************************************/ /* recvfrom */ /* */ /* Receive a UDP datagram. */ /* */ /* In : sock, data, len, flags, addr, addr_len */ /* Out : none */ /* Return: 0 or > 0 on success / -1 otherwise */ /*************************************************************************/ int recvfrom (socket_t sock, void *data, size_t len, int flags, struct sockaddr *addr, int *addr_len) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; struct sockaddr_in *addr_in = (struct sockaddr_in*)addr; uint32_t remote_addr; uint16_t remote_port; (void)flags; /* recvfrom is not supported for TCP */ if (SOCK_STREAM == bsd_sock->type) { return(-1); } /* Check for correct length */ if (sizeof(struct sockaddr_in) == *addr_len) { /* Clear addr, and set default values */ memset(addr, 0x00, *addr_len); addr_in->sin_family = AF_INET; addr_in->sin_len = sizeof(struct sockaddr_in); /* Receive a UDP datagram */ rc = NutUdpReceiveFrom(bsd_sock->nut_udp_sock, &remote_addr, &remote_port, data, (int)len, bsd_sock->udp_rcv_timeo); if (rc > 0) { /* Set address information of the remote side */ addr_in->sin_port = htons(remote_port); addr_in->sin_addr.s_addr = remote_addr; } } return(rc); } /* recvfrom */ /*************************************************************************/ /* send */ /* */ /* Send data on a connected TCP socket. */ /* */ /* In : sock, data, len, flags */ /* Out : none */ /* Return: 0 or > 0 on success / -1 otherwise */ /*************************************************************************/ int send (socket_t sock, void *data, size_t len, int flags) { int rc = 0; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; (void)flags; /* send is not supported for UDP */ if (SOCK_DGRAM == bsd_sock->type) { return(-1); } /* Send data on a connected TCP socket */ rc = NutTcpSend(bsd_sock->nut_tcp_sock, data, (int)len); return(rc); } /* send */ /*************************************************************************/ /* sendto */ /* */ /* Send a UDP datagram. */ /* */ /* In : sock, data, len, flags, addr, addr_len */ /* Out : none */ /* Return: 0 or > 0 on success / -1 otherwise */ /*************************************************************************/ int sendto (socket_t sock, void *data, size_t len, int flags, struct sockaddr *addr, int addr_len) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; struct sockaddr_in *addr_in = (struct sockaddr_in*)addr; uint32_t remote_addr; uint16_t remote_port; (void)flags; /* recvfrom is not supported for TCP */ if (SOCK_STREAM == bsd_sock->type) { return(-1); } /* Check for correct length */ if (sizeof(struct sockaddr_in) == addr_len) { remote_addr = addr_in->sin_addr.s_addr; remote_port = htons(addr_in->sin_port); /* Send a UDP datagram */ rc = NutUdpSendTo(bsd_sock->nut_udp_sock, remote_addr, remote_port, data, (int)len); if (0 == rc) { rc = (int)len; } } return(rc); } /* sendto */ /*************************************************************************/ /* getsockname */ /* */ /* Return the local address information bound to the socket. */ /* */ /* In : sock, addr, addr_len */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int getsockname (socket_t sock, struct sockaddr *addr, int *addr_len) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; struct sockaddr_in *addr_in = (struct sockaddr_in*)addr; /* Check for correct length */ if (sizeof(struct sockaddr_in) == *addr_len) { /* Clear address info first */ memset(addr, 0x00, *addr_len); /* Set local address information */ addr_in->sin_len = sizeof(struct sockaddr_in); addr_in->sin_family = AF_INET; addr_in->sin_port = bsd_sock->nut_tcp_sock->so_local_port; addr_in->sin_addr.s_addr = bsd_sock->nut_tcp_sock->so_local_addr; rc = 0; } return(rc); } /* getsockname */ /*************************************************************************/ /* getpeername */ /* */ /* Return the remote address information bound to the socket. */ /* */ /* In : sock, addr, addr_len */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int getpeername (socket_t sock, struct sockaddr *addr, int *addr_len) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; struct sockaddr_in *addr_in = (struct sockaddr_in*)addr; /* Check for correct length */ if (sizeof(struct sockaddr_in) == *addr_len) { /* Clear address info first */ memset(addr, 0x00, *addr_len); /* Set remote address information */ addr_in->sin_len = sizeof(struct sockaddr_in); addr_in->sin_family = AF_INET; addr_in->sin_port = bsd_sock->nut_tcp_sock->so_remote_port; addr_in->sin_addr.s_addr = bsd_sock->nut_tcp_sock->so_remote_addr; rc = 0; } return(rc); } /* getpeername */ /*************************************************************************/ /* getsockopt */ /* */ /* Get a TCP / UDP socket option value. */ /* */ /* In : sock, level, optname, optval, optlen */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int getsockopt (socket_t sock, int level, int optname, void *optval, int optlen) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; /* Check for the correct level */ if (SOL_SOCKET == level) { switch (optname) { case SO_ERROR: /* Last error is available for TCP and UDP */ { /* Get the last error information, and clear it */ if ((optval != NULL) && (sizeof(int) == optlen)) { /* Last error */ *((int*)optval) = NutTcpError(bsd_sock->nut_tcp_sock); /* Clear info */ bsd_sock->nut_tcp_sock->so_last_error = 0; rc = 0; } break; } /* SO_ERROR */ default: { /* Check for stream or datagram */ if (SOCK_STREAM == bsd_sock->type) { /* It is a stream socket, get the option value */ rc = NutTcpGetSockOpt(bsd_sock->nut_tcp_sock, optname, optval, optlen); } else { /* It must be a datagram socket */ if (SO_RCVTIMEO == optname) /* <= special case, receive timeout */ { /* Yes, receive timeout, check optval and optlen */ if ((optval != NULL) && (sizeof(uint32_t) == optlen)) { /* Get requested information */ *((uint32_t*)optval) = bsd_sock->udp_rcv_timeo; rc = 0; } } else { /* Get the datagram option value */ rc = NutUdpGetSockOpt(bsd_sock->nut_udp_sock, optname, optval, optlen); } } break; } /* default */ } /* end switch (optname) */ } /* end if (SOL_SOCKET == level) */ return(rc); } /* getsockopt */ /*************************************************************************/ /* setsockopt */ /* */ /* Set value of a TCP socket option. */ /* */ /* In : sock, level, optname, optval, optlen */ /* Out : none */ /* Return: 0 on success / -1 otherwise */ /*************************************************************************/ int setsockopt (socket_t sock, int level, int optname, void *optval, int optlen) { int rc = -1; bsd_socket_t *bsd_sock = (bsd_socket_t*)sock; /* Check for the correct level */ if (SOL_SOCKET == level) { /* Check for stream or datagram */ if (SOCK_STREAM == bsd_sock->type) { /* It is a stream socket, set the option value */ rc = NutTcpSetSockOpt(bsd_sock->nut_tcp_sock, optname, optval, optlen); } else { /* Check for receive timeout */ if (SO_RCVTIMEO == optname) { /* Yes, receive timeout, check optval and optlen */ if ((optval != NULL) && (sizeof(uint32_t) == optlen)) { /* Set requested information */ bsd_sock->udp_rcv_timeo = *((uint32_t*)optval); rc = 0; } } else { /* Set the datagram option value */ rc = NutUdpSetSockOpt(bsd_sock->nut_udp_sock, optname, optval, optlen); } /* end if (SO_RCVTIMEO == optname) */ } /* end if (SOCK_STREAM == bsd_sock->type) */ } /* end if (SOL_SOCKET == level) */ /* Check for the correct level */ if (IPPROTO_IP == level) { switch (optname) { case IP_ADD_MEMBERSHIP: { struct ip_mreq *mreq; if ((optlen == sizeof(struct ip_mreq)) && (optval != NULL)) { mreq = (struct ip_mreq *)optval; rc = NutNetIfAddMcastAddr("eth0", mreq->imr_multiaddr.s_addr); } break; } /* IP_ADD_MEMBERSHIP */ case IP_DROP_MEMBERSHIP: { struct ip_mreq *mreq; if ((optlen == sizeof(struct ip_mreq)) && (optval != NULL)) { mreq = (struct ip_mreq *)optval; rc = NutNetIfDelMcastAddr("eth0", mreq->imr_multiaddr.s_addr); } break; } /* IP_DROP_MEMBERSHIP */ default: { /* Do nothing */ break; } } } /* end if (IPPROTO_IP == level) */ return(rc); } /* setsockopt */ /*** EOF ***/