error_t sntpWaitForResponse(SntpClientContext *context, systime_t timeout) { error_t error; size_t length; systime_t elapsedTime; //Time elapsed since the NTP request was sent elapsedTime = 0; //Keep listening as long as the retransmission timeout has not been reached while(elapsedTime < timeout) { //Adjust receive timeout error = socketSetTimeout(context->socket, timeout - elapsedTime); //Any error to report? if(error) break; //Wait for a response from the NTP server error = socketReceive(context->socket, &context->message, sizeof(NtpHeader), &length, 0); //Any datagram received? if(!error) { //Time at which the response was received context->t4 = osGetSystemTime(); //Parse incoming datagram error = sntpParseResponse(context, &context->message, length); //Valid NTP response message? if(!error) return NO_ERROR; } //Compute the time elapsed since the NTP request was sent elapsedTime = osGetSystemTime() - context->t1; } //The timeout period elapsed return ERROR_TIMEOUT; }
void tcpEchoConnectionTask(void *param) { error_t error; uint_t n; uint_t writeIndex; uint_t readIndex; uint_t bufferLength; uint_t rxByteCount; uint_t txByteCount; time_t startTime; time_t duration; SocketEventDesc eventDesc; EchoServiceContext *context; //Get a pointer to the context context = (EchoServiceContext *) param; //Get current time startTime = osGetTickCount(); //Initialize variables writeIndex = 0; readIndex = 0; bufferLength = 0; rxByteCount = 0; txByteCount = 0; //Main loop while(1) { //Buffer is empty? if(!bufferLength) { //Get notified when the socket is readable eventDesc.socket = context->socket; eventDesc.eventMask = SOCKET_EVENT_RX_READY; } //Buffer is not empty of full? else if(bufferLength < ECHO_BUFFER_SIZE) { //Get notified when the socket is readable or writable eventDesc.socket = context->socket; eventDesc.eventMask = SOCKET_EVENT_RX_READY | SOCKET_EVENT_TX_READY; } //Buffer is full? else { //Get notified when the socket is writable eventDesc.socket = context->socket; eventDesc.eventMask = SOCKET_EVENT_TX_READY; } //Wait for an event to be fired error = socketPoll(&eventDesc, 1, NULL, ECHO_TIMEOUT); //Timeout error or any other exception to report? if(error) break; //The socket is available for reading if(eventDesc.eventFlags & SOCKET_EVENT_RX_READY) { //Read as much data as possible n = min(ECHO_BUFFER_SIZE - writeIndex, ECHO_BUFFER_SIZE - bufferLength); //Read incoming data error = socketReceive(context->socket, context->buffer + writeIndex, n, &n, 0); //Any error to report? if(error) break; //Increment write index writeIndex += n; //Wrap around if necessary if(writeIndex >= ECHO_BUFFER_SIZE) writeIndex = 0; //Increment buffer length bufferLength += n; //Total number of bytes received rxByteCount += n; } //The socket is available for writing? if(eventDesc.eventFlags & SOCKET_EVENT_TX_READY) { //Write as much data as possible n = min(ECHO_BUFFER_SIZE - readIndex, bufferLength); //Send data back to the client error = socketSend(context->socket, context->buffer + readIndex, n, &n, 0); //Any error to report? if(error && error != ERROR_TIMEOUT) break; //Increment read index readIndex += n; //Wrap around if necessary if(readIndex >= ECHO_BUFFER_SIZE) readIndex = 0; //Update buffer length bufferLength -= n; //Total number of bytes sent txByteCount += n; } } //Adjust timeout value socketSetTimeout(context->socket, ECHO_TIMEOUT); //Graceful shutdown socketShutdown(context->socket, SOCKET_SD_BOTH); //Compute total duration duration = osGetTickCount() - startTime; //Debug message TRACE_INFO("Echo service: %u bytes received, %u bytes sent in %lu ms\r\n", rxByteCount, txByteCount, duration); //Close socket socketClose(context->socket); //Release previously allocated memory osMemFree(context); //Kill ourselves osTaskDelete(NULL); }
void tcpEchoListenerTask(void *param) { error_t error; uint16_t clientPort; IpAddr clientIpAddr; Socket *serverSocket; Socket *clientSocket; EchoServiceContext *context; OsTask *task; //Point to the listening socket serverSocket = (Socket *) param; //Main loop while(1) { //Accept an incoming connection clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort); //Check whether a valid connection request has been received if(!clientSocket) continue; //Debug message TRACE_INFO("Echo service: connection established with client %s port %u\r\n", ipAddrToString(&clientIpAddr, NULL), clientPort); //The socket operates in non-blocking mode error = socketSetTimeout(clientSocket, 0); //Any error to report? if(error) { //Close socket socketClose(clientSocket); //Wait for an incoming connection attempt continue; } //Allocate resources for the new connection context = osMemAlloc(sizeof(EchoServiceContext)); //Failed to allocate memory? if(!context) { //Close socket socketClose(clientSocket); //Wait for an incoming connection attempt continue; } //Record the handle of the newly created socket context->socket = clientSocket; //Create a task to service the current connection task = osTaskCreate("TCP Echo Connection", tcpEchoConnectionTask, context, ECHO_SERVICE_STACK_SIZE, ECHO_SERVICE_PRIORITY); //Did we encounter an error? if(task == OS_INVALID_HANDLE) { //Close socket socketClose(clientSocket); //Release resources osMemFree(context); } } }
void httpListenerTask(void *param) { uint_t i; uint_t counter; uint16_t clientPort; IpAddr clientIpAddr; HttpServerContext *context; HttpConnection* connection; Socket *socket; //Retrieve the HTTP server context context = (HttpServerContext *) param; //Process incoming connections to the server for(counter = 1; ; counter++) { //Debug message TRACE_INFO("Ready to accept a new connection...\r\n"); //Limit the number of simultaneous connections to the HTTP server osWaitForSemaphore(&context->semaphore, INFINITE_DELAY); //Loop through available client connections for(i = 0; i < context->settings.maxConnections; i++) { //Point to the current connection connection = &context->connections[i]; //Ready to service the client request? if(!connection->running) { //Accept an incoming connection socket = socketAccept(context->socket, &clientIpAddr, &clientPort); //Make sure the socket handle is valid if(socket != NULL) { //Debug message TRACE_INFO("Connection #%u established with client %s port %" PRIu16 "...\r\n", counter, ipAddrToString(&clientIpAddr, NULL), clientPort); //Reference to the HTTP server settings connection->settings = &context->settings; //Reference to the HTTP server context connection->serverContext = context; //Reference to the new socket connection->socket = socket; //Set timeout for blocking functions socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT); //The client connection task is now running... connection->running = TRUE; //Service the current connection request osSetEvent(&connection->startEvent); //We are done break; } } } } }
error_t httpServerInit(HttpServerContext *context, const HttpServerSettings *settings) { error_t error; uint_t i; //Debug message TRACE_INFO("Initializing HTTP server...\r\n"); //Ensure the parameters are valid if(context == NULL || settings == NULL) return ERROR_INVALID_PARAMETER; //Check user settings if(settings->maxConnections == 0 || settings->connections == NULL) return ERROR_INVALID_PARAMETER; //Clear the HTTP server context memset(context, 0, sizeof(HttpServerContext)); //Save user settings context->settings = *settings; //Client connections context->connections = settings->connections; //Create a semaphore to limit the number of simultaneous connections if(!osCreateSemaphore(&context->semaphore, context->settings.maxConnections)) return ERROR_OUT_OF_RESOURCES; //Loop through client connections for(i = 0; i < context->settings.maxConnections; i++) { //Create an event object to manage connection lifetime if(!osCreateEvent(&context->connections[i].startEvent)) return ERROR_OUT_OF_RESOURCES; } #if (HTTP_SERVER_DIGEST_AUTH_SUPPORT == ENABLED) //Create a mutex to prevent simultaneous access to the nonce cache if(!osCreateMutex(&context->nonceCacheMutex)) return ERROR_OUT_OF_RESOURCES; #endif //Open a TCP socket context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Failed to open socket? if(!context->socket) return ERROR_OPEN_FAILED; //Set timeout for blocking functions error = socketSetTimeout(context->socket, INFINITE_DELAY); //Any error to report? if(error) return error; //Associate the socket with the relevant interface error = socketBindToInterface(context->socket, settings->interface); //Unable to bind the socket to the desired interface? if(error) return error; //Bind newly created socket to port 80 error = socketBind(context->socket, &IP_ADDR_ANY, settings->port); //Failed to bind socket to port 80? if(error) return error; //Place socket in listening state error = socketListen(context->socket, settings->backlog); //Any failure to report? if(error) return error; //Successful initialization return NO_ERROR; }
error_t dnsResolve(NetInterface *interface, const char_t *name, IpAddr *ipAddr) { error_t error; uint_t i; size_t length; uint16_t identifier; IpAddr serverIpAddr; Socket *socket; DnsHeader *dnsMessage; //Debug message TRACE_INFO("Trying to resolve %s...\r\n", name); //Use default network interface? if(!interface) interface = tcpIpStackGetDefaultInterface(); //Allocate a memory buffer to hold DNS messages dnsMessage = memPoolAlloc(DNS_MESSAGE_MAX_SIZE); //Failed to allocate memory? if(!dnsMessage) return ERROR_OUT_OF_MEMORY; //Open a UDP socket socket = socketOpen(SOCKET_TYPE_DGRAM, SOCKET_PROTOCOL_UDP); //Failed to open socket? if(!socket) { //Free previously allocated memory osMemFree(dnsMessage); //Return status code return ERROR_OPEN_FAILED; } #if (IPV4_SUPPORT == ENABLED) //IP address of the DNS server serverIpAddr.length = sizeof(Ipv4Addr); serverIpAddr.ipv4Addr = interface->ipv4Config.dnsServer[0]; #elif (IPV6_SUPPORT == ENABLED) //IP address of the DNS server serverIpAddr.length = sizeof(Ipv6Addr); serverIpAddr.ipv6Addr = interface->ipv6Config.dnsServer[0]; #endif //Associate the socket with the relevant interface error = socketBindToInterface(socket, interface); //Any error to report? if(error) { //Free previously allocated memory osMemFree(dnsMessage); //Close socket socketClose(socket); //Return status code return error; } //Connect the newly created socket to the primary DNS server error = socketConnect(socket, &serverIpAddr, DNS_PORT); //Failed to connect? if(error) { //Free previously allocated memory osMemFree(dnsMessage); //Close socket socketClose(socket); //Return status code return error; } //An identifier is used by the client to match replies //with corresponding requests identifier = rand(); //Try to retransmit the DNS message if the previous query timed out for(i = 0; i < DNS_MAX_RETRIES; i++) { //Send DNS query message error = dnsSendQuery(socket, dnsMessage, identifier, name); //Failed to send message ? if(error) break; //Adjust receive timeout error = socketSetTimeout(socket, DNS_REQUEST_TIMEOUT); //Any error to report? if(error) break; //Wait for the server response error = socketReceive(socket, dnsMessage, DNS_MESSAGE_MAX_SIZE, &length, 0); //Any response from the specified DNS server? if(!error) { //Parse DNS response error = dnsParseResponse(dnsMessage, length, identifier, ipAddr); //DNS response successfully decoded? if(!error) break; } } //The maximum number of retransmissions has been reached? if(i >= DNS_MAX_RETRIES) error = ERROR_TIMEOUT; //Free previously allocated memory osMemFree(dnsMessage); //Close socket socketClose(socket); //Debug message if(!error) { //Name resolution succeeds TRACE_INFO("Host name resolved to %s...\r\n", ipAddrToString(ipAddr, NULL)); } else { //Report an error TRACE_ERROR("DNS resolution failed!\r\n"); } //Return status code return error; }
error_t smtpSendMail(const SmtpAuthInfo *authInfo, const SmtpMail *mail) { error_t error; uint_t i; uint_t replyCode; IpAddr serverIpAddr; SmtpClientContext *context; //Check parameters if(!authInfo || !mail) return ERROR_INVALID_PARAMETER; //Make sure the server name is valid if(!authInfo->serverName) return ERROR_INVALID_PARAMETER; //Debug message TRACE_INFO("Sending a mail to %s port %" PRIu16 "...\r\n", authInfo->serverName, authInfo->serverPort); //The specified SMTP server can be either an IP or a host name error = getHostByName(authInfo->interface, authInfo->serverName, &serverIpAddr, 0); //Unable to resolve server name? if(error) return ERROR_NAME_RESOLUTION_FAILED; //Allocate a memory buffer to hold the SMTP client context context = osAllocMem(sizeof(SmtpClientContext)); //Failed to allocate memory? if(!context) return ERROR_OUT_OF_MEMORY; //Open a TCP socket context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Failed to open socket? if(!context->socket) { //Free previously allocated resources osFreeMem(context); //Report an error return ERROR_OPEN_FAILED; } #if (SMTP_TLS_SUPPORT == ENABLED) //Do not use SSL/TLS for the moment context->tlsContext = NULL; #endif //Start of exception handling block do { //Bind the socket to a particular network interface? if(authInfo->interface) { //Associate the socket with the relevant interface error = socketBindToInterface(context->socket, authInfo->interface); //Any error to report? if(error) break; } //Set timeout for blocking operations error = socketSetTimeout(context->socket, SMTP_DEFAULT_TIMEOUT); //Any error to report? if(error) break; //Connect to the SMTP server error = socketConnect(context->socket, &serverIpAddr, authInfo->serverPort); //Connection to server failed? if(error) break; #if (SMTP_TLS_SUPPORT == ENABLED) //Open a secure SSL/TLS session? if(authInfo->useTls) { //Initialize TLS context context->tlsContext = tlsInit(); //Initialization failed? if(!context->tlsContext) { //Unable to allocate memory error = ERROR_OUT_OF_MEMORY; //Stop immediately break; } //Bind TLS to the relevant socket error = tlsSetSocket(context->tlsContext, context->socket); //Any error to report? if(error) break; //Select client operation mode error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT); //Any error to report? if(error) break; //Set the PRNG algorithm to be used error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext); //Any error to report? if(error) break; //Perform TLS handshake error = tlsConnect(context->tlsContext); //Failed to established a TLS session? if(error) break; } #endif //Wait for the connection greeting reply error = smtpSendCommand(context, NULL, &replyCode, NULL); //Any communication error to report? if(error) break; //Check whether the greeting message was properly received if(!SMTP_REPLY_CODE_2YZ(replyCode)) { //An unexpected response was received... error = ERROR_UNEXPECTED_RESPONSE; //Stop immediately break; } //Clear security features context->authLoginSupported = FALSE; context->authPlainSupported = FALSE; context->authCramMd5Supported = FALSE; context->startTlsSupported = FALSE; //Send EHLO command and parse server response error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n", &replyCode, smtpEhloReplyCallback); //Any communication error to report? if(error) break; //Check SMTP response code if(!SMTP_REPLY_CODE_2YZ(replyCode)) { //An unexpected response was received... error = ERROR_UNEXPECTED_RESPONSE; //Stop immediately break; } #if (SMTP_TLS_SUPPORT == ENABLED) //Check whether the STARTTLS command is supported if(context->startTlsSupported && !context->tlsContext) { //Send STARTTLS command error = smtpSendCommand(context, "STARTTLS\r\n", &replyCode, NULL); //Any communication error to report? if(error) break; //Check SMTP response code if(!SMTP_REPLY_CODE_2YZ(replyCode)) { //An unexpected response was received... error = ERROR_UNEXPECTED_RESPONSE; //Stop immediately break; } //Initialize TLS context context->tlsContext = tlsInit(); //Initialization failed? if(!context->tlsContext) { //Unable to allocate memory error = ERROR_OUT_OF_MEMORY; //Stop immediately break; } //Bind TLS to the relevant socket error = tlsSetSocket(context->tlsContext, context->socket); //Any error to report? if(error) break; //Select client operation mode error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT); //Any error to report? if(error) break; //Set the PRNG algorithm to be used error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext); //Any error to report? if(error) break; //Perform TLS handshake error = tlsConnect(context->tlsContext); //Failed to established a TLS session? if(error) break; //Clear security features context->authLoginSupported = FALSE; context->authPlainSupported = FALSE; context->authCramMd5Supported = FALSE; //Send EHLO command and parse server response error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n", &replyCode, smtpEhloReplyCallback); //Any communication error to report? if(error) break; //Check SMTP response code if(!SMTP_REPLY_CODE_2YZ(replyCode)) { //An unexpected response was received... error = ERROR_UNEXPECTED_RESPONSE; //Stop immediately break; } } #endif //Authentication requires a valid user name and password if(authInfo->userName && authInfo->password) { #if (SMTP_LOGIN_AUTH_SUPPORT == ENABLED) //LOGIN authentication mechanism supported? if(context->authLoginSupported) { //Perform LOGIN authentication error = smtpSendAuthLogin(context, authInfo); //Authentication failed? if(error) break; } else #endif #if (SMTP_PLAIN_AUTH_SUPPORT == ENABLED) //PLAIN authentication mechanism supported? if(context->authPlainSupported) { //Perform PLAIN authentication error = smtpSendAuthPlain(context, authInfo); //Authentication failed? if(error) break; } else #endif #if (SMTP_CRAM_MD5_AUTH_SUPPORT == ENABLED) //CRAM-MD5 authentication mechanism supported? if(context->authCramMd5Supported) { //Perform CRAM-MD5 authentication error = smtpSendAuthCramMd5(context, authInfo); //Authentication failed? if(error) break; } else #endif //No authentication mechanism supported? { //Skip authentication step } } //Format the MAIL FROM command (a null return path must be accepted) if(mail->from.addr) sprintf(context->buffer, "MAIL FROM:<%s>\r\n", mail->from.addr); else strcpy(context->buffer, "MAIL FROM:<>\r\n"); //Send the command to the server error = smtpSendCommand(context, context->buffer, &replyCode, NULL); //Any communication error to report? if(error) break; //Check SMTP response code if(!SMTP_REPLY_CODE_2YZ(replyCode)) { //An unexpected response was received... error = ERROR_UNEXPECTED_RESPONSE; //Stop immediately break; } //Format the RCPT TO command for(i = 0; i < mail->recipientCount; i++) { //Skip recipient addresses that are not valid if(!mail->recipients[i].addr) continue; //Format the RCPT TO command sprintf(context->buffer, "RCPT TO:<%s>\r\n", mail->recipients[i].addr); //Send the command to the server error = smtpSendCommand(context, context->buffer, &replyCode, NULL); //Any communication error to report? if(error) break; //Check SMTP response code if(!SMTP_REPLY_CODE_2YZ(replyCode)) { //An unexpected response was received... error = ERROR_UNEXPECTED_RESPONSE; //Stop immediately break; } } //Propagate exception if necessary if(error) break; //Send message body error = smtpSendData(context, mail); //Any error to report? if(error) break; //End of exception handling block } while(0); //Check status code if(error == NO_ERROR || error == ERROR_UNEXPECTED_RESPONSE || error == ERROR_AUTHENTICATION_FAILED) { //Properly disconnect from the SMTP server smtpSendCommand(context, "QUIT\r\n", &replyCode, NULL); } #if (SMTP_TLS_SUPPORT == ENABLED) //Gracefully close SSL/TLS session if(context->tlsContext != NULL) tlsFree(context->tlsContext); #endif //Close socket socketClose(context->socket); //Clean up previously allocated resources osFreeMem(context); //Return status code return error; }
error_t httpReadHeader(HttpConnection *connection) { error_t error; size_t length; char_t *token; char_t *p; char_t *s; //Set the maximum time the server will wait for an HTTP //request before closing the connection error = socketSetTimeout(connection->socket, HTTP_SERVER_IDLE_TIMEOUT); //Any error to report? if(error) return error; //Read the first line of the request error = httpReceive(connection, connection->buffer, HTTP_SERVER_BUFFER_SIZE - 1, &length, SOCKET_FLAG_BREAK_CRLF); //Unable to read any data? if(error) return error; //Revert to default timeout error = socketSetTimeout(connection->socket, HTTP_SERVER_TIMEOUT); //Any error to report? if(error) return error; //Properly terminate the string with a NULL character connection->buffer[length] = '\0'; //Debug message TRACE_INFO("%s", connection->buffer); //The Request-Line begins with a method token token = strtok_r(connection->buffer, " \r\n", &p); //Unable to retrieve the method? if(!token) return ERROR_INVALID_REQUEST; //The Method token indicates the method to be performed on the //resource identified by the Request-URI error = strSafeCopy(connection->request.method, token, HTTP_SERVER_METHOD_MAX_LEN); //Any error to report? if(error) return ERROR_INVALID_REQUEST; //The Request-URI is following the method token token = strtok_r(NULL, " \r\n", &p); //Unable to retrieve the Request-URI? if(!token) return ERROR_INVALID_REQUEST; //Check whether a query string is present s = strchr(token, '?'); //Query string found? if(s != NULL) { //Split the string *s = '\0'; //Save the Request-URI error = httpDecodePercentEncodedString(token, connection->request.uri, HTTP_SERVER_URI_MAX_LEN); //Any error to report? if(error) return ERROR_INVALID_REQUEST; //Check the length of the query string if(strlen(s + 1) > HTTP_SERVER_QUERY_STRING_MAX_LEN) return ERROR_INVALID_REQUEST; //Save the query string strcpy(connection->request.queryString, s + 1); } else { //Save the Request-URI error = httpDecodePercentEncodedString(token, connection->request.uri, HTTP_SERVER_URI_MAX_LEN); //Any error to report? if(error) return ERROR_INVALID_REQUEST; //No query string connection->request.queryString[0] = '\0'; } //Redirect to the default home page if necessary if(!strcasecmp(connection->request.uri, "/")) strcpy(connection->request.uri, connection->settings->defaultDocument); //Clean the resulting path pathCanonicalize(connection->request.uri); //The protocol version is following the Request-URI token = strtok_r(NULL, " \r\n", &p); //HTTP version 0.9? if(!token) { //Save version number connection->request.version = HTTP_VERSION_0_9; //Persistent connections are not supported connection->request.keepAlive = FALSE; } //HTTP version 1.0? else if(!strcasecmp(token, "HTTP/1.0")) { //Save version number connection->request.version = HTTP_VERSION_1_0; //By default connections are not persistent connection->request.keepAlive = FALSE; } //HTTP version 1.1? else if(!strcasecmp(token, "HTTP/1.1")) { //Save version number connection->request.version = HTTP_VERSION_1_1; //HTTP 1.1 makes persistent connections the default connection->request.keepAlive = TRUE; } //HTTP version not supported? else { return ERROR_INVALID_REQUEST; } //Default value for properties connection->request.chunkedEncoding = FALSE; connection->request.contentLength = 0; //HTTP 0.9 does not support Full-Request if(connection->request.version >= HTTP_VERSION_1_0) { //Local variables char_t firstChar; char_t *separator; char_t *name; char_t *value; //This variable is used to decode header fields that span multiple lines firstChar = '\0'; //Parse the header fields of the HTTP request while(1) { //Decode multiple-line header field error = httpReadHeaderField(connection, connection->buffer, HTTP_SERVER_BUFFER_SIZE, &firstChar); //Any error to report? if(error) return error; //Debug message TRACE_DEBUG("%s", connection->buffer); //An empty line indicates the end of the header fields if(!strcmp(connection->buffer, "\r\n")) break; //Check whether a separator is present separator = strchr(connection->buffer, ':'); //Separator found? if(separator != NULL) { //Split the line *separator = '\0'; //Get field name and value name = strTrimWhitespace(connection->buffer); value = strTrimWhitespace(separator + 1); //Connection field found? if(!strcasecmp(name, "Connection")) { //Check whether persistent connections are supported or not if(!strcasecmp(value, "keep-alive")) connection->request.keepAlive = TRUE; else if(!strcasecmp(value, "close")) connection->request.keepAlive = FALSE; } //Transfer-Encoding field found? else if(!strcasecmp(name, "Transfer-Encoding")) { //Check whether chunked encoding is used if(!strcasecmp(value, "chunked")) connection->request.chunkedEncoding = TRUE; } //Content-Type field found? else if(!strcasecmp(name, "Content-Type")) { //Parse Content-Type field httpParseContentTypeField(connection, value); } //Content-Length field found? else if(!strcasecmp(name, "Content-Length")) { //Get the length of the body data connection->request.contentLength = atoi(value); } //Authorization field found? else if(!strcasecmp(name, "Authorization")) { //Parse Authorization field httpParseAuthField(connection, value); } } } } //Prepare to read the HTTP request body if(connection->request.chunkedEncoding) { connection->request.byteCount = 0; connection->request.firstChunk = TRUE; connection->request.lastChunk = FALSE; } else { connection->request.byteCount = connection->request.contentLength; } //The request header has been successfully parsed return NO_ERROR; }
error_t ping(NetInterface *interface, const IpAddr *ipAddr, time_t timeout, time_t *rtt) { error_t error; uint_t i; size_t length; uint16_t identifier; uint16_t sequenceNumber; time_t startTime; time_t roundTripTime; Socket *socket; IcmpEchoMessage *message; //Debug message TRACE_INFO("Pinging %s with 64 bytes of data...\r\n", ipAddrToString(ipAddr, NULL)); //Length of the complete ICMP message including header and data length = sizeof(IcmpEchoMessage) + PING_DATA_SIZE; //Allocate memory buffer to hold an ICMP message message = osMemAlloc(length); //Failed to allocate memory? if(!message) return ERROR_OUT_OF_MEMORY; //Identifier field is used to help matching requests and replies identifier = rand(); //Sequence Number field is increment each time an Echo Request is sent sequenceNumber = osAtomicInc16(&pingSequenceNumber); //Format ICMP Echo Request message message->type = ICMP_TYPE_ECHO_REQUEST; message->code = 0; message->checksum = 0; message->identifier = identifier; message->sequenceNumber = sequenceNumber; //Copy data for(i = 0; i < PING_DATA_SIZE; i++) message->data[i] = i; #if (IPV4_SUPPORT == ENABLED) //Target address is an IPv4 address? if(ipAddr->length == sizeof(Ipv4Addr)) { Ipv4Addr srcIpAddr; //Select the source IPv4 address and the relevant network //interface to use when pinging the specified host error = ipv4SelectSourceAddr(&interface, ipAddr->ipv4Addr, &srcIpAddr); //Any error to report? if(error) { //Free previously allocated memory osMemFree(message); //Return the corresponding error code return error; } //ICMP Echo Request message message->type = ICMP_TYPE_ECHO_REQUEST; //Message checksum calculation message->checksum = ipCalcChecksum(message, length); //Open a raw socket socket = socketOpen(SOCKET_TYPE_RAW, SOCKET_PROTOCOL_ICMP); } else #endif #if (IPV6_SUPPORT == ENABLED) //Target address is an IPv6 address? if(ipAddr->length == sizeof(Ipv6Addr)) { Ipv6PseudoHeader pseudoHeader; //Select the source IPv6 address and the relevant network //interface to use when pinging the specified host error = ipv6SelectSourceAddr(&interface, &ipAddr->ipv6Addr, &pseudoHeader.srcAddr); //Any error to report? if(error) { //Free previously allocated memory osMemFree(message); //Return the corresponding error code return error; } //ICMPv6 Echo Request message message->type = ICMPV6_TYPE_ECHO_REQUEST; //Format IPv6 pseudo header pseudoHeader.destAddr = ipAddr->ipv6Addr; pseudoHeader.length = htonl(length); pseudoHeader.reserved = 0; pseudoHeader.nextHeader = IPV6_ICMPV6_HEADER; //Message checksum calculation message->checksum = ipCalcUpperLayerChecksum( &pseudoHeader, sizeof(Ipv6PseudoHeader), message, length); //Open a raw socket socket = socketOpen(SOCKET_TYPE_RAW, SOCKET_PROTOCOL_ICMPV6); } else #endif //Target address is not valid? { //Free previously allocated memory osMemFree(message); //Report an error return ERROR_INVALID_ADDRESS; } //Failed to open socket? if(!socket) { //Free previously allocated memory osMemFree(message); //Report an error return ERROR_OPEN_FAILED; } //Associate the newly created socket with the relevant interface error = socketBindToInterface(socket, interface); //Unable to bind the socket to the desired interface? if(error) { //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return status code return error; } //Connect the socket to the target host error = socketConnect(socket, ipAddr, 0); //Any error to report? if(error) { //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return status code return error; } //Send Echo Request message error = socketSend(socket, message, length, NULL, 0); //Failed to send message ? if(error) { //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return status code return error; } //Save the time at which the request was sent startTime = osGetTickCount(); //Timeout value exceeded? while((osGetTickCount() - startTime) < timeout) { //Adjust receive timeout error = socketSetTimeout(socket, timeout); //Any error to report? if(error) break; //Wait for an incoming ICMP message error = socketReceive(socket, message, sizeof(IcmpEchoMessage) + PING_DATA_SIZE, &length, 0); //Any error to report? if(error) break; //Check message length if(length != (sizeof(IcmpEchoMessage) + PING_DATA_SIZE)) continue; //Verify message type if(ipAddr->length == sizeof(Ipv4Addr) && message->type != ICMP_TYPE_ECHO_REPLY) continue; if(ipAddr->length == sizeof(Ipv6Addr) && message->type != ICMPV6_TYPE_ECHO_REPLY) continue; //Response identifier matches request identifier? if(message->identifier != identifier) continue; //Make sure the sequence number is correct if(message->sequenceNumber != sequenceNumber) continue; //Loop through data field for(i = 0; i < PING_DATA_SIZE; i++) { //Compare received data against expected data if(message->data[i] != i) break; } //Valid Echo Reply message received? if(i == PING_DATA_SIZE) { //Calculate round-trip time roundTripTime = osGetTickCount() - startTime; //Debug message TRACE_INFO("Echo received (round-trip time = %ums)...\r\n", roundTripTime); //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //Return round-trip time if(rtt) *rtt = roundTripTime; //No error to report return NO_ERROR; } } //Debug message TRACE_INFO("No echo received!\r\n"); //Free previously allocated memory osMemFree(message); //Close socket socketClose(socket); //No Echo Reply received from host... return ERROR_NO_RESPONSE; }
error_t ftpOpenFile(FtpClientContext *context, const char_t *path, uint_t flags) { error_t error; uint_t replyCode; IpAddr ipAddr; uint16_t port; //Invalid context? if(context == NULL) return ERROR_INVALID_PARAMETER; //Open data socket context->dataSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Failed to open socket? if(!context->dataSocket) return ERROR_OPEN_FAILED; //Start of exception handling block do { //Bind the socket to a particular network interface? if(context->interface != NULL) { //Associate the socket with the relevant interface error = socketBindToInterface(context->dataSocket, context->interface); //Any error to report? if(error) break; } //Set timeout for blocking operations error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT); //Any error to report? if(error) break; //Check data transfer direction if(flags & (FTP_FOR_WRITING | FTP_FOR_APPENDING)) { //Maximize transmission throughput by using a large buffer error = socketSetTxBufferSize(context->dataSocket, FTP_CLIENT_SOCKET_MAX_TX_BUFFER_SIZE); //Any error to report? if(error) break; //Use a small buffer for the reception path error = socketSetRxBufferSize(context->dataSocket, FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE); //Any error to report? if(error) break; } else { //Use a small buffer for the transmission path error = socketSetTxBufferSize(context->dataSocket, FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE); //Any error to report? if(error) break; //Maximize reception throughput by using a large buffer error = socketSetRxBufferSize(context->dataSocket, FTP_CLIENT_SOCKET_MAX_RX_BUFFER_SIZE); //Any error to report? if(error) break; } //Set representation type if(flags & FTP_TEXT_TYPE) { //Use ASCII type error = ftpSetType(context, 'A'); //Any error to report? if(error) break; } else { //Use image type error = ftpSetType(context, 'I'); //Any error to report? if(error) break; } //Check transfer mode if(!context->passiveMode) { //Place the data socket in the listening state error = socketListen(context->dataSocket, 1); //Any error to report? if(error) break; //Retrieve local IP address error = socketGetLocalAddr(context->controlSocket, &ipAddr, NULL); //Any error to report? if(error) break; //Retrieve local port number error = socketGetLocalAddr(context->dataSocket, NULL, &port); //Any error to report? if(error) break; //Set the port to be used in data connection error = ftpSetPort(context, &ipAddr, port); //Any error to report? if(error) break; } else { //Enter passive mode error = ftpSetPassiveMode(context, &port); //Any error to report? if(error) break; //Establish data connection error = socketConnect(context->dataSocket, &context->serverAddr, port); //Connection to server failed? if(error) break; } //Format the command if(flags & FTP_FOR_WRITING) sprintf(context->buffer, "STOR %s\r\n", path); else if(flags & FTP_FOR_APPENDING) sprintf(context->buffer, "APPE %s\r\n", path); else sprintf(context->buffer, "RETR %s\r\n", path); //Send the command to the server error = ftpSendCommand(context, context->buffer, &replyCode); //Any error to report? if(error) break; //Check FTP response code if(!FTP_REPLY_CODE_1YZ(replyCode)) { //Report an error error = ERROR_UNEXPECTED_RESPONSE; break; } //Check transfer mode if(!context->passiveMode) { //Wait for the server to connect back to the client's data port Socket *socket = socketAccept(context->dataSocket, NULL, NULL); //No connection request? if(!socket) { //Report an error error = ERROR_FAILURE; break; } //Close the listening socket socketClose(context->dataSocket); //Save socket handle context->dataSocket = socket; //Set timeout for blocking operations error = socketSetTimeout(context->dataSocket, FTP_CLIENT_DEFAULT_TIMEOUT); //Any error to report? if(error) break; } //End of exception handling block } while(0); //Any error to report? if(error) { //Clean up side effects socketClose(context->dataSocket); context->dataSocket = NULL; } //Return status code return error; }
error_t ftpConnect(FtpClientContext *context, NetInterface *interface, IpAddr *serverAddr, uint16_t serverPort, uint_t flags) { error_t error; uint_t replyCode; //Invalid context? if(context == NULL) return ERROR_INVALID_PARAMETER; //Clear context memset(context, 0, sizeof(FtpClientContext)); //Underlying network interface context->interface = interface; //Save the IP address of the FTP server context->serverAddr = *serverAddr; //Use passive mode? if(flags & FTP_PASSIVE_MODE) context->passiveMode = TRUE; //Open control socket context->controlSocket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); //Failed to open socket? if(!context->controlSocket) return ERROR_OPEN_FAILED; //Start of exception handling block do { //Bind the socket to a particular network interface? if(context->interface != NULL) { //Associate the socket with the relevant interface error = socketBindToInterface(context->controlSocket, context->interface); //Any error to report? if(error) break; } //Set timeout for blocking operations error = socketSetTimeout(context->controlSocket, FTP_CLIENT_DEFAULT_TIMEOUT); //Any error to report? if(error) break; //Specify the size of the send buffer error = socketSetTxBufferSize(context->controlSocket, FTP_CLIENT_SOCKET_MIN_TX_BUFFER_SIZE); //Any error to report? if(error) break; //Specify the size of the receive buffer error = socketSetRxBufferSize(context->controlSocket, FTP_CLIENT_SOCKET_MIN_RX_BUFFER_SIZE); //Any error to report? if(error) break; //Connect to the FTP server error = socketConnect(context->controlSocket, serverAddr, serverPort); //Connection to server failed? if(error) break; //Wait for the connection greeting reply error = ftpSendCommand(context, NULL, &replyCode); //Any communication error to report? if(error) break; //Check FTP response code if(!FTP_REPLY_CODE_2YZ(replyCode)) error = ERROR_UNEXPECTED_RESPONSE; //End of exception handling block } while(0); //Any error to report? if(error) { //Clean up side effects socketClose(context->controlSocket); context->controlSocket = NULL; } //Return status code return error; }
void tcpDiscardListenerTask(void *param) { error_t error; uint16_t clientPort; IpAddr clientIpAddr; Socket *serverSocket; Socket *clientSocket; DiscardServiceContext *context; OsTask *task; //Point to the listening socket serverSocket = (Socket *) param; //Main loop while(1) { //Accept an incoming connection clientSocket = socketAccept(serverSocket, &clientIpAddr, &clientPort); //Check whether a valid connection request has been received if(!clientSocket) continue; //Debug message TRACE_INFO("Discard service: connection established with client %s port %" PRIu16 "\r\n", ipAddrToString(&clientIpAddr, NULL), clientPort); //Adjust timeout error = socketSetTimeout(clientSocket, DISCARD_TIMEOUT); //Any error to report? if(error) { //Close socket socketClose(clientSocket); //Wait for an incoming connection attempt continue; } //Allocate resources for the new connection context = osAllocMem(sizeof(DiscardServiceContext)); //Failed to allocate memory? if(!context) { //Close socket socketClose(clientSocket); //Wait for an incoming connection attempt continue; } //Record the handle of the newly created socket context->socket = clientSocket; //Create a task to service the current connection task = osCreateTask("TCP Discard Connection", tcpDiscardConnectionTask, context, DISCARD_SERVICE_STACK_SIZE, DISCARD_SERVICE_PRIORITY); //Did we encounter an error? if(task == OS_INVALID_HANDLE) { //Close socket socketClose(clientSocket); //Release resources osFreeMem(context); } } }
error_t icecastClientConnect(IcecastClientContext *context) { error_t error; size_t length; IpAddr serverIpAddr; //Icecast request template const char_t requestTemplate[] = "GET /%s HTTP/1.1\r\n" "Host: %s\r\n" "User-agent: UserAgent\r\n" "Icy-MetaData: 1\r\n" "Connection: close\r\n" "\r\n"; //The specified Icecast server can be either an IP or a host name error = getHostByName(context->settings.interface, context->settings.serverName, &serverIpAddr, 1, NULL, 0); //Unable to resolve server name? if(error) return error; //Open a TCP socket context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_PROTOCOL_TCP); //Failed to open socket? if(!context->socket) return ERROR_OUT_OF_RESOURCES; //Start of exception handling block do { //Adjust receive timeout error = socketSetTimeout(context->socket, ICECAST_CLIENT_TIMEOUT); //Any error to report? if(error) return error; //Connect to the specified Icecast server error = socketConnect(context->socket, &serverIpAddr, context->settings.serverPort); //Connection with server failed? if(error) return error; //Format Icecast request length = sprintf(context->buffer, requestTemplate, context->settings.resource, context->settings.serverName); //Debug message TRACE_DEBUG(context->buffer); //Send Icecast request error = socketSend(context->socket, context->buffer, length, NULL, SOCKET_FLAG_WAIT_ACK); //Failed to send the request? if(error) return error; //Parse response header while(1) { char_t *separator; char_t *property; char_t *value; //Read a line from the response header error = socketReceive(context->socket, context->buffer, ICECAST_CLIENT_METADATA_MAX_SIZE, &length, SOCKET_FLAG_BREAK_CRLF); //Failed to read data? if(error) break; //Properly terminate the string with a NULL character context->buffer[length] = '\0'; //The end of the header has been reached? if(!strcmp(context->buffer, "\r\n")) break; //Check whether a separator is present separator = strchr(context->buffer, ':'); //Separator found? if(separator) { //Split the line *separator = '\0'; //Get property name and value property = strTrimWhitespace(context->buffer); value = strTrimWhitespace(separator + 1); //Debug message TRACE_INFO("<%s>=<%s>\r\n", property, value); //Icy-Metaint property found? if(!strcasecmp(property, "Icy-Metaint")) context->blockSize = atoi(value); } } //End of exception handling block } while(0); //Check whether an error occurred if(error) { //Clean up side effects socketClose(context->socket); } //Return status code return error; }