I am trying to send an email using the SMTP protocol and boost libraries
but for some reason i am unable to connect to the hotmail smtp server and send email to myself..
this is my code:
what am i doing wrong?
DNS_RECORD* ppQueryResultsSet = NULL;
// dns mx lookup
DnsQuery(L"hotmail.com", DNS_TYPE_MX, DNS_QUERY_STANDARD, NULL, &ppQueryResultsSet, NULL);
if (ppQueryResultsSet == NULL)
return false;
// i am getting the mx servers (i.e: mx3.hotmail.com)
wcout << "MX Host name: " << ppQueryResultsSet->Data.MX.pNameExchange << endl;
DnsRecordListFree(ppQueryResultsSet, DnsFreeRecordList);
string host_name = String_Convertion::WStringToString(ppQueryResultsSet->Data.MX.pNameExchange);
// setting up socket connection
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(host_name, "25");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query), end;
tcp::socket socket(io_service);
// connecting to the server
boost::system::error_code error_code_connect;
boost::asio::connect(socket, endpoint_iterator, end, error_code_connect);
if (error_code_connect)
{
cout << "Error connecting to SMTP Server" << endl;
return false;
}
boost::system::error_code error;
std::string temp_buf;
boost::array<char, 128> buf;
size_t len;
// settings the packets to send
list<string> packets;
packets.push_back("HELO");
packets.push_back("MAIL FROM: xxx#xxx.xxx");
packets.push_back("VRFY xxx#xxx.xxx");
packets.push_back("RCPT TO: x#hotmail.com");
packets.push_back("DATA");
packets.push_back("Subject: subject!");
packets.push_back("hi how are you doing?.");
packets.push_back("QUIT");
list<string>::iterator it;
// iterating to send the packets
for (it = packets.begin(); it != packets.end(); it++)
{
socket.write_some(boost::asio::buffer(*it+"\r\n"));
len = socket.read_some(boost::asio::buffer(buf), error);
temp_buf = buf.data();
temp_buf = temp_buf.substr(0, len);
cout << temp_buf << endl;
}
I think you need to read up a bit on how boost:asio works. Essentially, you are creating a service, but never calling run(). Look at the chat sample here, and note that it spawns a thread exclusively to perform work:
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
Not withstanding that there may be other problems, you need an extra \r\n between the headers of your message and the body.
You also need to correctly terminate the DATA portion by sending a line just containing .\r\n
Related
I am trying to write an async server using asio with SSL encrypted sockets. Currently I have code that does not use SSL, and after following this tutorial I have a basic idea of how to accept an SSL socket, however I do not know how to adapt this code to accept an SSL connection:
void waitForClients() {
acceptor.async_accept(
[this](std::error_code ec, asio::ip::tcp::socket socket) {
if (!ec) {
Conn newConn = std::make_shared<Connection>(ctx, std::move(socket));
connections.push_back(newConn);
} else {
std::cerr << "[SERVER] New connection error: " << ec.message() << "\n";
}
waitForClients();
}
);
}
//this is how the tutorial shows to accept a connection
ssl_socket socket(io_context, ssl_context);
acceptor.accept(socket.next_layer());
The issue is that the callback for acceptor.async_accept gives an ordinary asio::ip::tcp::socket rather than an asio::ssl::ssl_socket<asio::ip::tcp::socket>, and I cannot find any documentation that suggests there is a method of async_accepting an SSL socket in such a way. The only method I have seen is to construct a socket first then accept it afterwards, which cannot be done in this asynchronous manner.
Any help would be much appreciated.
I solved the problem by realising that the second argument to the constructor of asio::ssl::stream<asio::ip::tcp::socket> is any initialiser for the underlying type asio::ip::tcp::socket. Thus the problem can be solved:
void waitForClients() {
acceptor.async_accept(
[this](std::error_code ec, asio::ip::tcp::socket socket) {
if (!ec) {
//initialise an ssl stream from already created socket
asio::ssl::stream<asio::ip::tcp::socket> sslStream(sslCtx, std::move(socket);
//then pass it on to be used
Conn newConn = std::make_shared<Connection>(ctx, sslStream);
connections.push_back(newConn);
} else {
std::cerr << "[SERVER] New connection error: " << ec.message() << "\n";
}
waitForClients();
}
);
}
how to open socket for connecting to VK API, this code works good with other resources, but gives APPCRASH with api.vk.com. In browser it works with http, hence it should work here, and problem is not in 'http`, or am I wrong? Help!
P.S. I'm new to Boost and VK API, so if you can, explain it in details, thank you.
int main()
{
boost::asio::io_service io_service;
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
tcp::resolver::query query("api.vk.com", "http");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
tcp::socket socket(io_service);
boost::system::error_code error = boost::asio::error::host_not_found;
socket.connect(*endpoint_iterator, error);
return 0;
}
It looks like APPCRASH might be a thing you see in the Windows event log.
From that, I formed the hypothesis that you might be running this code in a windows service context.
Windows services do not have network access by default.
This means the DNS lookup may fail. You get an exception, e.g. resolve: Host not found (authoritative). This is what happens in a Linux console when I purposefully change the domain name to a nonexisting TLD:
$ ./test
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
what(): resolve: Host not found (authoritative)
Aborted (core dumped)
Because you don't handle the exception or check for errors, the program is abnormally terminated.
Fixed Demo
Note:
I opted to handle errors rather than exceptions.
You failed to loop through the query results (using only the first without even checking whether it was valid)
Coliru, much like a restricted windows service, does not allow network connectivity outside the loopback adaptor either, so it shows a proper error
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
using boost::asio::ip::tcp;
int main()
{
boost::asio::io_service io_service;
boost::system::error_code error = boost::asio::error::host_not_found;
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
tcp::resolver::query query("api.vk.com", "http");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, error), last;
if (!error) {
// Try each endpoint until we successfully establish a connection.
tcp::socket socket(io_service);
for (;endpoint_iterator != last; ++endpoint_iterator) {
socket.connect(*endpoint_iterator, error);
if (!error) {
std::cout << "Successfully connected to " << endpoint_iterator->endpoint() << "\n";
break; // found working endpoint
} else {
std::cout << "Skipped " << endpoint_iterator->endpoint() << " - not connecting\n";
}
}
return 0;
} else {
std::cout << error.message() << "\n";
return 255;
}
}
On my system it prints
Successfully connected to 87.240.131.97:80
I simply changed my DNS server and it works: Successfully connected to 87.240.131.119:80
Trying to send multiple requests via one connection:
int count = boost::asio::write(socket, request); // this will result in normal count2
int count2 = boost::asio::write(socket, request); // this one gives zero
However cont2 is equal to zero meaning that id does not send data.. Ho to fix it
Adding more code for getting better view:
boost::asio::io_service io_service;
boost::asio::streambuf lastResponse;
// Get a list of endpoints corresponding to the server name.
tcp::resolver resolver(io_service);
char *host = "somewebsie.coom";
char *requestPath = "/somequery";
tcp::resolver::query query(host, "http");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
tcp::socket socket(io_service);
boost::asio::socket_base::keep_alive option(true);
boost::asio::socket_base::non_blocking_io option2(true);
boost::asio::socket_base::non_blocking_io command(true);
double startTime,endTime;
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET "<< requestPath <<" HTTP/1.1\r\nHost: "<<host <<":80\r\n\r\n";
boost::asio::connect(socket, endpoint_iterator);
socket.set_option(option);
socket.io_control(command);
startTime = getRealTime( );
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
int count2 = boost::asio::write(socket, request);
count2 = boost::asio::write(socket, request);
bool isopen = socket.is_open(); // return true...
Why are you calling write with a stream you just emptied?! Trying to write zero bytes is not a good idea.
You're not trying to send multiple requests. There is only one request in your code, and you've already sent it.
Im following the tutorials at the boost official web site http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/tutorial/tutdaytime1.html.
The program is working perfectly if i connect to "localhost" or "127.0.0.1" on the same machine. But if i run the client on another computer with the same network it fails to connect to the server. Why is this happening? and what would i have to do to get the client to run on another network?
Error: connect: No connection could be made because the target machine actively refused it.
Client:
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
try
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
char* serverName = "localhost";
tcp::resolver::query query(serverName, "daytime");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
while(true)
{
boost::asio::connect(socket, endpoint_iterator);
for (;;)
{
boost::array<char, 128> buf;
boost::system::error_code error;
size_t len = socket.read_some(boost::asio::buffer(buf), error);
if (error == boost::asio::error::eof)
break; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error(error); // Some other error.
std::cout.write(buf.data(), len);
std::cout <<"\n";
}
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Server:
#include <iostream>
#include <string>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main()
{
try
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 13));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
std::string message = "This is the Server!";
boost::system::error_code ignored_error;
boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I would guess your problem might be that you return on the first error. Resolving gives you an iterator on a number of endpoints. You try the first of those and if it does not work out you give up instead of letting the iterator go on.
Again, i am by no means an expert in boost::asio and far less in its TCP world but resolve may return more than one endpoint (for example IPv4 and IPv6) and possibly only one of them does not work out here.
For testing you could create the endpoint yourself by first creating a ip::address object, using its from_string() method to give it the address of the server (works only on your local network of course) and then using it for your endpoint:
boost::asio::ip::address address;
address.from_string("the.servers.ip.here");
boost::asio::ip::tcp::endpoint endpoint(address, 13);
boost::asio::connect(socket, endpoint);
And see if that works. If not, it probably is a problem on the server side.
To run the server and client on separate networks, Make the client connect to the servers external ip address. This is obvious but external ip addresses constantly change so to solve this problem you can go to www.noip.com and create a name that links to your ip address. This way in the client all you have to do is specify a name instead of an ip address.
most likely firewall issue, if you are using windows for server check windows firewall, if you are using linux, check the iptables.
I apologize in advance if the question has been previously answered, but I've searched and found nothing that helps me. As indicated by the question's title, I'm trying to broadcast a package from a server to a set of clients listening for any message.
The client will count the number of messages it receives during one second.
The server side of things goes like this:
class Server
{
public:
Server(boost::asio::io_service& io)
: socket(io, udp::endpoint(udp::v4(), 8888))
, broadcastEndpoint(address_v4::broadcast(), 8888)
, tickHandler(boost::bind(&Server::Tick, this, boost::asio::placeholders::error))
, timer(io, boost::posix_time::milliseconds(20))
{
socket.set_option(boost::asio::socket_base::reuse_address(true));
socket.set_option(boost::asio::socket_base::broadcast(true));
timer.async_wait(tickHandler);
}
private:
void Tick(const boost::system::error_code&)
{
socket.send_to(boost::asio::buffer(buffer), broadcastEndpoint);
timer.expires_at(timer.expires_at() + boost::posix_time::milliseconds(20));
timer.async_wait(tickHandler);
}
private:
udp::socket socket;
udp::endpoint broadcastEndpoint;
boost::function<void(const boost::system::error_code&)> tickHandler;
boost::asio::deadline_timer timer;
boost::array<char, 100> buffer;
};
It is initialized and run in the following way:
int main()
{
try
{
boost::asio::io_service io;
Server server(io);
io.run();
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
return 0;
}
This (apparently) works fine. Now comes the client...
void HandleReceive(const boost::system::error_code&, std::size_t bytes)
{
std::cout << "Got " << bytes << " bytes\n";
}
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << " <host>\n";
return 1;
}
try
{
boost::asio::io_service io;
udp::resolver resolver(io);
udp::resolver::query query(udp::v4(), argv[1], "1666");
udp::endpoint serverEndpoint = *resolver.resolve(query);
//std::cout << serverEndpoint.address() << "\n";
udp::socket socket(io);
socket.open(udp::v4());
socket.bind(serverEndpoint);
udp::endpoint senderEndpoint;
boost::array<char, 300> buffer;
auto counter = 0;
auto start = std::chrono::system_clock::now();
while (true)
{
socket.receive_from(boost::asio::buffer(buffer), senderEndpoint);
++counter;
auto current = std::chrono::system_clock::now();
if (current - start >= std::chrono::seconds(1))
{
std::cout << counter << "\n";
counter = 0;
start = current;
}
}
}
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
}
This works when running both the server and client on the same machine, but doesn't when I run the server on a machine different from that of where I run the client.
First thing is, it seems odd to me that I have to resolve the server's address. Perhaps I don't know how broadcasting really works, but I thought the server would send a message using its socket with the broadcast option turned on, and it would arrive to all the sockets in the same network.
I read you should bind the client's socket to the address_v4::any() address. I did, it doesn't work (says something about a socket already using the address/port).
Thanks in advance.
PS: I'm under Windows 8.
I am a bit surprised this works on the same machine. I would not have expected the client, listening to port 1666, to receive data being sent to the broadcast address on port 8888.
bind() assigns a local endpoint (composed of a local address and port) to the socket. When a socket binds to an endpoint, it specifies that the socket will only receive data sent to the bound address and port. It is often advised to bind to address_v4::any(), as this will use all available interfaces for listening. In the case of a system with multiple interfaces (possible multiple NIC cards), binding to a specific interface address will result in the socket only listening to data received from the specified interface[1]. Thus, one might find themselves obtaining an address through resolve() when the application wants to bind to a specific network interface and wants to support resolving it by providing the IP directly (127.0.0.1) or a name (localhost).
It is important to note that when binding to a socket, the endpoint is composed of both an address and port. This is the source of my surprise that it works on the same machine. If the server is writing to broadcast:8888, a socket bound to port 1666 should not receive the datagram. Nevertheless, here is a visual of the endpoints and networking:
.--------.
.--------.|
.--------. address: any address: any .--------.||
| | port: any / \ port: 8888 | |||
| server |-( ----------->| address: broadcast |----------> )-| client ||'
| | \ port: 8888 / | |'
'--------' '--------'
The server binds to any address and any port, enables the broadcast option, and sends data to the remote endpoint (broadcast:8888). Clients bound to the any address on port 8888 should receive the data.
A simple example is as follows.
The server:
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Server binds to any address and any port.
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 0));
socket.set_option(boost::asio::socket_base::broadcast(true));
// Broadcast will go to port 8888.
ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 8888);
// Broadcast data.
boost::array<char, 4> buffer;
socket.send_to(boost::asio::buffer(buffer), broadcast_endpoint);
}
The client:
#include <iostream>
#include <boost/asio.hpp>
int main()
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
// Client binds to any address on port 8888 (the same port on which
// broadcast data is sent from server).
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 8888 ));
ip::udp::endpoint sender_endpoint;
// Receive data.
boost::array<char, 4> buffer;
std::size_t bytes_transferred =
socket.receive_from(boost::asio::buffer(buffer), sender_endpoint);
std::cout << "got " << bytes_transferred << " bytes." << std::endl;
}
When the client is not co-located with the server, then it could be a variety of network related issues:
Verify connectivity between the server and client.
Verify firewall exceptions.
Verify broadcast support/exceptions on the routing device.
Use a network analyzer tool, such as Wireshark, to verify that the time to live field in the packets is high enough that it will not be discarded during routing.
1. On Linux, broadcast datagrams received by an adapter will not be passed to a socket bound to a specific interface, as the datagram's destination is set to the broadcast address. On the other hand, Windows will pass broadcast datagrams received by an adapter to sockets bound to a specific interface.