I have a Debian/linux server which has several Ip adresses, all assigned to the same physical network card. The /etc/network/interfaces
config file looks like this (the xx represent numbers)
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 176.xx.xx.144
netmask 255.255.255.0
network 176.xx.xx.0
broadcast 176.xx.xx.255
gateway 176.xx.xx.254
auto eth0:0
allow-hotplug eth0:0
iface eth0:0 inet static
address 46.xx.xx.57
netmask 255.255.255.255
broadcast 46.xx.xx.57
auto eth0:1
allow-hotplug eth0:1
iface eth0:1 inet static
address 94.xx.xx.166
netmask 255.255.255.255
broadcast 94.xx.xx.166
//IPv6 Stuff...
I am working on a client application that uses Boost Asio to handle all network connections. In this Application I want to be able to connect to an external server using a specific networkinterface/Ip address. I found this similar question, however simply binding a boost::asio::ip::tcp::socket
to a specfic endpoint and then connect to an external Server doesn't work. Here is a minimal working example of what I tried:
#include <iostream>
#include <boost/asio.hpp>
int main( int argC, char *argV[] ) {
boost::asio::io_service ioService;
boost::asio::ip::tcp::socket socket(ioService);
boost::asio::ip::tcp::endpoint localEndpoint(
boost::asio::ip::address::from_string("94.xx.xx.166"), 0);
boost::asio::ip::tcp::resolver resolver(ioService);
boost::asio::ip::tcp::resolver::iterator remoteEndpoint =
resolver.resolve(boost::asio::ip::tcp::resolver::query("haatschii.de", "80"));
socket.open(boost::asio::ip::tcp::v4());
std::cout << "Before binding socket has local endpoint: "
<< socket.local_endpoint().address().to_string()
<< ":" << socket.local_endpoint().port() << std::endl;
socket.bind(localEndpoint);
std::cout << "Before connecting socket has local endpoint: "
<< socket.local_endpoint().address().to_string()
<< ":" << socket.local_endpoint().port() << std::endl;
boost::asio::connect(socket, remoteEndpoint);
std::cout << "After connecting socket has local endpoint: "
<< socket.local_endpoint().address().to_string()
<< ":" << socket.local_endpoint().port() << std::endl;
//Test request to a page that echos our IP address.
boost::asio::write(socket,
boost::asio::buffer("GET /ip.php HTTP/1.1\r\nHost: haatschii.de\r\nAccept: */*\r\n\r\n", 57));
//Parse server response (not important for this code example)
return 0;
}
When I run this on my server I get:
Before binding socket has local endpoint: 0.0.0.0:0
Before connecting socket has local endpoint: 94.xx.xx.166:38399
After connecting socket has local endpoint: 176.xx.xx.144:45959
External server says we are using IP: 176.xx.xx.144
Right now I am a bit lost, because I don't know what else to try. I don't necessarily need a portable solution for this, anything that works with this Debian setup will do.
I'll offer the bounty for a solution that works for my setup. If necessary I can change the /etc/network/interfaces
config file. However in order to reuse my code, any solution has to work with Boost Asio sockets (at least as a wrapper).
To bind to a specific interface you have to open the connection first. You do that - so far so good. But after that you call boost::asio::connect(socket, remoteEndpoint);
which will close the connection for you (as a service so to say).
Boost tells you that it does so - but you have to look closely. In the reference under parameters for the overloaded version of connect
you are using it will say
Parameters
s
The socket to be connected. If the socket is already open, it will be closed.
or in its implementation in boost/asio/impl/connect.hpp:
// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
[...]
template <typename Protocol, typename SocketService,
typename Iterator, typename ConnectCondition>
Iterator connect(basic_socket<Protocol, SocketService>& s,
Iterator begin, Iterator end, ConnectCondition connect_condition,
boost::system::error_code& ec)
{
ec = boost::system::error_code();
for (Iterator iter = begin; iter != end; ++iter)
{
iter = connect_condition(ec, iter);
if (iter != end)
{
s.close(ec);
s.connect(*iter, ec);
if (!ec)
return iter;
}
}
if (!ec)
ec = boost::asio::error::not_found;
return end;
}
(note the s.close(ec);
)
The solution
should be simple. Replace boost::asio::connect...
by
socket.connect(*remoteEndpoint);
(or a loop over the respective remote endpoints, similar to the boost sourcecode, if necessary.)
Generally, you could use the following workflow:
void connect_handler(const boost::system::error_code& error)
{
if (!error) { // Connect succeeded.
}
}
...
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::ip::tcp::endpoint remote_endpoint(
boost::asio::ip::address::from_string("1.2.3.4"), 12345); // server address
socket.open(boost::asio::ip::tcp::v4());
socket.bind(boost::asio::ip::tcp::endpoint(
boost::asio::ip::address::from_string("1.2.3.55"), // your local address
7777)
);
socket.async_connect(remote_endpoint, connect_handler);
More info could be found here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With