I want to build an stmp client using c++ for learning purposes.
After I managed to implement the initial connection + auth login I am stuck on sending the message after using the data command.
Here is my code
void sendmail()
{
write_command("MAIL FROM: <foo#bar.de>");
write_command("RCPT TO: <bar.foo#baz.de>");
write_command("DATA");
write_command("Subject: testmail"); // HANGS here after data command
write_command("BlaBlub");
write_command(" ");
write_command(".");
write_command("QUIT");
}
void write_command(std::string command)
{
ssize_t n;
empty_buffer();
command += '\r';
command += '\n';
char command_buffer[255];
strcpy(command_buffer, command.c_str());
n = write(sockfd,command_buffer,strlen(command_buffer));
if (n < 0){
error("ERROR writing to socket");
}
n = read_to_buffer();
if (n < 0) {
error("ERROR reading from socket");
}
printf("%s\n",this->buffer);
}
I'm using smtp.mailtrap.io on port 25.
Here is a gist with the full class https://gist.github.com/xhallix/7f2d87a8b2eab4953d161059c2482b37
Here is the server output
Starting smpt client
220 mailtrap.io ESMTP ready
250-mailtrap.io
250-SIZE 5242880
250-PIPELINING
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-AUTH PLAIN LOGIN CRAM-MD5
250 STARTTLS
334 VXNlcm5hbWU6
334 UGFzc3dvcmQ6
235 2.0.0 OK
250 2.1.0 Ok
250 2.1.0 Ok
354 Go ahead
(HANGS HERE)
Thanks for helping me out
DATA command expects the whole mail message, as shown here. The write_command() sends a message by lines and expects response after each line. Since the server returns the response once the mail message is finished (after empty line and dot), it stays in the hanging mode after the first message line. This code snippet can be helpful for your case.
BTW, you should put an empty line between the mail header and body, which I guess is after the subject line. Also, it might happen that the server rejects the message without the From and To headers.
Related
I'm using Mailgun through my local installation of Mautic. It used to connect correctly. Today however I got this error message: Unable to connect with TLS encryption Log data: ++ Starting Swift_SmtpTransport << 220-node6237.myfcloud.com ESMTP Exim 4.95 #2 Tue, 12 Apr 2022 13:38:14 +0000 220-We do not authorize the use of this system to transport unsolicited, 220 and/or bulk e-mail. >> EHLO dashboard.nsking.ee << 250-node6237.myfcloud.com Hello dashboard.nsking.ee [194.233.160.33] 250-SIZE 52428800 250-8BITMIME 250-PIPELINING 250-PIPE_CONNECT 250-AUTH PLAIN LOGIN 250-STARTTLS 250 HELP >> STARTTLS << 220 TLS go ahead !! Unable to connect with TLS encryption (code: 0) ++ Starting Swift_SmtpTransport << 220-node6237.myfcloud.com ESMTP Exim 4.95 #2 Tue, 12 Apr 2022 13:38:14 +0000 220-We do not authorize the use of this system to transport unsolicited, 220 and/or bulk e-mail. >> EHLO dashboard.nsking.ee << 250-node6237.myfcloud.com Hello dashboard.nsking.ee [194.233.160.33] 250-SIZE 52428800 250-8BITMIME 250-PIPELINING 250-PIPE_CONNECT 250-AUTH PLAIN LOGIN 250-STARTTLS 250 HELP >> STARTTLS << 220 TLS go ahead !! Unable to connect with TLS encryption (code: 0)
What is the cause of it? Keep in mind, nothing has changed in our installation except the server name and the IP.
I tried to change to SSL and I got this error:
Connection could not be established with host smtp.mailgun.org :stream_socket_client(): Peer certificate CN=node6237.myfcloud.com' did not match expected CN=smtp.mailgun.org' Log data: ++ Starting Swift_SmtpTransport !! Connection could not be established with host smtp.mailgun.org :stream_socket_client(): Peer certificate CN=node6237.myfcloud.com' did not match expected CN=smtp.mailgun.org' (code: 0)
++ Starting Swift_SmtpTransport !! Connection could not be established with host smtp.mailgun.org :stream_socket_client(): Peer certificate CN=node6237.myfcloud.com' did not match expected CN=smtp.mailgun.org' (code: 0)
Domain is fully verified but sometimes we did get FROM 0101016efadf82da-b7e07022-37ba-4cae-aa6c-780052992485-000000#us-west-2.amazonses.com instead of using FROM from our domain this is log can you show me what could be possible the reason ?
<< 220 email-smtp.amazonaws.com ESMTP SimpleEmailService-d-XTDLI25GD
>> EHLO [IP]
<< 250-email-smtp.amazonaws.com
250-8BITMIME
250-SIZE 10485760
250-STARTTLS
250-AUTH PLAIN LOGIN
250 Ok
>> STARTTLS
<< 220 Ready to start TLS
>> EHLO [IP]
<< 250-email-smtp.amazonaws.com
250-8BITMIME
250-SIZE 10485760
250-STARTTLS
250-AUTH PLAIN LOGIN
250 Ok
>> AUTH LOGIN
<< 334 val
>> val
<< 334 val
>> Qk8=
<< 235 Authentication successful.
++ Swift_SmtpTransport started
>> MAIL FROM:<12345#example.com>
<< 250 Ok
>> RCPT TO:<500500500#msg.fi.google.com>
<< 250 Ok
>> DATA
<< 354 End data with <CR><LF>.<CR><LF>
>>
.
<< 250 Ok
First to understand, there are two things in SMTP:
Mailfrom (Envelope From): It's a SMTP command
From header (thats something you see in Outlook)
SES always changes Mailfrom address to message-id#amazonses.com or messageid#us-west-2.amazonses.com (if region used other than us-east-1).
SES does that so you can always pass in SPF. SPF check happens on mailfrom domain which is now us-west-2.amazonses.com and Amazon publishes the TXT/SPF record for it so you don't need to configure it your domain.
AWS SES provides an option to use custom mail from where you can use your own mailfrom but in this case you would need to publish a TXT record so SPF can be passed.
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/mail-from.html
Generally, you don't see Mailfrom in outlook or any webmail. The reason you're seeing it because in your telnet test, you're not adding from header. Try below test: (Add DATA command after "rcpt to" and add below line)
>> MAIL FROM:<12345#example.com>
<< 250 Ok
>> RCPT TO:<500500500#msg.fi.google.com>
Data
From: 12345#example.com
To: 500500500#msg.fi.google.com
Subject: Test
.
Once you add the From header, you should be able to see correct address in outlook or webmail.
I've written a simple web service using pistache. I'm seding requests to it using sf::Http and sf::Http::Request classes. However, call of sf::Http::sendRequest never returns, even though I specified a 250 ms timeout. The thing happens only with requests to my website. If I send GET request to www.google.com the method returns correct response quite quickly.
Here's the client-side code sample:
sf::Http http("http://192.168.1.10", 8080);
sf::Http::Request request("/highscores", sf::Http::Request::Method::Get);
request.setHttpVersion(1, 1);
//the call below never returns
auto response = http.sendRequest(request, sf::seconds(0.25f));
std::cout << response.getBody();
The service response seems correct in browser and in curl:
$ curl -v 192.168.1.10:8080/highscores
* Trying 192.168.1.10...
* Connected to 192.168.1.10 (192.168.1.10) port 8080 (#0)
> GET /highscores HTTP/1.1
> Host: 192.168.1.10:8080
> User-Agent: curl/7.47.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Connection: Keep-Alive
< Content-Length: 2
<
* Connection #0 to host 192.168.1.10 left intact
[]%
Using strace on my application shows that it sends correct request and even at some point it receives the correct response:
$ strace -s 192 ./sfmlApplication
...
sendto(20, "GET /highscores HTTP/1.1\r\nconnection: close\r\ncontent-length: 0\r\ncontent-type: application/json\r\nfrom: user#sfml-dev.org\r\nhost: 192.168.1.10\r\nuser-agent: libsfml-network/2.x\r\n\r\n", 176, MSG_NOSIGNAL, NULL, 0) = 176
recvfrom(20, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: Keep-Alive\r\nContent-Length: 2\r\n\r\n[]", 1024, MSG_NOSIGNAL, NULL, NULL) = 96
recvfrom(20,
These are the last lines from strace output, after recvfrom(20, the program stops responding and has to be killed.
And the top of stack trace of blocked operation is:
recv() at 0x7ffff7bcd10f
sf::TcpSocket::receive() at 0x7ffff77b12c0
sf::Http::sendRequest() at 0x7ffff77ad5ed
SFML Version: 2.3.2
System: Fedora 4.8.4-200.fc24.x86_64
Any ideas why the sf::Http::sendRequest method call never returns?
Suppose I want to connect to FTP server, then list root directory in active mode (via PORT command). For this I'm going to use cURL library.
Seems, like cURL provides such capability. There's CURLOPT_FTPPORT option and the official reference says, that the port may be optionally followed by a '-' to specify a port range.
if (CURL* curl = ::curl_easy_init())
{
guard.reset(curl, ::curl_easy_cleanup);
::curl_easy_setopt(curl, CURLOPT_URL, "ftp://10.0.0.162:21");
::curl_easy_setopt(curl, CURLOPT_FTPPORT, "localhost:37000-38000");
CURLcode retval = ::curl_easy_perform(curl);
if (retval != CURLE_OK)
{
throw NetworkError(retval, ::curl_easy_strerror(retval));
}
}
...
But here's a problem.
If the first port in the range is not available, curl_easy_perform fails with CURLE_FTP_PORT_FAILED.
EDIT
Output in verbose mode:
* Rebuilt URL to: ftp://10.0.0.162:21/
* timeout on name lookup is not supported
* Trying 10.0.0.162...
* Connected to 10.0.0.162 (10.0.0.162) port 21 (#0)
< 220 eugee-server / banner
> USER anonymous
< 331 Anonymous access allowed, send identity (e-mail name) as password.
> PASS ftp#example.com
< 230-Welcome to ftp.eugeeserver
< 230 User logged in.
> PWD
< 257 "/" is current directory.
* Entry path is '/'
* socket failure: Bad access
* Remembering we are in dir ""
* Connection #0 to host 10.0.0.162 left intact
Hello and good evening to you,
This topic has been sort of a trouble to me and to many, suppose i want to send EMail in a C++ program to use smtp and StartTLS , what do i do, i culled a simple source code from google and i saw this code from here
http://www.drdobbs.com/sending-e-mail-using-smtp-and-winsock/184416591
now i want to use google mail and it uses authentication for smtp and also startTLS how do i do this
the sourcecode i saw looks like this
#pragma comment(lib, "wsock32.lib")
#include <windows.h>
#include "MailMessage.h"
int main(int argc, char **argv)
{
MailMessage mail("A Sender",
"someone#someplace.com",
"mail.someplace.com");
mail.To("A Recipient",
"you#yourplace.com");
mail.Subject("Sample message");
mail.Body("Plain text body",
"<HTML><BODY>\r\n"
" <H2>HTML Body</H2>\r\n"
"</BODY></HTML>""\r\n");
mail.Attach("C:\\Attach.txt");
const char *result =
mail.Send().data();
if (result[0] == '\0')
result = "Success";
MessageBox(NULL, result, "Result",
MB_ICONINFORMATION|MB_OK);
return 0;
}
After connecting to the server and issuing a EHLO (not HELO) command, if the server's reply includes the STARTTLS capability then you can issue a STARTTLS command at any time to create a secure session with the server. Upon receiving a successful STARTTLS reply, you need to send and complete an SSL/TLS handshake. Once the session has been created, you can continue sending your SMTP commands and receiving SMTP replies, starting with a new EHLO command (as the server's capabilities can change after the connection is secured). You have to encrypt your commands and decrypt the replies as you go. The communication would look like this (this example assumes an Application-Specific password has been configured in GMail if two-step verification is enabled):
S: 220 smtp.gmail.com ESMTP dg12sm55710335pac.47 - gsmtp
C: EHLO <hostname>
S: 250-smtp.gmail.com at your service, [<ip address>]
S: 250-SIZE 35882577
S: 250-8BITMIME
S: 250-STARTTLS
S: 250-ENHANCEDSTATUSCODES
S: 250-PIPELINING
S: 250-CHUNKING
S: 250 SMTPUTF8
C: STARTTLS
S: 220 2.0.0 Ready to start TLS
C/S: (Exchange SSL/TLS handshake)
C/S: (Everything from here on is now encrypted)
C: EHLO <hostname>
S: 250-smtp.gmail.com at your service, [<ip address>]
S: 250-SIZE 35882577
S: 250-8BITMIME
S: 250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
S: 250-ENHANCEDSTATUSCODES
S: 250-PIPELINING
S: 250-CHUNKING
S: 250 SMTPUTF8
C: AUTH LOGIN
S: 334 VXNlcm5hbWU6
C: (Send base64 encoded username)
S: 334 UGFzc3dvcmQ6
C: (Send base64 encoded password)
S: 235 2.7.0 Accepted
C: (Send email as needed)
C: QUIT
S: 221 2.0.0 closing connection m1sm91929700pfi.27 - gsmtp
Now, how you actually handle the encryption is up to you. You can use a library like OpenSSL, or you can use Microsoft's Crypto/SChannel API. There are plenty of online tutorials and books on how to use them with sockets.