Am writing simple program for searching through LAN, program have few options how to search (check single IP address, check hostname, check local machine subnet....etc).
And am stuck on "check local machine subnet" (atleast i think it could be a problem)!?
Ok, here is what might be a problem so i would like to know if there is a way to handle that in c++.
When i use getadapteraddresses()to get IP address and subnet mask so i could search local machine subnet, my doubts are what if getadapteraddresses() return subnet - 255.255.254.0 and IP - 192.160.5.10. First step is to search 192.168.5.0-255, but what next, what range should i search 192.168.6.0-255 or 192.168.4.0-255 ..?
If there is way to determine this in c++, any hint would be great.
Thank you :)
You can compute all addresses with something like
int count = (~netmask) & 0x7FFFFFFF;
int network = ip_address & netmask;
for (int i=1; i<count; i++) {
int addr = network + i;
...
}
Dude I think you got a wrong value. 192.168.5.x is not in the range of 255.255.254.0. This subnet masks allows adresses from 192.168.0.0 to 192.168.1.255. If the 5 is host part the mask has to be 255.255.248.0.
Related
I have a host with two interfaces. In my specific case I am trying to join a multicast group using boost::asio::ip::multicast::join_group which appears to only work if i use the constructor that includes the local address. However i do not know in advance the ip address of the local interface connected to the remote host that will do the multicasting. I do however know that it will be eth1. Of course, i could make it configurable but that seems like introducing a useless opportunity to misconfigure, seeing how the same address would have to be configured for the interface and my application.
Ideally there would be a glaringly obvious way to create a boost::asio::endpoint or boost::asio::address from an interface instead of an address that i somehow missed. Alternatively i would of course be just as happy with any other way to deduce an interfaces Ip that works both, with and without a DHCP-Server supplying the Ip.
Is there a proper way to do either or should i just trust users to never fumble with the configuration?
To make sure that this is not completely a XY Problem, here is the code i used while testing to join a multicast group:
m_socket.open(boost_ip::udp::v4());
m_socket.bind(boost_ip::udp::endpoint(boost_ip::udp::v4(), listeningPort));
m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
m_socket.set_option(boost::asio::ip::multicast::join_group(
boost::asio::ip::address::from_string("225.x.x.10").to_v4(), // remote
boost::asio::ip::address::from_string("192.x.x.3").to_v4())); // local
This does work but when i discard the last line to not be dependent on the current local address (that might be changed in deployment), i no longer receive any packets.
Multicast uses the IGMP protocol to form a multicast group. Since IGMP operates on the network layer, it requires the local ip address of the end-point that is joining the multicast group.
The application should wait for an event indicating an ip address has been assigned to the ethernet interface and then call the join_group method to join the multicast group.
I came up with an answer to my question myself. This works for me but I will leave the question open in case anyone has a more straightforward solution.
I settled with using <ifaddrs.h> to just find my current ip for the interface and use that to join the multicast group. This is the code i ended up with to determine my ip:
#include <ifaddrs.h>
#include <boost/asio.hpp>
#include <cstring>
std::string getInterfaceAddress(const std::string & interfaceName)
{
ifaddrs* firstNetIf = 0;
getifaddrs(&firstNetIf);
ifaddrs* netIf = 0;
for(netIf = firstNetIf; netIf != 0; netIf = netIf->ifa_next)
{
if(netIf->ifa_addr->sa_family == AF_INET && std::strncmp(netIf->ifa_name, interfaceName.c_str(), interfaceName.length()) == 0)
{
break;
}
}
unsigned long address =
netIf != 0 ? reinterpret_cast<sockaddr_in*>(netIf->ifa_addr)->sin_addr.s_addr : 0;
if(firstNetIf != 0)
{
freeifaddrs(firstNetIf);
}
return boost::asio::ip::address_v4(htonl(address)).to_string();
}
Of course in my case i could compare with "eth1" and return the boost::asio::ip::address directly but it turns out that this code can be used at one other place as well this way.
I'm currently working on a UDP server.
I want to redirect all incoming packets to the connected clients by using the ip address and port.
My current way of doing it looks like this:
class Connection;
typedef std::map<unsigned short, Connection*> PortMap;
typedef std::map<unsigned int, PortMap> AddressMap;
So I'm basically using two maps. The second one contains a map of all the ports using an ipv4 address(unsigned int) as a key. The PortMap uses the port as the key and it contains a pointer to the Connection class(the clients).
I speed tested it by accessing 64 clients using randomly generated ips and ports and it took ~ (EDIT : 0.4 milliseconds) to access 64 different clients 64 times.
I don't know really if it's slow or not. Of course it depends on the system I'm running the test on.
Here's how I'm accessing the client using the address:
Client * GetClient(Address address)
{
AddressMap::iterator ipIt;
PortMap::iterator portIt;
unsigned int ip = address.GetAddress();
unsigned short port = address.GetPort();
/// Does the ip exist?
if((ipIt = clientAddresses.find(ip)) == clientAddresses.end())
{
return NULL;
}
/// Does the port exist?
if(clientAddresses[ip].find(port) == clientAddresses[ip].end())
{
return NULL;
}
return clientAddresses[ip][port];
}
Does anyone know another faster way of doing it?
Maybe it'll be better to combine IP and Port..
Port < 65535. You can get key: IP * 65535 + Port, it'll be unique for all ports and IPs.
About speed: for example we have N Ip's, each IP has M ports.
Searching into map has efficient N log(N), so your search take N*M*log(N)*log(M).
If you combine IP and port into one key, efficient will be N*M*log(N*M).
log(N*M) = log(N)+log(M) < log(N)*log(M), for big N, M..
So, I think it'll be better.
64 accesses into a map taking 400ms sounds horrifically slow... check your measurements. Your map should probably be based on a combination of IP and port (not separate), since a NAT can combine clients under a specific IP.
This question already has answers here:
Get local IP address in Qt
(6 answers)
Closed 8 years ago.
I am trying to get the local ip address (IPV4) of a computer in QT.
I found the following code:
QNetworkInterface *inter = new QNetworkInterface();
QList<QHostAddress> list;
list=inter->allAddresses();
QString str;
for (int i = 0; i < list.size(); ++i) {
str = list.at(i).toString();
}
Going through for loop I can see there are a number of values (ip's) in the list, one of them is the actual local ip address that I get by typing ipconfig in a command window.
My question is how to distinguish the ip address from all the ip's that are in list?
PCs often have more than one IP address. There's not really such a thing as "the" local IP address; the IP address which would be used when connecting to some remote host depends at least on the local routing table (which may change drastically at any time, e.g. when starting/stopping VPN software).
It seems to me that it makes more sense to think about IPs as valid only in the context of remote networks, e.g. "this is the local IP address I'd use if I were to connect to this host on the Internet; but this is the local IP address I'd use to connect to this host over my company's VPN".
If you want to find out the local IP address which would be used for general-purpose Internet connectivity, the most accurate way I know is simply to do a connection test to a representative host (and a host with high reliability!)
QTcpSocket socket;
socket.connectToHost("8.8.8.8", 53); // google DNS, or something else reliable
if (socket.waitForConnected()) {
qDebug()
<< "local IPv4 address for Internet connectivity is"
<< socket.localAddress();
} else {
qWarning()
<< "could not determine local IPv4 address:"
<< socket.errorString();
}
Note: the above example is blocking, you probably want to rewrite it to use signals and slots if your app has a UI.
I think, several attempts should be tried for increasing the chance of the GUESS (Regardless how clever the software is, it would be still a guess, which won't cover 1% of configurations which will be possibly you case :-)
I've combined and extended both solutions. First I'd check for google DNS, and then for local IPs having a standard gateway. The assumption is: Getaway has the same mask with the addreess ending with ".1". I couldn't find out, how to obtain std. gateway in Qt (which would be more reliable).
Here is the code which works ON MY COMPUTERS:
QTcpSocket dnsTestSocket;
QString localIP="127.0.0.1"; //fall back
QString googleDns = "8.8.8.83"; //try google DNS or sth. else reliable first
dnsTestSocket.connectToHost(googleDns, 53);
if (dnsTestSocket.waitForConnected(3000))
{
localIP = dnsTestSocket.localAddress().toString();
}
else
{
foreach (const QHostAddress &address, QNetworkInterface::allAddresses())
{
QString guessedGatewayAddress = address.toString().section( ".",0,2 ) + ".1";
if (address.protocol() == QAbstractSocket::IPv4Protocol
&& address != QHostAddress(QHostAddress::LocalHost)
)
{
dnsTestSocket.connectToHost(guessedGatewayAddress, 53);
if (dnsTestSocket.waitForConnected(3000))
{
localIP = dnsTestSocket.localAddress().toString();
break;
}
}
}
}
I am working on implementing UpNP on C++, and I need to get the local internal IP address assigned by the router to make the sockets works. The address I need is the one that appears on routers where it shows the computers connected to the router and the local IP assigned to each computer. I am using this:
PHOSTENT Addr = NULL;
char Host[MAX_PATH];
if( gethostname(Host, sizeof(Host)) == 0 )
{
Address = gethostbyname( Host );
if( Address != NULL )
{
//*(struct in_addr *)Address->h_addr_list[0]) <- this is my address
}
}
This works fine on the computer I am testing, but that computer has only one network card, so I was wondering if maybe when a computer has more than one card or network device, Address->h_addr_list[0] may not be the one I need and it could be in another index of that array.
Will [0] always retrieve the IP assigned by the router?
(Assuming winsock here, as per previous question)
You shouldn't assume that the first address is the correct one (as there may be multiple interfaces, and more than one may be active)
I'd recommend enumerating addresses using either getaddrinfo with an empty pNodeName argument, or GetAdaptersAddresses.
Both of these return a linked lists with your system's registered addresses
... get the local internal IP address assigned by the router ...
Note that in some cases, the machine's IP address will be manually assigned, but the user will still want to use UPnP.
On Linux, it is suggested to use getaddrinfo(3) instead of gethostbyname(3), perhaps Winsocks has made a similar transition?
On Linux, it is common for /etc/hosts to have loopback entries also accessible by hostname; /etc/gai.conf can be used to configure the sort order of returned addresses, and possibly a loopback address will be returned. Does Winsock make it that easy for sysadmins to change the order of returned addresses?
Don't forget that a system may legitimately have multiple upstream routers: a laptop with an EV-DO or EDGE or similar cellular data connection and a wireless or wired Ethernet will have multiple IPs, multiple upstream routers, and the routing table will be consulted to figure out which one should be used to send each packet.
Can you use either (a) the address used by clients to contact you? (getsockname(2) will return the local address used on a specific socket.) (b) ask the user to select among the list of IP addresses, if there are several? Binding to N of M interfaces would be nice, so users can select which networks get the services and which networks are left alone.
For a communication between two hosts, I need to send the IP address of my host to the other site. The problem is that if I request my IP address, it might be that I get back my local loopback IP addres (127.x.x.x) , not the network (ethernet) IP address.
I use the following code:
char myhostname[32];
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
if( (my_ip % 256) == 127) {
/* Wrong IP adress as it's 127.x.x.x */
printf("Error, local IP address!");
return;
}
The only way to solve it is to make sure my hostname in /etc/hosts is behind the real network address, not the local loopback (the default for e.g. Ubuntu).
Is there a way to solve this without relying on the content of /etc/hosts?
Edit: I changed the above code so it makes use of getaddrinfo, but I still get back the loopback device's number (127.0,0,1) instead of the real IP address:
struct addrinfo hint = {0};
struct addrinfo *aip = NULL;
unsigned ip = 0;
struct sockaddr_in *sinp = NULL;
hint.ai_family = AF_INET; /* IPv4 */
hint.ai_socktype = SOCK_STREAM;
if(getaddrinfo(hostname, NULL, &hint, &aip) != 0) {
return 0;
}
sinp = (struct sockaddr_in *) aip->ai_addr;
ip = *(unsigned *) &sinp->sin_addr;
(I used to get back a list of 3 addrinfo's with the three SOCK_STREAM,SOCK_DGRAM and SOCK_RAW, but the hint prevents that)
So my question still stands...
There is POSIX function getaddrinfo() that returns linked list of addresses for given hostname, so you just need to go through that list and find non-loopback address.
See man getaddrinfo.
Not an answer, but a relevant comment: be aware that as soon as you start sending addressing information in the content of packets, you run the risk of making your application unable to work across NAT:ing routers and/or through firewalls.
These technologies rely on the information in IP packet headers to keep track of the traffic, and if applications exchange addressing information inside packets, where they remain invisible to this inspection, they might break.
Of course, this might be totally irrelevant to your application, but I thought it worth pointing out in this context.
The originating address will be included in the packet sent... there's no need to duplicate this information. It's obtained when accepting the communication from the remote host (see beej's guide to networking, specifically the part on accept())
I just ran into a situation where when only /etc/hosts has information in it and when I used getaddrinfo to get the IP address list, it returned 127.0.0.1 each time. As it turned out, the hostname was aliased to localhost...something often easy to overlook. Here's what happened:
The /etc/hosts file:
127.0.0.1 localhost.localdomain localhost foo
::1 localhost6.localdomain6 localhost6
172.16.1.248 foo
172.16.1.249 bie
172.16.1.250 bletch
So, now, when you call getaddrinfo with host="foo", it returns 127.0.0.1 3 times. The error here, is that foo appears both on the line with "127.0.0.1" and "172.16.1.248". Once I removed foo from the line with "127.0.0.1" things worked fine.
Hope this helps someone.
Look at this:
Discovering public IP programmatically
Note that in some cases a computer can have more than one non-loopback IP address, and in that case the answers to that question tell you how to get the one that is exposed to the internet.
Even if the computer has only one physical network interface (an assumption that may or may not hold, even netbooks have two - ethernet and WLAN), VPNs can add even more IP adresses. Anyway, the host on the other side should be able to determine the IP your host used to contact it.
Use getaddrinfo()
You're almost there. I'm not sure how you're getting my_ip from hp.
gethostbyname() returns a pointer to a hostent structure which has an h_addr_list field.
The h_addr_list field is a null-terminated list of all the ip addresses bound to that host.
I think you're getting the loopback address because it's the first entry in h_addr_list.
EDIT: It should work something like this:
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
for (int i = 0; hp->h_addr_list[i] != 0; ++i) {
if (hp->h_addr_list[i] != INADDR_LOOPBACK) {
// hp->addr_list[i] is a non-loopback address
}
}
// no address found
If /etc/hosts is still there and still the same, looking for all entries of h_addr_list won't help.
Your new code hardwires the use of IPv4 (in the hint.ai_family field) which is a terrible idea.
Apart from that, you're close, you just should loop through the results of getaddrinfo. Your code just gets the first IP address but there is an aip->ai_next field to follow...
struct addrinfo {
...
struct addrinfo *ai_next; /* next structure in linked list */
};