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); } }
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; }
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; }
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; }