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;
}
Example #2
0
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);
}
Example #3
0
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;
}
Example #6
0
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;
}
Example #9
0
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;
}
Example #10
0
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;
}
Example #11
0
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;
}
Example #12
0
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);
      }
   }
}
Example #13
0
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;
}