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);
   }
}
Example #2
0
error_t processClientRequest(SOCKET clientSocket)
{
   error_t error;
   size_t n;
   char_t *p;
   char_t buffer[512];
   char_t response[2048];
   TlsContext *tlsContext;

   //Start of exception handling block
   do
   {
      //Initialize SSL/TLS context
      tlsContext = tlsInit();
      //Initialization failed?
      if(!tlsContext)
      {
         //Report an error
         error = ERROR_OUT_OF_MEMORY;
         //Exit immediately
         break;
      }

      //Select server operation mode
      error = tlsSetConnectionEnd(tlsContext, TLS_CONNECTION_END_SERVER);
      //Any error to report?
      if(error) break;

      //Bind TLS to the relevant socket
      error = tlsSetSocket(tlsContext, clientSocket);
      //Any error to report?
      if(error) break;

      //Set the PRNG algorithm to be used
      error = tlsSetPrng(tlsContext, YARROW_PRNG_ALGO, &yarrowContext);
      //Any error to report?
      if(error) break;

      //Session cache that will be used to save/resume TLS sessions
      error = tlsSetCache(tlsContext, tlsCache);
      //Any error to report?
      if(error) break;

      //Client authentication is optional
      error = tlsSetClientAuthMode(tlsContext, TLS_CLIENT_AUTH_OPTIONAL);
      //Any error to report?
      if(error) break;

      //Import Diffie-Hellman parameters
      error = tlsSetDhParameters(tlsContext, dhParams, dhParamsLength);
      //Any error to report?
      if(error) break;

      //Import the server's RSA certificate
      error = tlsAddCertificate(tlsContext, serverRsaCert,
         serverRsaCertLength, serverRsaPrivateKey, serverRsaPrivateKeyLength);
      //Any error to report?
      if(error) break;

      //Import the server's DSA certificate
      error = tlsAddCertificate(tlsContext, serverDsaCert,
         serverDsaCertLength, serverDsaPrivateKey, serverDsaPrivateKeyLength);
      //Any error to report?
      if(error) break;

      //Import the server's ECDSA certificate
      error = tlsAddCertificate(tlsContext, serverEcdsaCert,
         serverEcdsaCertLength, serverEcdsaPrivateKey, serverEcdsaPrivateKeyLength);
      //Any error to report?
      if(error) break;

      //Import the list of trusted CA certificates
      error = tlsSetTrustedCaList(tlsContext, trustedCaList, trustedCaListLength);
      //Any error to report?
      if(error) break;

      //Establish a secure session
      error = tlsConnect(tlsContext);
      //TLS handshake failure?
      if(error) break;

      //Debug message
      TRACE_INFO("\r\n");
      TRACE_INFO("HTTP request:\r\n");

      //Read HTTP request
      while(1)
      {
         //Read a complete line
         error = tlsRead(tlsContext, buffer, sizeof(buffer), &n, TLS_FLAG_BREAK_CRLF);
         //Any error to report?
         if(error) break;

         //Properly terminate the string with a NULL character
         buffer[n] = '\0';
         //Dump HTTP request
         TRACE_INFO("%s", buffer);

         //The end of the header has been reached?
         if(!strcmp(buffer, "\r\n")) break;
      }

      //Propagate exception if necessary...
      if(error) break;

      //Point to the beginning of the response
      p = response;

      //Format response
      p += sprintf(p, "HTTP/1.0 200 OK\r\n");
      p += sprintf(p, "Content-Type: text/html\r\n");
      p += sprintf(p, "\r\n");

      p += sprintf(p, "<!DOCTYPE html>\r\n");
      p += sprintf(p, "<html>\r\n");
      p += sprintf(p, "<head>\r\n");
      p += sprintf(p, "  <title>Oryx Embedded - CycloneSSL Server Demo</title>\r\n");
      p += sprintf(p, "  <style>\r\n");
      p += sprintf(p, "    body {font-family: monospace; font-size: 13px;}\r\n");
      p += sprintf(p, "    table {border-width: 1px; border-style: ouset; border-collapse: collapse;}\r\n");
      p += sprintf(p, "    td {border-width: 1px; border-style: inset; padding: 3px;}\r\n");
      p += sprintf(p, "  </style>\r\n");
      p += sprintf(p, "</head>\r\n");
      p += sprintf(p, "<body>\r\n");
      p += sprintf(p, "  <p>Welcome to the CycloneSSL demo server!</p>\r\n");
      p += sprintf(p, "  <table>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Hit counter</td>\r\n");
      p += sprintf(p, "    <td>%d</td>\r\n", hitCounter);
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Server version</td>\r\n");
      p += sprintf(p, "    <td>%s</td>\r\n", tlsGetVersionName(TLS_MAX_VERSION));
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Client version</td>\r\n");
      p += sprintf(p, "    <td>%s</td>\r\n", tlsGetVersionName(tlsContext->clientVersion));
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Negotiated version</td>\r\n");
      p += sprintf(p, "    <td>%s</td>\r\n", tlsGetVersionName(tlsContext->version));
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Cipher suite</td>\r\n");
      p += sprintf(p, "    <td>%s</td>\r\n", tlsGetCipherSuiteName(tlsContext->cipherSuite));
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Client random</td>\r\n");
      p += sprintf(p, "      <td>\r\n");
      p += sprintf(p, "        %s\r\n", dumpArray(buffer, (uint8_t *) &tlsContext->clientRandom, 32));
      p += sprintf(p, "      </td>\r\n");
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Server random</td>\r\n");
      p += sprintf(p, "      <td>\r\n");
      p += sprintf(p, "        %s\r\n", dumpArray(buffer, (uint8_t *) &tlsContext->serverRandom, 32));
      p += sprintf(p, "      </td>\r\n");
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  <tr>\r\n");
      p += sprintf(p, "    <td>Session ID</td>\r\n");
      p += sprintf(p, "      <td>\r\n");
      p += sprintf(p, "        %s\r\n", dumpArray(buffer, tlsContext->sessionId, tlsContext->sessionIdLength));
      p += sprintf(p, "      </td>\r\n");
      p += sprintf(p, "  </tr>\r\n");

      p += sprintf(p, "  </table>\r\n");
      p += sprintf(p, "</body>\r\n");
      p += sprintf(p, "</html>\r\n");

      //Dump HTTP response
      TRACE_INFO("HTTP response:\r\n");
      TRACE_INFO("%s\r\n", response);

      //Send response to the client
      error = tlsWrite(tlsContext, response, strlen(response), 0);
      //Any error to report?
      if(error) break;

      //End of exception handling block
   } while(0);

   //Terminate SSL session
   tlsFree(tlsContext);

   //Return status code
   return error;
}
Example #3
0
int_t main(void)
{
   error_t error;
   size_t length;
   int_t ret;
   WSADATA wsaData;
   HOSTENT *host;
   SOCKADDR_IN addr;
   HCRYPTPROV hProvider;
   YarrowContext yarrowContext;
   char_t buffer[512];
   uint8_t seed[32];

   //Socket descriptor
   SOCKET sock = SOCKET_ERROR;

   //SSL/TLS context
   TlsContext *tlsContext = NULL;

   //Credentials
   char_t *clientCert = NULL;
   size_t clientCertLength = 0;
   char_t *clientPrivateKey = NULL;
   size_t clientPrivateKeyLength = 0;
   char_t *trustedCaList = NULL;
   size_t trustedCaListLength = 0;

   //Start-up message
   TRACE_INFO("******************************\r\n");
   TRACE_INFO("*** CycloneSSL Client Demo ***\r\n");
   TRACE_INFO("******************************\r\n");
   TRACE_INFO("\r\n");

   //Acquire cryptographic context
   ret = CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
   //Any error to report?
   if(!ret)
   {
      //Debug message
      TRACE_ERROR("Error: Cannot acquire cryptographic context (%d)\r\n", GetLastError());
      //Exit immediately
      return ERROR_FAILURE;
   }

   //Generate a random seed
   ret = CryptGenRandom(hProvider, sizeof(seed), seed);
   //Any error to report?
   if(!ret)
   {
      //Debug message
      TRACE_ERROR("Error: Failed to generate random data (%d)\r\n", GetLastError());
      //Exit immediately
      return ERROR_FAILURE;
   }

   //Release cryptographic context
   CryptReleaseContext(hProvider, 0);

   //PRNG initialization
   error = yarrowInit(&yarrowContext);
   //Any error to report?
   if(error)
   {
      //Debug message
      TRACE_ERROR("Error: PRNG initialization failed (%d)\r\n", error);
      //Exit immediately
      return ERROR_FAILURE;
   }

   //Properly seed the PRNG
   error = yarrowSeed(&yarrowContext, seed, sizeof(seed));
   //Any error to report?
   if(error)
   {
      //Debug message
      TRACE_ERROR("Error: Failed to seed PRNG (%d)\r\n", error);
      //Exit immediately
      return error;
   }

   //Winsock initialization
   ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
   //Any error to report?
   if(ret)
   {
      //Debug message
      TRACE_ERROR("Error: Winsock initialization failed (%d)\r\n", ret);
      //Exit immediately
      return ERROR_FAILURE;
   }

   //Start of exception handling block
   do
   {
      //Debug message
      TRACE_INFO("Loading credentials...\r\n");

      //Load trusted CA certificates
      error = readPemFile(APP_CA_CERT_BUNDLE, &trustedCaList, &trustedCaListLength);
      //Any error to report?
      if(error) break;

      //Load client's certificate
      error = readPemFile(APP_CLIENT_CERT, &clientCert, &clientCertLength);
      //Any error to report?
      if(error) break;

      //Load client's private key
      error = readPemFile(APP_CLIENT_PRIVATE_KEY, &clientPrivateKey, &clientPrivateKeyLength);
      //Any error to report?
      if(error) break;

      //Debug message
      TRACE_INFO("Trying to resolve %s...\r\n", APP_SERVER_NAME);

      //Resolve server name
      host = gethostbyname(APP_SERVER_NAME);
      //Failed to resolve server name?
      if(!host)
      {
         //Debug message
         TRACE_ERROR("Error: Cannot resolve server name (%d)\r\n", WSAGetLastError());
         //Report an error
         error = ERROR_FAILURE;
         //Exit immediately
         break;
      }

      //Open a socket
      sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      //Failed to open socket?
      if(sock < 0)
      {
         //Debug message
         TRACE_ERROR("Error: Cannot open socket (%d)\r\n", WSAGetLastError());
         //Report an error
         error = ERROR_FAILURE;
         //Exit immediately
         break;
      }

      //Destination address
      addr.sin_family = host->h_addrtype;
      memcpy(&addr.sin_addr, host->h_addr, host->h_length);
      addr.sin_port = htons(APP_SERVER_PORT);

      //Connect to the SSL server
      ret = connect(sock, (PSOCKADDR) &addr, sizeof(addr));
      //Connection with server failed?
      if(ret < 0)
      {
         //Debug message
         TRACE_ERROR("Error: Failed to connect (%d)\r\n", WSAGetLastError());
         //Report an error
         error = ERROR_FAILURE;
         //Exit immediately
         break;
      }

      //Initialize SSL/TLS context
      tlsContext = tlsInit();
      //Initialization failed?
      if(!tlsContext)
      {
         //Report an error
         error = ERROR_OUT_OF_MEMORY;
         //Exit immediately
         break;
      }

      //Bind TLS to the relevant socket
      error = tlsSetSocket(tlsContext, sock);
      //Any error to report?
      if(error) break;

      //Select client operation mode
      error = tlsSetConnectionEnd(tlsContext, TLS_CONNECTION_END_CLIENT);
      //Any error to report?
      if(error) break;

      //Set the PRNG algorithm to be used
      error = tlsSetPrng(tlsContext, YARROW_PRNG_ALGO, &yarrowContext);
      //Any error to report?
      if(error) break;

#if (APP_SET_CIPHER_SUITES == ENABLED)
      //Preferred cipher suite list
      error = tlsSetCipherSuites(tlsContext, cipherSuites, arraysize(cipherSuites));
      //Any error to report?
      if(error) break;
#endif

#if (APP_SET_SERVER_NAME == ENABLED)
      //Set the fully qualified domain name of the server
      error = tlsSetServerName(tlsContext, APP_SERVER_NAME);
      //Any error to report?
      if(error) break;
#endif

#if (APP_SET_TRUSTED_CA_LIST == ENABLED)
      //Import the list of trusted CA certificates
      error = tlsSetTrustedCaList(tlsContext, trustedCaList, trustedCaListLength);
      //Any error to report?
      if(error) break;
#endif

#if (APP_SET_CLIENT_CERT == ENABLED)
      //Import the client's certificate
      error = tlsAddCertificate(tlsContext, clientCert,
         clientCertLength, clientPrivateKey, clientPrivateKeyLength);
      //Any error to report?
      if(error) break;
#endif

      //Establish a secure session
      error = tlsConnect(tlsContext);
      //TLS handshake failure?
      if(error) break;

      //Format HTTP request
      sprintf(buffer, "GET %s HTTP/1.0\r\nHost: %s:%u\r\n\r\n",
         APP_REQUEST_URI, APP_SERVER_NAME, APP_SERVER_PORT);

      //Debug message
      TRACE_INFO("\r\n");
      TRACE_INFO("HTTP request:\r\n%s", buffer);

      //Send the request
      error = tlsWrite(tlsContext, buffer, strlen(buffer), 0);
      //Any error to report?
      if(error) break;

      //Debug message
      TRACE_INFO("HTTP response:\r\n");

      //Read the whole response
      while(1)
      {
         //Read data
         error = tlsRead(tlsContext, buffer, sizeof(buffer) - 1, &length, 0);
         //End of stream?
         if(error) break;

         //Properly terminate the string with a NULL character
         buffer[length] = '\0';
         //Debug message
         TRACE_INFO("%s", buffer);
      }

      //Successfull processing
      error = NO_ERROR;

   //End of exception handling block
   } while(0);

   //Terminate TLS session
   tlsFree(tlsContext);

   //Close socket if necessary
   if(sock >= 0)
      closesocket(sock);

   //Free previously allocated resources
   free(trustedCaList);
   free(clientCert);
   free(clientPrivateKey);

   //Release PRNG context
   yarrowRelease(&yarrowContext);

   //Winsock related cleanup
   WSACleanup();
   //Dumps all the memory blocks in the heap when a memory leak has occurred
   _CrtDumpMemoryLeaks();

   //Wait for the user to press a key
   system("pause");

   //Return status code
   return error;
}
Example #4
0
error_t sslClientTest(void)
{
   error_t error;
   size_t length;
   IpAddr ipAddr;
   static char_t buffer[256];

   //Underlying socket
   Socket *socket = NULL;
   //SSL/TLS context
   TlsContext *tlsContext = NULL;

   //Debug message
   TRACE_INFO("Resolving server name...\r\n");

   //Resolve SSL server name
   error = getHostByName(NULL, APP_SERVER_NAME, &ipAddr, 0);
   //Any error to report?
   if(error)
   {
      //Debug message
      TRACE_INFO("Failed to resolve server name!\r\n");
      //Exit immediately
      return error;
   }

   //Create a new socket to handle the request
   socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP);
   //Any error to report?
   if(!socket)
   {
      //Debug message
      TRACE_INFO("Failed to open socket!\r\n");
      //Exit immediately
      return ERROR_OPEN_FAILED;
   }

   //Start of exception handling block
   do
   {
      //Debug message
      TRACE_INFO("Connecting to SSL server %s\r\n", ipAddrToString(&ipAddr, NULL));

      //Connect to the SSL server
      error = socketConnect(socket, &ipAddr, APP_SERVER_PORT);
      //Any error to report?
      if(error) break;

      //Initialize SSL/TLS context
      tlsContext = tlsInit();
      //Initialization failed?
      if(!tlsContext)
      {
         //Report an error
         error = ERROR_OUT_OF_MEMORY;
         //Exit immediately
         break;
      }

      //Bind TLS to the relevant socket
      error = tlsSetSocket(tlsContext, socket);
      //Any error to report?
      if(error) break;

      //Select client operation mode
      error = tlsSetConnectionEnd(tlsContext, TLS_CONNECTION_END_CLIENT);
      //Any error to report?
      if(error) break;

      //Set the PRNG algorithm to be used
      error = tlsSetPrng(tlsContext, YARROW_PRNG_ALGO, &yarrowContext);
      //Any error to report?
      if(error) break;

#if (APP_SET_CIPHER_SUITES == ENABLED)
      //Preferred cipher suite list
      error = tlsSetCipherSuites(tlsContext, cipherSuites, arraysize(cipherSuites));
      //Any error to report?
      if(error) break;
#endif

#if (APP_SET_SERVER_NAME == ENABLED)
      //Set the fully qualified domain name of the server
      error = tlsSetServerName(tlsContext, APP_SERVER_NAME);
      //Any error to report?
      if(error) break;
#endif

#if (APP_SET_TRUSTED_CA_LIST == ENABLED)
      //Import the list of trusted CA certificates
      error = tlsSetTrustedCaList(tlsContext, trustedCaList, trustedCaListLength);
      //Any error to report?
      if(error) break;
#endif

#if (APP_SET_CLIENT_CERT == ENABLED)
      //Import the client's certificate
      error = tlsAddCertificate(tlsContext, clientCert,
         clientCertLength, clientPrivateKey, clientPrivateKeyLength);
      //Any error to report?
      if(error) break;
#endif

      //Establish a secure session
      error = tlsConnect(tlsContext);
      //TLS handshake failure?
      if(error) break;

      //Format HTTP request
      sprintf(buffer, "GET %s HTTP/1.0\r\nHost: %s:%u\r\n\r\n",
         APP_REQUEST_URI, APP_SERVER_NAME, APP_SERVER_PORT);

      //Debug message
      TRACE_INFO("\r\n");
      TRACE_INFO("HTTP request:\r\n%s", buffer);

      //Send the request
      error = tlsWrite(tlsContext, buffer, strlen(buffer), 0);
      //Any error to report?
      if(error) break;

      //Debug message
      TRACE_INFO("HTTP response:\r\n");

      //Read the whole response
      while(1)
      {
         //Read data
         error = tlsRead(tlsContext, buffer, sizeof(buffer) - 1, &length, 0);
         //End of stream?
         if(error) break;

         //Properly terminate the string with a NULL character
         buffer[length] = '\0';
         //Debug message
         TRACE_INFO("%s", buffer);
      }

      //Successfull processing
      error = NO_ERROR;

      //End of exception handling block
   } while(0);

   //Any error to report?
   if(error)
   {
      //Debug message
      TRACE_INFO("Failed to communicate with SSL server!\r\n");
   }

   //Terminate TLS session
   tlsFree(tlsContext);
   //Close socket
   socketClose(socket);

   //Debug message
   TRACE_INFO("Connection closed...\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;
}