Em um post anterior eu mostrei como se enviava e-mail e adverti para o fato de que a conexão não era segura e as credenciais podiam ser interceptadas (além do fato óbvio do e-mail poder ser lido). Eu achei que enviar por uma conexão segura fosse muito complicado, mas descobri que não é. O problema é que usa muita RAM. O email sem criptografia te deixa com 46KB livres para o resto do seu programa enquanto este aqui te deixa com “apenas” 18KB. Lembre-se de que isso ainda é 9x a RAM total de um Arduino UNO.
Sobre fingerprints e validação de certificado
Se você usa um servidor de e-mail numa conta compartilhada, como é o caso da hostgator, o certificado não vai validar se você colocar o nome do seu domínio como servidor. Você tem que obter o nome correto do servidor de e-mail que atende a sua conta. Na hostgator isso pode ser visto no cPanel.
Por exemplo, meu servidor de e-mail normal seria: meudominio.com.br ou mail.meudominio.com.br mas para validar o certificado eu preciso colocar gator1234.hostgator.com.
O exemplo abaixo é uma adaptação simples do exemplo que não usa criptografia.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
/* * Envio de e-mail por SMTP usando conexão criptografada * Jefferson Ryan - automalabs.com.br - Julho de 2017 * Testado em um ESP8266 - ESP-01 - IDE Arduino 1.8.1 e ESP8266 Core v2.3.0 * Mais informações em http://www.automalabs.com.br/?p=1595 * */ #include <ESP8266WiFi.h> #include <WiFiClientSecure.h> //======== Você precisa preencher estes dados =========================================== const char* SSID = ""; const char* PASS = ""; char smtp_server[] = ""; const int smtp_port = 465; //465 é a porta segura no meu servidor. A sua pode ser outra. const String mail_username="nome_do_usuario"; const String mail_password="senha"; const String mail_from="ESP8266 <usuario@servidor_remetente>"; const String mail_to="Jefferson <usuario@servidor_destino>"; const String mail_subject="Enviado pelo ESP8266"; const String mail_line1="Esta é a linha 1\n"; const String mail_line2="Esta é a linha 2"; const String mail_line3="Esta é a linha 3"; //Obtenha com a ajuda de https://www.grc.com/fingerprints.htm //Precisará ser atualizado manualmente quando o certificado expirar const char* fingerprint = ""; //========================================================================================= bool construindoLinha=false; const char* _b64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Cria uma conexao SSL/TLS WiFiClientSecure client; void printFreeRAM(){ Serial.print("RAM livre: "); Serial.print(ESP.getFreeHeap()); Serial.println(" bytes."); } void a3_to_a4(unsigned char * a4, unsigned char * a3) { a4[0] = (a3[0] & 0xfc) >> 2; a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4); a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6); a4[3] = (a3[2] & 0x3f); } int base64_encode(char *output, const char *input, int inputLen) { int i = 0, j = 0; int encLen = 0; unsigned char a3[3]; unsigned char a4[4]; while(inputLen--) { a3[i++] = *(input++); if(i == 3) { a3_to_a4(a4, a3); for(i = 0; i < 4; i++) { output[encLen++] = _b64_alphabet[a4[i]]; } i = 0; } } if(i) { for(j = i; j < 3; j++) { a3[j] = '\0'; } a3_to_a4(a4, a3); for(j = 0; j < i + 1; j++) { output[encLen++] = _b64_alphabet[a4[j]]; } while((i++ < 3)) { output[encLen++] = '='; } } output[encLen] = '\0'; return encLen; } void setup() { Serial.begin(115200); // Serial.setDebugOutput(true); delay(10); Serial.println(""); Serial.println(""); Serial.print("Conectando a: "); Serial.println(SSID); Serial.print("Com a senha: "); Serial.println(PASS); WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi Conectado."); Serial.print("Endereco IP local: "); Serial.println(WiFi.localIP()); printFreeRAM(); byte ret = sendEmail(); printFreeRAM(); } void loop() { } void SEND(String message) { if (!construindoLinha){Serial.print("EU: ");}; //Imprimo apenas no início de cada linha Serial.print (message); client.print (message); construindoLinha=true; } void SENDln(String message) { if (!construindoLinha){Serial.print("EU: ");}; //Imprimo apenas no início de cada linha Serial.println (message); client.println (message); construindoLinha=false; } byte sendEmail() { byte thisByte = 0; byte respCode; Serial.print("Conectando a: "); Serial.println(smtp_server); if (!client.connect(smtp_server, smtp_port)) { Serial.println("Falha na conexao."); return 0; }else { Serial.println("Conectado."); } if (client.verify(fingerprint,smtp_server)) { Serial.println("O certificado confere."); } else { Serial.println("O certificado nao confere."); //return 0; //Descomente esta linha para impedir o envio se o certificado estiver errado } if (!eRcv()) return 0; char* buf = (char*)malloc(100); SEND("EHLO "); SENDln(smtp_server); if (!eRcv()) return 0; SENDln("auth login"); if (!eRcv()) return 0; base64_encode(buf, mail_username.c_str(), mail_username.length()); client.println(buf); Serial.println(buf); if (!eRcv()) return 0; base64_encode(buf, mail_password.c_str(), mail_password.length()); client.println(buf); Serial.println(buf); if (!eRcv()) return 0; SEND("MAIL From: "); SENDln(mail_from); if (!eRcv()) return 0; SEND("RCPT To: "); SENDln(mail_to); if (!eRcv()) return 0; SENDln("DATA"); if (!eRcv()) return 0; SEND("To: "); SENDln(mail_to); SEND("From: "); SENDln(mail_from); //Este é o "nome para exibição" SEND("Subject: "); SENDln(mail_subject); SENDln(mail_line1); SENDln(mail_line2); SENDln(mail_line3); SENDln("."); //O email tem que terminar assim if (!eRcv()) return 0; SENDln("QUIT"); if (!eRcv()) return 0; client.stop(); Serial.println("Desconectado."); return 1; } byte eRcv() { byte respCode; byte thisByte; int loopCount = 0; while (!client.available()) { delay(1); loopCount++; // Se nada for recebido em 10s, desiste. if (loopCount > 10000) { client.stop(); Serial.println("\r\nTimeout"); return 0; } } respCode = client.peek(); while (client.available()) { thisByte = client.read(); Serial.write(thisByte); } if (respCode >= '4') { // efail(); return 0; } return 1; } |
Por alto, as diferenças são 4:
1)Acrescente: #include <WiFiClientSecure.h>
2)Acrescente: const char* fingerprint = “fingerprint_aqui”;
3)Mude: WiFiClient client; para WiFiClientSecure client;
4)Acrescente a validação de certificado logo após a conexão:
1 2 3 4 5 6 |
if (client.verify(fingerprint,smtp_server)) { Serial.println("O certificado confere."); } else { Serial.println("O certificado nao confere."); //return 0; //Descomente esta linha para impedir o envio se o certificado estiver errado } |
Boa noite Jefferson.
Não sei se ainda ve este post mas mesmo assim pergunto:
Usei seu programa de enviar email quando muda status da GPIO0 , SEM criptografia.
Usei porta 465 e um servidor de email particular (host UAI). Ele informa que foi conectado mas da TIME OUT.
Aumentei de 10 para 30 segundos mas ainda assim deu alarme. Alguma sugestão ?
Obs: Tentei usar seu programa de enviar email normalmente COM criptografia e deu FALHA DE CONEXÃO.
Agradeço qualquer resposta.
Obrigado. Olinto
Eu estou usando o exemplo desta página (com criptografia) em um projeto que comecei no mês passado e está funcionando normalmente. Mas eu só usei até hoje o gmail, que eu considero um exemplo de servidor “complicado”. Outros deveriam criar ainda menos problemas. O exemplo sem criptografia é ainda mais garantido.
Como eu não esbarrei nesses problemas eu não sei o que pode ser.
Quando a função sendEmail() é chamada pela primeira vez provoca uma redução de cerca de 15KB na RAM livre, que não é devolvida. Eu conseguir resolver isso usando ponteiros, da seguinte forma:
Mude
WiFiClientSecure client;
para
WiFiClientSecure *client;
delete
WiFiClientSecure client;
Acrescente no início de sendEmail():
client = new WiFiClientSecure;
Acrescente no final de sendEmail():
delete client;
Troque todas as ocorrências de “client.” por “client->”. Por exemplo, client.stop() vira client->stop();
Desta forma o objeto “client” é criado no início de sendEmail() e destruído no final.
Mas atente para o fato de que ainda assim a função precisa de 15KB livres para funcionar. Se você não tiver essa RAM livre o programa vai travar ao executar sendEmail();