Webserver+3d print
Diff: cyclone_tcp/core/socket.c
- Revision:
- 0:8918a71cdbe9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cyclone_tcp/core/socket.c Sat Feb 04 18:15:49 2017 +0000 @@ -0,0 +1,1169 @@ +/** + * @file socket.c + * @brief Socket API + * + * @section License + * + * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. + * + * This file is part of CycloneTCP Open. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * @author Oryx Embedded SARL (www.oryx-embedded.com) + * @version 1.7.6 + **/ + +//Switch to the appropriate trace level +#define TRACE_LEVEL SOCKET_TRACE_LEVEL + +//Dependencies +#include <string.h> +#include "core/net.h" +#include "core/socket.h" +#include "core/raw_socket.h" +#include "core/udp.h" +#include "core/tcp.h" +#include "core/tcp_misc.h" +#include "dns/dns_client.h" +#include "mdns/mdns_client.h" +#include "netbios/nbns_client.h" +#include "debug.h" + +//Socket table +Socket socketTable[SOCKET_MAX_COUNT]; + + +/** + * @brief Socket related initialization + * @return Error code + **/ + +error_t socketInit(void) +{ + uint_t i; + uint_t j; + + //Initialize socket descriptors + memset(socketTable, 0, sizeof(socketTable)); + + //Loop through socket descriptors + for(i = 0; i < SOCKET_MAX_COUNT; i++) + { + //Set socket identifier + socketTable[i].descriptor = i; + + //Create an event object to track socket events + if(!osCreateEvent(&socketTable[i].event)) + { + //Clean up side effects + for(j = 0; j < i; j++) + osDeleteEvent(&socketTable[j].event); + + //Report an error + return ERROR_OUT_OF_RESOURCES; + } + } + + //Successful initialization + return NO_ERROR; +} + + +/** + * @brief Create a socket (UDP or TCP) + * @param[in] type Type specification for the new socket + * @param[in] protocol Protocol to be used + * @return Handle referencing the new socket + **/ + +Socket *socketOpen(uint_t type, uint_t protocol) +{ + error_t error; + uint_t i; + uint16_t port; + Socket *socket; + OsEvent event; + + //Initialize socket handle + socket = NULL; + + //Get exclusive access + osAcquireMutex(&netMutex); + +#if (TCP_SUPPORT == ENABLED) + //Connection-oriented socket? + if(type == SOCKET_TYPE_STREAM) + { + //Always use TCP as underlying transport protocol + protocol = SOCKET_IP_PROTO_TCP; + //Get an ephemeral port number + port = tcpGetDynamicPort(); + //Continue processing + error = NO_ERROR; + } + else +#endif +#if (UDP_SUPPORT == ENABLED) + //Connectionless socket? + if(type == SOCKET_TYPE_DGRAM) + { + //Always use UDP as underlying transport protocol + protocol = SOCKET_IP_PROTO_UDP; + //Get an ephemeral port number + port = udpGetDynamicPort(); + //Continue processing + error = NO_ERROR; + } + else +#endif +#if (RAW_SOCKET_SUPPORT == ENABLED) + //Raw socket? + if(type == SOCKET_TYPE_RAW_IP || type == SOCKET_TYPE_RAW_ETH) + { + //Port numbers are not relevant for raw sockets + port = 0; + //Continue processing + error = NO_ERROR; + } + else +#endif + { + //The socket type is not supported + error = ERROR_INVALID_PARAMETER; + } + + //Check status code + if(!error) + { + //Loop through socket descriptors + for(i = 0; i < SOCKET_MAX_COUNT; i++) + { + //Unused socket found? + if(socketTable[i].type == SOCKET_TYPE_UNUSED) + { + //Save socket handle + socket = &socketTable[i]; + //We are done + break; + } + } + +#if (TCP_SUPPORT == ENABLED) + //No more sockets available? + if(socket == NULL) + { + //Kill the oldest connection in the TIME-WAIT state + //whenever the socket table runs out of space + socket = tcpKillOldestConnection(); + } +#endif + + //Check whether the current entry is free + if(socket != NULL) + { + //Save socket descriptor + i = socket->descriptor; + //Save event object instance + memcpy(&event, &socket->event, sizeof(OsEvent)); + + //Clear associated structure + memset(socket, 0, sizeof(Socket)); + //Reuse event objects and avoid recreating them whenever possible + memcpy(&socket->event, &event, sizeof(OsEvent)); + + //Save socket characteristics + socket->descriptor = i; + socket->type = type; + socket->protocol = protocol; + socket->localPort = port; + socket->timeout = INFINITE_DELAY; + +#if (TCP_SUPPORT == ENABLED) + socket->txBufferSize = MIN(TCP_DEFAULT_TX_BUFFER_SIZE, TCP_MAX_TX_BUFFER_SIZE); + socket->rxBufferSize = MIN(TCP_DEFAULT_RX_BUFFER_SIZE, TCP_MAX_RX_BUFFER_SIZE); +#endif + } + } + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Return a handle to the freshly created socket + return socket; +} + + +/** + * @brief Set timeout value for blocking operations + * @param[in] socket Handle to a socket + * @param[in] timeout Maximum time to wait + * @return Error code + **/ + +error_t socketSetTimeout(Socket *socket, systime_t timeout) +{ + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Get exclusive access + osAcquireMutex(&netMutex); + //Record timeout value + socket->timeout = timeout; + //Release exclusive access + osReleaseMutex(&netMutex); + + //No error to report + return NO_ERROR; +} + + +/** + * @brief Specify the size of the send buffer + * @param[in] socket Handle to a socket + * @param[in] size Desired buffer size in bytes + * @return Error code + **/ + +error_t socketSetTxBufferSize(Socket *socket, size_t size) +{ +#if (TCP_SUPPORT == ENABLED) + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + //Check parameter value + if(size < 1 || size > TCP_MAX_TX_BUFFER_SIZE) + return ERROR_INVALID_PARAMETER; + + //This function shall be used with connection-oriented socket types + if(socket->type != SOCKET_TYPE_STREAM) + return ERROR_INVALID_SOCKET; + //The buffer size cannot be changed when the connection is established + if(tcpGetState(socket) != TCP_STATE_CLOSED) + return ERROR_INVALID_SOCKET; + + //Use the specified buffer size + socket->txBufferSize = size; + //No error to report + return NO_ERROR; +#else + return ERROR_NOT_IMPLEMENTED; +#endif +} + + +/** + * @brief Specify the size of the receive buffer + * @param[in] socket Handle to a socket + * @param[in] size Desired buffer size in bytes + * @return Error code + **/ + +error_t socketSetRxBufferSize(Socket *socket, size_t size) +{ +#if (TCP_SUPPORT == ENABLED) + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + //Check parameter value + if(size < 1 || size > TCP_MAX_RX_BUFFER_SIZE) + return ERROR_INVALID_PARAMETER; + + //This function shall be used with connection-oriented socket types + if(socket->type != SOCKET_TYPE_STREAM) + return ERROR_INVALID_SOCKET; + //The buffer size cannot be changed when the connection is established + if(tcpGetState(socket) != TCP_STATE_CLOSED) + return ERROR_INVALID_SOCKET; + + //Use the specified buffer size + socket->rxBufferSize = size; + //No error to report + return NO_ERROR; +#else + return ERROR_NOT_IMPLEMENTED; +#endif +} + + +/** + * @brief Bind a socket to a particular network interface + * @param[in] socket Handle to a socket + * @param[in] interface Network interface to be used + * @return Error code + **/ + +error_t socketBindToInterface(Socket *socket, NetInterface *interface) +{ + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Explicitly associate the socket with the specified interface + socket->interface = interface; + + //No error to report + return NO_ERROR; +} + + +/** + * @brief Associate a local address with a socket + * @param[in] socket Handle to a socket + * @param[in] localIpAddr Local address to assign to the bound socket + * @param[in] localPort Local port number to assign to the bound socket + * @return Error code + **/ + +error_t socketBind(Socket *socket, const IpAddr *localIpAddr, uint16_t localPort) +{ + //Check input parameters + if(!socket || !localIpAddr) + return ERROR_INVALID_PARAMETER; + //Make sure the socket type is correct + if(socket->type != SOCKET_TYPE_STREAM && socket->type != SOCKET_TYPE_DGRAM) + return ERROR_INVALID_SOCKET; + + //Associate the specified IP address and port number + socket->localIpAddr = *localIpAddr; + socket->localPort = localPort; + + //No error to report + return NO_ERROR; +} + + +/** + * @brief Establish a connection to a specified socket + * @param[in] socket Handle to an unconnected socket + * @param[in] remoteIpAddr IP address of the remote host + * @param[in] remotePort Remote port number that will be used to establish the connection + * @return Error code + **/ + +error_t socketConnect(Socket *socket, const IpAddr *remoteIpAddr, uint16_t remotePort) +{ + error_t error; + + //Check input parameters + if(!socket || !remoteIpAddr) + return ERROR_INVALID_PARAMETER; + +#if (TCP_SUPPORT == ENABLED) + //Connection-oriented socket? + if(socket->type == SOCKET_TYPE_STREAM) + { + //Get exclusive access + osAcquireMutex(&netMutex); + + //Establish TCP connection + error = tcpConnect(socket, remoteIpAddr, remotePort); + + //Release exclusive access + osReleaseMutex(&netMutex); + } + else +#endif + //Connectionless socket? + if(socket->type == SOCKET_TYPE_DGRAM) + { + //Save port number and IP address of the remote host + socket->remoteIpAddr = *remoteIpAddr; + socket->remotePort = remotePort; + //No error to report + error = NO_ERROR; + } + //Raw socket? + else if(socket->type == SOCKET_TYPE_RAW_IP) + { + //Save the IP address of the remote host + socket->remoteIpAddr = *remoteIpAddr; + //No error to report + error = NO_ERROR; + } + //Socket type not supported... + else + { + //Invalid socket type + error = ERROR_INVALID_SOCKET; + } + + //Return status code + return error; +} + + +/** + * @brief Place a socket in the listening state + * + * Place a socket in a state in which it is listening for an incoming connection + * + * @param[in] socket Socket to place in the listening state + * @param[in] backlog backlog The maximum length of the pending connection queue. + * If this parameter is zero, then the default backlog value is used instead + * @return Error code + **/ + +error_t socketListen(Socket *socket, uint_t backlog) +{ +#if (TCP_SUPPORT == ENABLED) + error_t error; + + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + //This function shall be used with connection-oriented socket types + if(socket->type != SOCKET_TYPE_STREAM) + return ERROR_INVALID_SOCKET; + + //Get exclusive access + osAcquireMutex(&netMutex); + + //Start listening for an incoming connection + error = tcpListen(socket, backlog); + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Return status code + return error; +#else + return ERROR_NOT_IMPLEMENTED; +#endif +} + + +/** + * @brief Permit an incoming connection attempt on a socket + * @param[in] socket Handle to a socket previously placed in a listening state + * @param[out] clientIpAddr IP address of the client + * @param[out] clientPort Port number used by the client + * @return Handle to the socket in which the actual connection is made + **/ + +Socket *socketAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort) +{ +#if (TCP_SUPPORT == ENABLED) + Socket *newSocket; + + //Make sure the socket handle is valid + if(socket == NULL) + return NULL; + //This function shall be used with connection-oriented socket types + if(socket->type != SOCKET_TYPE_STREAM) + return NULL; + + //Accept an incoming connection attempt + newSocket = tcpAccept(socket, clientIpAddr, clientPort); + + //Return a handle to the newly created socket + return newSocket; +#else + return NULL; +#endif +} + + +/** + * @brief Send data to a connected socket + * @param[in] socket Handle that identifies a connected socket + * @param[in] data Pointer to a buffer containing the data to be transmitted + * @param[in] length Number of data bytes to send + * @param[out] written Actual number of bytes written (optional parameter) + * @param[in] flags Set of flags that influences the behavior of this function + * @return Error code + **/ + +error_t socketSend(Socket *socket, const void *data, + size_t length, size_t *written, uint_t flags) +{ + //Use default remote IP address for connectionless or raw sockets + return socketSendTo(socket, &socket->remoteIpAddr, + socket->remotePort, data, length, written, flags); +} + + +/** + * @brief Send a datagram to a specific destination + * @param[in] socket Handle that identifies a socket + * @param[in] destIpAddr IP address of the target host + * @param[in] destPort Target port number + * @param[in] data Pointer to a buffer containing the data to be transmitted + * @param[in] length Number of data bytes to send + * @param[out] written Actual number of bytes written (optional parameter) + * @param[in] flags Set of flags that influences the behavior of this function + * @return Error code + **/ + +error_t socketSendTo(Socket *socket, const IpAddr *destIpAddr, uint16_t destPort, + const void *data, size_t length, size_t *written, uint_t flags) +{ + error_t error; + + //No data has been transmitted yet + if(written) + *written = 0; + + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Get exclusive access + osAcquireMutex(&netMutex); + +#if (TCP_SUPPORT == ENABLED) + //Connection-oriented socket? + if(socket->type == SOCKET_TYPE_STREAM) + { + //For connection-oriented sockets, target address is ignored + error = tcpSend(socket, data, length, written, flags); + } + else +#endif +#if (UDP_SUPPORT == ENABLED) + //Connectionless socket? + if(socket->type == SOCKET_TYPE_DGRAM) + { + //Send UDP datagram + error = udpSendDatagram(socket, destIpAddr, + destPort, data, length, written); + } + else +#endif +#if (RAW_SOCKET_SUPPORT == ENABLED) + //Raw socket? + if(socket->type == SOCKET_TYPE_RAW_IP) + { + //Send a raw IP packet + error = rawSocketSendIpPacket(socket, destIpAddr, data, length, written); + } + else if(socket->type == SOCKET_TYPE_RAW_ETH) + { + //Send a raw Ethernet packet + error = rawSocketSendEthPacket(socket, data, length, written); + } + else +#endif + //Socket type not supported... + { + //Invalid socket type + error = ERROR_INVALID_SOCKET; + } + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Return status code + return error; +} + + +/** + * @brief Receive data from a connected socket + * @param[in] socket Handle that identifies a connected socket + * @param[out] data Buffer where to store the incoming data + * @param[in] size Maximum number of bytes that can be received + * @param[out] received Number of bytes that have been received + * @param[in] flags Set of flags that influences the behavior of this function + * @return Error code + **/ + +error_t socketReceive(Socket *socket, void *data, + size_t size, size_t *received, uint_t flags) +{ + //For connection-oriented sockets, source and destination addresses are no use + return socketReceiveEx(socket, NULL, NULL, NULL, data, size, received, flags); +} + + +/** + * @brief Receive a datagram from a connectionless socket + * @param[in] socket Handle that identifies a socket + * @param[out] srcIpAddr Source IP address (optional) + * @param[out] srcPort Source port number (optional) + * @param[out] data Buffer where to store the incoming data + * @param[in] size Maximum number of bytes that can be received + * @param[out] received Number of bytes that have been received + * @param[in] flags Set of flags that influences the behavior of this function + * @return Error code + **/ + +error_t socketReceiveFrom(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, + void *data, size_t size, size_t *received, uint_t flags) +{ + //Destination address is no use + return socketReceiveEx(socket, srcIpAddr, srcPort, NULL, data, size, received, flags); +} + + +/** + * @brief Receive a datagram + * @param[in] socket Handle that identifies a socket + * @param[out] srcIpAddr Source IP address (optional) + * @param[out] srcPort Source port number (optional) + * @param[out] destIpAddr Destination IP address (optional) + * @param[out] data Buffer where to store the incoming data + * @param[in] size Maximum number of bytes that can be received + * @param[out] received Number of bytes that have been received + * @param[in] flags Set of flags that influences the behavior of this function + * @return Error code + **/ + +error_t socketReceiveEx(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, + IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags) +{ + error_t error; + + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Get exclusive access + osAcquireMutex(&netMutex); + +#if (TCP_SUPPORT == ENABLED) + //Connection-oriented socket? + if(socket->type == SOCKET_TYPE_STREAM) + { + //Receive data + error = tcpReceive(socket, data, size, received, flags); + + //Output parameters + if(srcIpAddr) + *srcIpAddr = socket->remoteIpAddr; + if(srcPort) + *srcPort = socket->remotePort; + if(destIpAddr) + *destIpAddr = socket->localIpAddr; + } + else +#endif +#if (UDP_SUPPORT == ENABLED) + //Connectionless socket? + if(socket->type == SOCKET_TYPE_DGRAM) + { + //Receive UDP datagram + error = udpReceiveDatagram(socket, srcIpAddr, + srcPort, destIpAddr, data, size, received, flags); + } + else +#endif +#if (RAW_SOCKET_SUPPORT == ENABLED) + //Raw socket? + if(socket->type == SOCKET_TYPE_RAW_IP) + { + //Receive a raw IP packet + error = rawSocketReceiveIpPacket(socket, + srcIpAddr, destIpAddr, data, size, received, flags); + } + else if(socket->type == SOCKET_TYPE_RAW_ETH) + { + //Receive a raw Ethernet packet + error = rawSocketReceiveEthPacket(socket, data, size, received, flags); + } + else +#endif + //Socket type not supported... + { + //No data can be read + *received = 0; + //Invalid socket type + error = ERROR_INVALID_SOCKET; + } + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Return status code + return error; +} + + +/** + * @brief Retrieve the local address for a given socket + * @param[in] socket Handle that identifies a socket + * @param[out] localIpAddr Local IP address (optional) + * @param[out] localPort Local port number (optional) + * @return Error code + **/ + +error_t socketGetLocalAddr(Socket *socket, IpAddr *localIpAddr, uint16_t *localPort) +{ + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Retrieve local IP address + if(localIpAddr != NULL) + *localIpAddr = socket->localIpAddr; + + //Retrieve local port number + if(localPort != NULL) + *localPort = socket->localPort; + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Retrieve the address of the peer to which a socket is connected + * @param[in] socket Handle that identifies a socket + * @param[out] remoteIpAddr IP address of the remote host (optional) + * @param[out] remotePort Remote port number (optional) + * @return Error code + **/ + +error_t socketGetRemoteAddr(Socket *socket, IpAddr *remoteIpAddr, uint16_t *remotePort) +{ + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Retrieve local IP address + if(remoteIpAddr != NULL) + *remoteIpAddr = socket->remoteIpAddr; + + //Retrieve local port number + if(remotePort != NULL) + *remotePort = socket->remotePort; + + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Disable reception, transmission, or both + * + * Note that socketShutdown() does not close the socket, and resources attached + * to the socket will not be freed until socketClose() is invoked + * + * @param[in] socket Handle to a socket + * @param[in] how Flag that describes what types of operation will no longer be allowed + * @return Error code + **/ + +error_t socketShutdown(Socket *socket, uint_t how) +{ +#if (TCP_SUPPORT == ENABLED) + error_t error; + + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + //Make sure the socket type is correct + if(socket->type != SOCKET_TYPE_STREAM) + return ERROR_INVALID_SOCKET; + //Check flags + if((how != SOCKET_SD_SEND) && (how != SOCKET_SD_RECEIVE) && (how != SOCKET_SD_BOTH)) + return ERROR_INVALID_PARAMETER; + + //Get exclusive access + osAcquireMutex(&netMutex); + + //Graceful shutdown + error = tcpShutdown(socket, how); + + //Release exclusive access + osReleaseMutex(&netMutex); + + //Return status code + return error; +#else + return ERROR_NOT_IMPLEMENTED; +#endif +} + + +/** + * @brief Close an existing socket + * @param[in] socket Handle identifying the socket to close + **/ + +void socketClose(Socket *socket) +{ + //Make sure the socket handle is valid + if(socket == NULL) + return; + + //Get exclusive access + osAcquireMutex(&netMutex); + +#if (TCP_SUPPORT == ENABLED) + //Connection-oriented socket? + if(socket->type == SOCKET_TYPE_STREAM) + { + //Abort the current TCP connection + tcpAbort(socket); + } +#endif +#if (UDP_SUPPORT == ENABLED || RAW_SOCKET_SUPPORT == ENABLED) + //Connectionless socket or raw socket? + if(socket->type == SOCKET_TYPE_DGRAM || + socket->type == SOCKET_TYPE_RAW_IP || + socket->type == SOCKET_TYPE_RAW_ETH) + { + //Point to the first item in the receive queue + SocketQueueItem *queueItem = socket->receiveQueue; + + //Purge the receive queue + while(queueItem) + { + //Keep track of the next item in the queue + SocketQueueItem *nextQueueItem = queueItem->next; + //Free previously allocated memory + memPoolFree(queueItem->buffer); + //Point to the next item + queueItem = nextQueueItem; + } + + //Mark the socket as closed + socket->type = SOCKET_TYPE_UNUSED; + } +#endif + + //Release exclusive access + osReleaseMutex(&netMutex); +} + + +/** + * @brief Wait for one of a set of sockets to become ready to perform I/O + * + * The socketPoll function determines the status of one or more sockets, + * waiting if necessary, to perform synchronous I/O + * + * @param[in,out] eventDesc Set of entries specifying the events the user is interested in + * @param[in] size Number of entries in the descriptor set + * @param[in] extEvent External event that can abort the wait if necessary (optional) + * @param[in] timeout Maximum time to wait before returning + * @return Error code + **/ + +error_t socketPoll(SocketEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout) +{ + uint_t i; + bool_t status; + OsEvent *event; + OsEvent eventObject; + + //Check parameters + if(!eventDesc || !size) + return ERROR_INVALID_PARAMETER; + + //Try to use the supplied event object to receive notifications + if(!extEvent) + { + //Create an event object only if necessary + if(!osCreateEvent(&eventObject)) + { + //Report an error + return ERROR_OUT_OF_RESOURCES; + } + + //Reference to the newly created event + event = &eventObject; + } + else + { + //Reference to the external event + event = extEvent; + } + + //Loop through descriptors + for(i = 0; i < size; i++) + { + //Clear event flags + eventDesc[i].eventFlags = 0; + //Subscribe to the requested events + socketRegisterEvents(eventDesc[i].socket, event, eventDesc[i].eventMask); + } + + //Block the current task until an event occurs + status = osWaitForEvent(event, timeout); + + //Any socket event is in the signaled state? + if(status) + { + //Loop through descriptors + for(i = 0; i < size; i++) + { + //Retrieve event flags for the current socket + socketGetEvents(eventDesc[i].socket, &eventDesc[i].eventFlags); + //Clear unnecessary flags + eventDesc[i].eventFlags &= eventDesc[i].eventMask; + } + } + + //Unsubscribe previously registered events + for(i = 0; i < size; i++) + socketUnregisterEvents(eventDesc[i].socket); + + //Reset event object before exiting... + osResetEvent(event); + + //Release previously allocated resources + if(!extEvent) + osDeleteEvent(&eventObject); + + //Return status code + return status ? NO_ERROR : ERROR_TIMEOUT; +} + + +/** + * @brief Subscribe to the specified socket events + * @param[in] socket Handle that identifies a socket + * @param[in] event Event object used to receive notifications + * @param[in] eventMask Logic OR of the requested socket events + * @return Error code + **/ + +error_t socketRegisterEvents(Socket *socket, OsEvent *event, uint_t eventMask) +{ + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Get exclusive access + osAcquireMutex(&netMutex); + + //An user event may have been previously registered... + if(socket->userEvent != NULL) + socket->eventMask |= eventMask; + else + socket->eventMask = eventMask; + + //Suscribe to get notified of events + socket->userEvent = event; + +#if (TCP_SUPPORT == ENABLED) + //Handle TCP specific events + if(socket->type == SOCKET_TYPE_STREAM) + { + tcpUpdateEvents(socket); + } +#endif +#if (UDP_SUPPORT == ENABLED) + //Handle UDP specific events + if(socket->type == SOCKET_TYPE_DGRAM) + { + udpUpdateEvents(socket); + } +#endif +#if (RAW_SOCKET_SUPPORT == ENABLED) + //Handle events that are specific to raw sockets + if(socket->type == SOCKET_TYPE_RAW_IP || + socket->type == SOCKET_TYPE_RAW_ETH) + { + rawSocketUpdateEvents(socket); + } +#endif + + //Release exclusive access + osReleaseMutex(&netMutex); + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Unsubscribe previously registered events + * @param[in] socket Handle that identifies a socket + * @return Error code + **/ + +error_t socketUnregisterEvents(Socket *socket) +{ + //Make sure the socket handle is valid + if(socket == NULL) + return ERROR_INVALID_PARAMETER; + + //Get exclusive access + osAcquireMutex(&netMutex); + + //Unsuscribe socket events + socket->userEvent = NULL; + + //Release exclusive access + osReleaseMutex(&netMutex); + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Retrieve event flags for a specified socket + * @param[in] socket Handle that identifies a socket + * @param[out] eventFlags Logic OR of events in the signaled state + * @return Error code + **/ + +error_t socketGetEvents(Socket *socket, uint_t *eventFlags) +{ + //Make sure the socket handle is valid + if(socket == NULL) + { + //Always return a valid value + *eventFlags = 0; + //Report an error + return ERROR_INVALID_PARAMETER; + } + + //Get exclusive access + osAcquireMutex(&netMutex); + + //Read event flags for the specified socket + *eventFlags = socket->eventFlags; + + //Release exclusive access + osReleaseMutex(&netMutex); + //Successful processing + return NO_ERROR; +} + + +/** + * @brief Resolve a host name into an IP address + * @param[in] interface Underlying network interface (optional parameter) + * @param[in] name Name of the host to be resolved + * @param[out] ipAddr IP address corresponding to the specified host name + * @param[in] flags Set of flags that influences the behavior of this function + * @return Error code + **/ + +error_t getHostByName(NetInterface *interface, + const char_t *name, IpAddr *ipAddr, uint_t flags) +{ + error_t error; + + //Default address type depends on TCP/IP stack configuration +#if (IPV4_SUPPORT == ENABLED) + HostType type = HOST_TYPE_IPV4; +#elif (IPV6_SUPPORT == ENABLED) + HostType type = HOST_TYPE_IPV6; +#else + HostType type = HOST_TYPE_ANY; +#endif + + //Default name resolution protocol depends on TCP/IP stack configuration +#if (DNS_CLIENT_SUPPORT == ENABLED) + HostnameResolver protocol = HOST_NAME_RESOLVER_DNS; +#elif (MDNS_CLIENT_SUPPORT == ENABLED) + HostnameResolver protocol = HOST_NAME_RESOLVER_MDNS; +#elif (NBNS_CLIENT_SUPPORT == ENABLED) + HostnameResolver protocol = HOST_NAME_RESOLVER_NBNS; +#else + HostnameResolver protocol = HOST_NAME_RESOLVER_ANY; +#endif + + //Check parameters + if(name == NULL || ipAddr == NULL) + return ERROR_INVALID_PARAMETER; + + //Use default network interface? + if(interface == NULL) + interface = netGetDefaultInterface(); + + //The specified name can be either an IP or a host name + error = ipStringToAddr(name, ipAddr); + + //Perform name resolution if necessary + if(error) + { + //The user may provide a hint to choose between IPv4 and IPv6 + if(flags & HOST_TYPE_IPV4) + type = HOST_TYPE_IPV4; + else if(flags & HOST_TYPE_IPV6) + type = HOST_TYPE_IPV6; + + //The user may provide a hint to to select the desired protocol to be used + if(flags & HOST_NAME_RESOLVER_DNS) + { + //Use DNS to resolve the specified host name + protocol = HOST_NAME_RESOLVER_DNS; + } + else if(flags & HOST_NAME_RESOLVER_MDNS) + { + //Use mDNS to resolve the specified host name + protocol = HOST_NAME_RESOLVER_MDNS; + } + else if(flags & HOST_NAME_RESOLVER_NBNS) + { + //Use NBNS to resolve the specified host name + protocol = HOST_NAME_RESOLVER_NBNS; + } + else + { + //Retrieve the length of the host name to be resolved + size_t n = strlen(name); + + //Select the most suitable protocol + if(n >= 6 && !strcasecmp(name + n - 6, ".local")) + { +#if (MDNS_CLIENT_SUPPORT == ENABLED) + //Use mDNS to resolve the specified host name + protocol = HOST_NAME_RESOLVER_MDNS; +#endif + } + else if(n <= 15 && !strchr(name, '.') && type == HOST_TYPE_IPV4) + { +#if (NBNS_CLIENT_SUPPORT == ENABLED) + //Use NetBIOS Name Service to resolve the specified host name + protocol = HOST_NAME_RESOLVER_NBNS; +#endif + } + } + +#if (DNS_CLIENT_SUPPORT == ENABLED) + //Use DNS protocol? + if(protocol == HOST_NAME_RESOLVER_DNS) + { + //Perform host name resolution + error = dnsResolve(interface, name, type, ipAddr); + } + else +#endif +#if (MDNS_CLIENT_SUPPORT == ENABLED) + //Use mDNS protocol? + if(protocol == HOST_NAME_RESOLVER_MDNS) + { + //Perform host name resolution + error = mdnsClientResolve(interface, name, type, ipAddr); + } + else +#endif +#if (NBNS_CLIENT_SUPPORT == ENABLED && IPV4_SUPPORT == ENABLED) + //Use NetBIOS Name Service protocol? + if(protocol == HOST_NAME_RESOLVER_NBNS) + { + //Perform host name resolution + error = nbnsResolve(interface, name, ipAddr); + } + else +#endif + //Invalid protocol? + { + //Report an error + error = ERROR_INVALID_PARAMETER; + } + } + + //Return status code + return error; +} +