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 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
| #include <string>
#include <vector>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#if defined WIN32 || defined WIN64 || defined _WIN32_ || defined _WIN64_ || defined __WIN32 || defined __WIN64
#define WINDOWS
#endif
#ifdef WINDOWS
#include <windows.>
#include <iphlpapi.h>
#pragma comment(lib, "IPHLPAPI.lib")
#endif
bool verifyResponse(const std::string & responseWaited, boost::asio::ip::tcp::socket & socket)
{
bool retour = true;
boost::asio::streambuf response;
std::string code;
boost::asio::read_until(socket, response, "\r\n");
{
std::istream is(&response);
is >> code;
if( code != responseWaited)
{
std::cerr << "Réponse du serveur SMTP inattendu" << std::endl;
retour = false;
}
response.consume( response.size() );
boost::asio::socket_base::bytes_readable command(true);
socket.io_control(command);
while( command.get() )
{
boost::asio::read_until(socket, response, "\r\n");
socket.io_control(command);
}
response.consume( response.size() );
}
return retour;
}
std::vector<std::string> getDNS(void)
{
std::vector<std::string> dns_names;
#ifdef WINDOWS
// Structure qui contiendra les informations que l'on recherche.
FIXED_INFO * info = (FIXED_INFO *)malloc( sizeof(FIXED_INFO) );
ULONG bufferSize = sizeof(info); // Taille de la structure.
DWORD retour = GetNetworkParams( info, &bufferSize );
if( retour == ERROR_BUFFER_OVERFLOW ) // Si la structure n'est pas assez grande.
{
info = (FIXED_INFO *)realloc(info, bufferSize);
retour = GetNetworkParams( info, &bufferSize );
}
// Si on a réussi à obtenir nos informations.
if( retour == ERROR_SUCCESS)
{
// On récupère la liste des serveurs DNS.
struct _IP_ADDR_STRING * next = &info->DnsServerList;
// On parcourt la liste des serveurs DNS.
while( next )
{
dns_names.push_back( next->IpAddress.String );
next = next->Next;
}
}
free(info);
#else
// pour les Unix-like, lire le fichier /etc/resolv.conf
#endif
// au cas où, on ajoute les serveurs DNS de Google.
dns_names.push_back("8.8.8.8") ;
dns_names.push_back("8.8.4.4") ;
return dns_names ;
}
std::string buildDNSQuery(const std::string & mail)
{
// On récupère le nom de domaine.
int i = mail.find('@');
std::string host = mail.substr(i + 1);
// on met un identifiant quelconque pour notre demande
std::string dnsQueryText = "AA"; //id
char octet = 0x00 ; // On envoie une requête.
octet |= 0x00 ; // C'est une requête standard.
octet |= 0x00 ; // Le message n'est pas tronqué.
octet |= 0x01 ; // C'est une requête récursive.
dnsQueryText += octet ;
dnsQueryText += '\x00'; // Réservé au serveur.
dnsQueryText += '\x00';
dnsQueryText += '\x01'; // On n'a qu'une question.
dnsQueryText += '\x00';
dnsQueryText += '\x00'; // On n'a pas de réponses.
dnsQueryText += '\x00';
dnsQueryText += '\x00'; // Pas de réponses dautorité, on s'en moque ici.
dnsQueryText += '\x00';
dnsQueryText += '\x00'; // Pas de réponses additionnelles, on s'en moque aussi ici.
// Début de la requête.
// On va devoir renseigner le nom de domaine, avant chaque partie du nom de domaine séparé par des « . », on va devoir mettre la taille de cette partie et ne plus mettre les « . »
size_t pos = 0;
size_t oldPos = 0;
// On recherche tous les « . » du nom de domaine.
while( (pos = host.find('.', pos) ) != std::string::npos )
{
// On met la taille de la partie.
dnsQueryText += (char)pos - oldPos;
// On ajoute la partie.
dnsQueryText += host.substr(oldPos, pos - oldPos);
pos++;
oldPos = pos;
}
// On ajoute la dernière partie du nom de domaine.
dnsQueryText += (char)(host.size() - oldPos);
dnsQueryText += host.substr(oldPos, host.size() - oldPos );
dnsQueryText += '\x00'; // Fin du nom de domaine.
dnsQueryText += '\x00';
dnsQueryText += '\x0f'; // On recherche un serveur SMTP.
dnsQueryText += '\x00';
dnsQueryText += '\x01'; // On utilise la classe INET.
return dnsQueryText;
}
std::string askDNS( const std::string & queryText,
const std::vector<std::string> & DNSnames,
boost::asio::io_service & io_service )
{
// Socket UDP pour dialoguer avec le serveur DNS.
boost::asio::ip::udp::socket dnsSocket(io_service);
// Erreur par défaut
boost::system::error_code dnsError = boost::asio::error::host_not_found;
// Tampon pour contenir la réponse du serveur DNS.
boost::array<char, 1024> dnsBuffer;
// Trouve le serveur à partir de son nom de domaine ou de son adresse IPv4 ou IPv6 et de son port.
boost::asio::ip::udp::resolver dns_resolver(io_service);
// On va parcourir la liste des serveurs DNS tant qu'on n'a pas réussi à avoir une réponse.
std::vector<std::string>::const_iterator it = DNSnames.begin();
while( dnsError && it != DNSnames.end() )
{
// On recherche le serveur.
boost::asio::ip::udp::resolver::query dns_query(*it, "53");
auto dns_endpoint_iterator = dns_resolver.resolve(dns_query);
boost::asio::ip::udp::resolver::iterator dns_end;
// Pour chaque serveur trouvé.
while(dnsError && dns_endpoint_iterator != dns_end )
{
// On essaye de connecter le socket UDP.
dnsSocket.connect( *dns_endpoint_iterator++ , dnsError);
// Si on a réussi, on lui envoie notre requête.
if( ! dnsError)
{
dnsSocket.send( boost::asio::buffer(queryText) ,
boost::asio::socket_base::message_flags(), dnsError);
// Si pas d'erreur, on attend une réponse
if( ! dnsError )
{
// Si le serveur ne répond pas, on attendra indéfiniment, il faut donc penser à rajouter un timeout.
dnsSocket.receive( boost::asio::buffer(dnsBuffer) );
}
}
}
it++;
}
// Si on n'a pas eu de réponse, on lance une exception.
if(dnsError)
throw boost::system::system_error(dnsError);
// Si on a eu une réponse, on la retourne
return std::string (dnsBuffer.c_array(), dnsBuffer.size() );
}
std::string hostnameToString(std::string hostname, const std::string &);
std::vector<std::string> getXMServerFromAnswer( const std::string & answer, const std::string & query)
{
// On obtient le nombre de réponses :
int nbAnswer = (answer[7] << 8) + answer[8];
// On alloue le vecteur pour stocker les réponses
std::vector<std::string> MXserver(nbAnswer);
// On calcule la position du début de la première réponse
int offset = query.size() + 1;
// Pour chaque réponse.
for( ; nbAnswer ; nbAnswer--)
{
// On se place à la fin du nom de domaine
offset = answer.find('\0', offset);
// On se place après le type, la classe et le "time to live"
offset += 8;
// On récupère la taille des informations supplémentaires
size_t size = ( (unsigned int)answer[offset] << 8) + (unsigned int)answer[offset + 1];
// On se place après la longueur des informations supplémentaires et la préférence.
// Vous pouvez néanmoins récupérer la préférence pour établir une priorité entre les serveurs SMTP à utiliser.
offset += 4;
// On récupère le nom de domaine du serveur SMTP.
std::string hostname = answer.substr(offset, size - 2);
// Mais ce n'est pas fini, ce nom de domaine à un format un peu particulier...
MXserver.push_back( hostnameToString(hostname, answer) ) ;
}
return MXserver ;
}
std::string hostnameToString(std::string hostname, const std::string & response)
{
size_t i = 0;
// On va parcourir notre chaîne de caractères.
while( i < hostname.size() )
{
// Si le caractère vaut 0xc0, cela signifie que la suite du nom de domaine est à rechercher à l'emplacement indiqué par le caractère suivant.
if( hostname[i] == '\xc0' )
{
// On récupère la suite du nom de domaine.
size_t fin = response.find('\0', (unsigned char)hostname[i+1]) - (unsigned char)hostname[i+1];
hostname += response.substr( (unsigned char)hostname[i+1],
fin );
// On supprime le caractère 0xc0 et celui qui suivait.
hostname.erase(i,2);
}
else
{
// Sinon, le caractère sur lequel nous tombons indique la longueur de la partie de nom de domaine qui suit.
size_t size = hostname[i];
if(size)
{
// On le remplace par un « . ».
hostname[i] = '.';
// On se place après la fin de cette partie de nom de domaine.
i += size + 1;
}
else
break; // La taille est nulle, on est arrivé à la fin.
}
}
// Les noms de domaine ne commencent pas par un « . », on le supprime donc.
hostname.erase(0,1);
return hostname ;
}
std::vector<std::string> getMXServer( const std::string & mail, boost::asio::io_service & io_service )
{
// On obtient la liste des serveurs DNS.
std::vector<std::string> DNSnames = getDNS();
// On obtient la requête à envoyer.
std::string queryText = buildDNSQuery(mail) ;
// On obtient la réponse du serveur DNS.
std::string response = askDNS(queryText, DNSnames, io_service);
// On obtient la liste des serveurs MX.
return getXMServerFromAnswer( response, queryText) ;
}
boost::asio::ip::tcp::socket getMXSocket( std::vector<std::string> Mxnames,
boost::asio::io_service & io_service )
{
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::ip::tcp::resolver resolver(io_service);
// Erreur par défaut.
boost::system::error_code error = boost::asio::error::host_not_found;
auto it = Mxnames.begin() ;
auto end = Mxnames.end() ;
// Tant qu'on n'a pas réussi à se connecter, on parcourt la liste des serveurs MX.
while( error && it != end)
{
boost::asio::ip::tcp::resolver::query query(*it, "25");
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::ip::tcp::resolver::iterator end;
// Pour chaque serveur trouvé.
while(error && endpoint_iterator != end )
{
// On essaye de se connecter au serveur.
socket.close();
socket.connect(*endpoint_iterator++, error);
}
++it;
}
// Si aucun serveur n'a été trouvé.
if( error )
throw boost::system::system_error(error);
return socket ;
}
void sendMail( const std::string & destinataire,
const std::string & expediteur,
const std::string & message,
boost::asio::ip::tcp::socket & socket )
{
std::string hello = "HELO Client\r\n";
std::string exp = "MAIL FROM: <" + expediteur + ">\r\n";
std::string dest = "RCPT TO: <" + destinataire + ">\r\n";
std::string beginData = "DATA\r\n";
std::string endData = "\r\n.\r\n";
std::string quit = "QUIT\r\n";
verifyResponse("220", socket);
boost::asio::write(socket, boost::asio::buffer(hello) );
verifyResponse("250", socket);
boost::asio::write(socket, boost::asio::buffer(exp) );
verifyResponse("250", socket);
// Vous pouvez répéter cette action si vous souhaitez envoyer l'e-mail à plusieurs destinataires
// dont l'adresse e-mail a le même nom de domaine.
boost::asio::write(socket, boost::asio::buffer(dest) );
verifyResponse("250", socket);
boost::asio::write(socket, boost::asio::buffer(beginData) );
verifyResponse("354", socket);
boost::asio::write(socket, boost::asio::buffer(message) );
boost::asio::write(socket, boost::asio::buffer(endData) );
verifyResponse("250", socket);
boost::asio::write(socket, boost::asio::buffer(quit) );
verifyResponse("221", socket);
socket.close();
}
std::string buildMessage( const std::string & expediteur,
const std::vector<std::string> & listDestinataires,
const std::string & subject,
const std::string & htmlMessage,
const std::string & rawMessage,
const std::string & sectionWord = "08zs01293eraf47a7804dcd17b1e")
{
std::string message = "Mime-Version: 1.0\r\n";
// On renseigne l'expéditeur.
message += "from: automatique<" + expediteur + ">\r\n";
// On renseigne tous les destinataires.
// Ils doivent être au format alias<e-mail>, alias étant facultatif.
// Exemple : toto<toto@foo.fr>
for( const auto & destinataire : listDestinataires)
message += "to: "+ destinataire + "\r\n";
// Ajout du sujet du message.
message += "subject: " + subject + "\r\n";
// On a un message contenant plusieurs formats.
// Les différents formats seront séparés par le mot sectionWord.
message += "Content-Type: multipart/alternative; boundary=" + sectionWord + "\r\n";
message += "\r\n";
// Début du premier format.
message += "\r\n--" + sectionWord + "\r\n";
// Texte brut.
message += "Content-type: text/plain; charset=ISO-8859-1\r\n";
message += "\r\n";
// Texte brut ASCII.
// Pensez toujours à intégrer ce format à vos messages car tous les clients de messagerie ne supportent pas le HTML.
message += rawMessage ;
message += "\r\n";
// Fin de ce format et début de la prochaine.
message += "--" + sectionWord + "\r\n";
// Texte HTML.
message += "Content-type: text/html; charset=ISO-8859-1\r\n";
message += "\r\n";
// Texte HTML.
message += htmlMessage ;
message += "\r\n";
// Fin de l'e-mail.
message += "\r\n--" + sectionWord + "--\r\n";
return message ;
}
void sendMail( const std::string & destinataire,
const std::string & expediteur,
const std::vector<std::string> & listDestinataires,
const std::string & subject,
const std::string & htmlMessage,
const std::string & rawMessage,
const std::string & sectionWord,
boost::asio::io_service & io_service )
{
// On construit le message
std::string message = buildMessage( expediteur, listDestinataires, subject, htmlMessage, rawMessage, sectionWord) ;
// On recherche les serveurs SMTP.
std::vector<std::string> MXnames = getMXServer( destinataire, io_service ) ;
// On obtient un socket sur un serveur SMTP.
boost::asio::ip::tcp::socket socket = getMXSocket( MXnames, io_service ) ;
// On envoie l'e-mail.
sendMail( destinataire, expediteur, message, socket ) ;
} |
Partager