error_t udpReceiveDatagram(Socket *socket, IpAddr *srcIpAddr, uint16_t *srcPort, IpAddr *destIpAddr, void *data, size_t size, size_t *received, uint_t flags) { SocketQueueItem *queueItem; //The receive queue is empty? if(!socket->receiveQueue) { //Set the events the application is interested in socket->eventMask = SOCKET_EVENT_RX_READY; //Reset the event object osResetEvent(&socket->event); //Leave critical section osReleaseMutex(&socketMutex); //Wait until an event is triggered osWaitForEvent(&socket->event, socket->timeout); //Enter critical section osAcquireMutex(&socketMutex); } //Check whether the read operation timed out if(!socket->receiveQueue) { //No data can be read *received = 0; //Report a timeout error return ERROR_TIMEOUT; } //Point to the first item in the receive queue queueItem = socket->receiveQueue; //Copy data to user buffer *received = chunkedBufferRead(data, queueItem->buffer, queueItem->offset, size); //Save the source IP address if(srcIpAddr) *srcIpAddr = queueItem->srcIpAddr; //Save the source port number if(srcPort) *srcPort = queueItem->srcPort; //Save the destination IP address if(destIpAddr) *destIpAddr = queueItem->destIpAddr; //If the SOCKET_FLAG_PEEK flag is set, the data is copied //into the buffer but is not removed from the input queue if(!(flags & SOCKET_FLAG_PEEK)) { //Remove the item from the receive queue socket->receiveQueue = queueItem->next; //Deallocate memory buffer chunkedBufferFree(queueItem->buffer); } //Update the state of events udpUpdateEvents(socket); //Successful read operation return NO_ERROR; }
error_t rawSocketReceiveEthPacket(Socket *socket, void *data, size_t size, size_t *received, uint_t flags) { SocketQueueItem *queueItem; //The SOCKET_FLAG_DONT_WAIT enables non-blocking operation if(!(flags & SOCKET_FLAG_DONT_WAIT)) { //The receive queue is empty? if(!socket->receiveQueue) { //Set the events the application is interested in socket->eventMask = SOCKET_EVENT_RX_READY; //Reset the event object osResetEvent(&socket->event); //Leave critical section osReleaseMutex(&socketMutex); //Wait until an event is triggered osWaitForEvent(&socket->event, socket->timeout); //Enter critical section osAcquireMutex(&socketMutex); } } //Check whether the read operation timed out if(!socket->receiveQueue) { //No data can be read *received = 0; //Report a timeout error return ERROR_TIMEOUT; } //Point to the first item in the receive queue queueItem = socket->receiveQueue; //Copy data to user buffer *received = netBufferRead(data, queueItem->buffer, queueItem->offset, size); //If the SOCKET_FLAG_PEEK flag is set, the data is copied //into the buffer but is not removed from the input queue if(!(flags & SOCKET_FLAG_PEEK)) { //Remove the item from the receive queue socket->receiveQueue = queueItem->next; //Deallocate memory buffer netBufferFree(queueItem->buffer); } //Update the state of events rawSocketUpdateEvents(socket); //Successful read operation return NO_ERROR; }
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; }
void httpConnectionTask(void *param) { error_t error; uint_t counter; HttpConnection *connection; //Point to the structure representing the HTTP connection connection = (HttpConnection *) param; //Endless loop while(1) { //Wait for an incoming connection attempt osWaitForEvent(&connection->startEvent, INFINITE_DELAY); //Initialize status code error = NO_ERROR; #if (HTTP_SERVER_TLS_SUPPORT == ENABLED) //Use SSL/TLS to secure the connection? if(connection->settings->useTls) { //Debug message TRACE_INFO("Initializing SSL/TLS session...\r\n"); //Start of exception handling block do { //Allocate SSL/TLS context connection->tlsContext = tlsInit(); //Initialization failed? if(connection->tlsContext == NULL) { //Report an error error = ERROR_OUT_OF_MEMORY; //Exit immediately break; } //Select server operation mode error = tlsSetConnectionEnd(connection->tlsContext, TLS_CONNECTION_END_SERVER); //Any error to report? if(error) break; //Bind TLS to the relevant socket error = tlsSetSocket(connection->tlsContext, connection->socket); //Any error to report? if(error) break; //Invoke user-defined callback, if any if(connection->settings->tlsInitCallback != NULL) { //Perform SSL/TLS related initialization error = connection->settings->tlsInitCallback(connection, connection->tlsContext); //Any error to report? if(error) break; } //Establish a secure session error = tlsConnect(connection->tlsContext); //Any error to report? if(error) break; //End of exception handling block } while(0); } else { //Do not use SSL/TLS connection->tlsContext = NULL; } #endif //Check status code if(!error) { //Process incoming requests for(counter = 0; counter < HTTP_SERVER_MAX_REQUESTS; counter++) { //Debug message TRACE_INFO("Waiting for request...\r\n"); //Clear request header memset(&connection->request, 0, sizeof(HttpRequest)); //Clear response header memset(&connection->response, 0, sizeof(HttpResponse)); //Read the HTTP request header and parse its contents error = httpReadHeader(connection); //Any error to report? if(error) { //Debug message TRACE_INFO("No HTTP request received or parsing error...\r\n"); break; } #if (HTTP_SERVER_BASIC_AUTH_SUPPORT == ENABLED || HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) //No Authorization header found? if(!connection->request.auth.found) { //Invoke user-defined callback, if any if(connection->settings->authCallback != NULL) { //Check whether the access to the specified URI is authorized connection->status = connection->settings->authCallback(connection, connection->request.auth.user, connection->request.uri); } else { //Access to the specified URI is allowed connection->status = HTTP_ACCESS_ALLOWED; } } //Check access status if(connection->status == HTTP_ACCESS_ALLOWED) { //Access to the specified URI is allowed error = NO_ERROR; } else if(connection->status == HTTP_ACCESS_BASIC_AUTH_REQUIRED) { //Basic access authentication is required connection->response.auth.mode = HTTP_AUTH_MODE_BASIC; //Report an error error = ERROR_AUTH_REQUIRED; } else if(connection->status == HTTP_ACCESS_DIGEST_AUTH_REQUIRED) { //Digest access authentication is required connection->response.auth.mode = HTTP_AUTH_MODE_DIGEST; //Report an error error = ERROR_AUTH_REQUIRED; } else { //Access to the specified URI is denied error = ERROR_NOT_FOUND; } #endif //Debug message TRACE_INFO("Sending HTTP response to the client...\r\n"); //Check status code if(!error) { //Default HTTP header fields connection->response.version = connection->request.version; connection->response.noCache = FALSE; #if (HTTP_SERVER_PERSISTENT_CONN_SUPPORT == ENABLED) //Persistent connections are accepted connection->response.keepAlive = connection->request.keepAlive; #else //Connections are not persistent by default connection->response.keepAlive = FALSE; #endif #if (HTTP_SERVER_SSI_SUPPORT == ENABLED) //Use server-side scripting to dynamically generate HTML code? if(httpCompExtension(connection->request.uri, ".stm") || httpCompExtension(connection->request.uri, ".shtm") || httpCompExtension(connection->request.uri, ".shtml")) { //SSI processing (Server Side Includes) error = ssiExecuteScript(connection, connection->request.uri, 0); } else #endif { //Send the contents of the requested page error = httpSendResponse(connection, connection->request.uri); } //The requested resource is not available? if(error == ERROR_NOT_FOUND) { //Default HTTP header fields connection->response.version = connection->request.version; connection->response.statusCode = 200; connection->response.keepAlive = connection->request.keepAlive; connection->response.noCache = FALSE; connection->response.location = NULL; connection->response.contentType = mimeGetType(connection->request.uri); connection->response.chunkedEncoding = TRUE; //Invoke user-defined callback, if any if(connection->settings->uriNotFoundCallback != NULL) { error = connection->settings->uriNotFoundCallback(connection, connection->request.uri); } } } //Bad request? if(error == ERROR_INVALID_REQUEST) { //Send an error 400 and close the connection immediately httpSendErrorResponse(connection, 400, "The request is badly formed"); } //Authorization required? else if(error == ERROR_AUTH_REQUIRED) { //Send an error 401 and keep the connection alive error = httpSendErrorResponse(connection, 401, "Authorization required"); } //Page not found? else if(error == ERROR_NOT_FOUND) { //Send an error 404 and keep the connection alive error = httpSendErrorResponse(connection, 404, "The requested page could not be found"); } //Internal error? if(error) { //Close the connection immediately break; } //Check whether the connection is persistent or not if(!connection->request.keepAlive || !connection->response.keepAlive) { //Close the connection immediately break; } } } #if (HTTP_SERVER_TLS_SUPPORT == ENABLED) //Valid SSL/TLS context? if(connection->tlsContext != NULL) { //Debug message TRACE_INFO("Closing SSL/TLS session...\r\n"); //Gracefully close SSL/TLS session tlsShutdown(connection->tlsContext); //Release context tlsFree(connection->tlsContext); } #endif //Debug message TRACE_INFO("Graceful shutdown...\r\n"); //Graceful shutdown socketShutdown(connection->socket, SOCKET_SD_BOTH); //Debug message TRACE_INFO("Closing socket...\r\n"); //Close socket socketClose(connection->socket); //Ready to serve the next connection request... connection->running = FALSE; //Release semaphore osReleaseSemaphore(&connection->serverContext->semaphore); } }
Socket *tcpAccept(Socket *socket, IpAddr *clientIpAddr, uint16_t *clientPort) { error_t error; Socket *newSocket; TcpSynQueueItem *queueItem; //Ensure the socket was previously placed in the listening state if(tcpGetState(socket) != TCP_STATE_LISTEN) return NULL; //Enter critical section osAcquireMutex(&socketMutex); //Wait for an connection attempt while(1) { //The SYN queue is empty? if(!socket->synQueue) { //Set the events the application is interested in socket->eventMask = SOCKET_EVENT_RX_READY; //Reset the event object osResetEvent(&socket->event); //Leave critical section osReleaseMutex(&socketMutex); //Wait until a SYN message is received from a client osWaitForEvent(&socket->event, socket->timeout); //Enter critical section osAcquireMutex(&socketMutex); } //Check whether the queue is still empty if(!socket->synQueue) { //Timeout error newSocket = NULL; //Exit immediately break; } //Point to the first item in the receive queue queueItem = socket->synQueue; //Return the client IP address and port number if(clientIpAddr) *clientIpAddr = queueItem->srcAddr; if(clientPort) *clientPort = queueItem->srcPort; //Leave critical section osReleaseMutex(&socketMutex); //Create a new socket to handle the incoming connection request newSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Enter critical section osAcquireMutex(&socketMutex); //Socket successfully created? if(newSocket != NULL) { //The user owns the socket newSocket->ownedFlag = TRUE; //Inherit settings from the listening socket newSocket->txBufferSize = socket->txBufferSize; newSocket->rxBufferSize = socket->rxBufferSize; //Number of chunks that comprise the TX and the RX buffers newSocket->txBuffer.maxChunkCount = arraysize(newSocket->txBuffer.chunk); newSocket->rxBuffer.maxChunkCount = arraysize(newSocket->rxBuffer.chunk); //Allocate transmit buffer error = netBufferSetLength((NetBuffer *) &newSocket->txBuffer, newSocket->txBufferSize); //Check status code if(!error) { //Allocate receive buffer error = netBufferSetLength((NetBuffer *) &newSocket->rxBuffer, newSocket->rxBufferSize); } //Transmit and receive buffers successfully allocated? if(!error) { //Bind the newly created socket to the appropriate interface newSocket->interface = queueItem->interface; //Bind the socket to the specified address newSocket->localIpAddr = queueItem->destAddr; newSocket->localPort = socket->localPort; //Save the port number and the IP address of the remote host newSocket->remoteIpAddr = queueItem->srcAddr; newSocket->remotePort = queueItem->srcPort; //Save the maximum segment size newSocket->mss = queueItem->mss; //Initialize TCP control block newSocket->iss = netGetRand(); newSocket->irs = queueItem->isn; newSocket->sndUna = newSocket->iss; newSocket->sndNxt = newSocket->iss + 1; newSocket->rcvNxt = newSocket->irs + 1; newSocket->rcvUser = 0; newSocket->rcvWnd = newSocket->rxBufferSize; //Default retransmission timeout newSocket->rto = TCP_INITIAL_RTO; #if (TCP_CONGESTION_CONTROL_SUPPORT == ENABLED) //Initial congestion window newSocket->cwnd = MIN(TCP_INITIAL_WINDOW * newSocket->mss, newSocket->txBufferSize); //Slow start threshold should be set arbitrarily high newSocket->ssthresh = UINT16_MAX; #endif //Send a SYN ACK control segment error = tcpSendSegment(newSocket, TCP_FLAG_SYN | TCP_FLAG_ACK, newSocket->iss, newSocket->rcvNxt, 0, TRUE); //TCP segment successfully sent? if(!error) { //Remove the item from the SYN queue socket->synQueue = queueItem->next; //Deallocate memory buffer memPoolFree(queueItem); //Update the state of events tcpUpdateEvents(socket); //The connection state should be changed to SYN-RECEIVED tcpChangeState(newSocket, TCP_STATE_SYN_RECEIVED); //We are done... break; } } //Dispose the socket tcpAbort(newSocket); } //Debug message TRACE_WARNING("Cannot accept TCP connection!\r\n"); //Remove the item from the SYN queue socket->synQueue = queueItem->next; //Deallocate memory buffer memPoolFree(queueItem); //Wait for the next connection attempt } //Leave critical section osReleaseMutex(&socketMutex); //Return a handle to the newly created socket return newSocket; }