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;
}
}
}
}
Related
I tried to bind my socket to some random address 10.1.1.1:12001, and got QAbstractSocket::SocketAddressNotAvailableError.
Than i wrote a simple code:
for (int i = 0; i < 256; i++) {
QHostAddress address0(QString::number(i) + ".0.0.1");
quint16 port = 12101;
QUdpSocket* m_socket = new QUdpSocket();
if (m_socket->bind(address0, port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) {
qDebug() << i;
}
}
Only to learn that IP must start with 127 or 224-239. So it must be Class D address.. But I just didn't find anything in the qt documentation.
Is it a normal behaviour? Is there a possibility to use a global net IP for binding? Or at least use 192.168.x.x as IP of another computer in LAN?
You are not allowed to bind the socket to an arbitrary network IP address. You can only do it with an IP address of one of your network devices or some special IP addresses like 0.0.0.0. By default you always have the 127.0.0.1 but surely you have another network address in your computer.
By the other way if you want to write/read some data over an UDP Socket It's not necessary to bind it to a network address, you can use writeDatagram() or readDatagram() methods of the QUdpSocket class
Question:
Is there a way of using ioctl to change only a desired interface component without affecting the other parts of the network interface?
Reasoning
I'm writing a C++ program that allows a user to change the IP address, Broadcast address, Netmask, and Default Gateway independently of one another on a Linux machine. I modified this code for the IP, Bcast and NMask solution. However, changing the IP address with ioctl is automatically modifying my Broadcast/Netmask, and clearing the Kernel IP Routing table.
Here's an example. Before running the code below, this is the result of ifconfig and route -n:
This is a functional version of the code that modifies the IP address:
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <string.h>
#include <iostream>
int main(int argc, const char *argv[]) {
struct ifreq ifr;
struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr;
const char * name = "eth0";
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
inet_pton(AF_INET, "10.10.2.59", &addr->sin_addr);
if(ioctl(fd, SIOCSIFADDR, &ifr) < 0)
{
std::cout << "Failed to set IP: " << strerror(errno) << std::endl;
return -1;
}
if(ioctl(fd, SIOCGIFFLAGS, &ifr) < 0)
{
std::cout << "Failed to get flags: " << strerror(errno) << std::endl;
return -2;
}
strncpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
if(ioctl(fd, SIOCSIFFLAGS, &ifr) < 0)
{
std::cout << "Failed to set flags: " << strerror(errno) << std::endl;
return -3;
}
return 0;
}
This is the resulting network card status after running the above program:
As you can see, the IP address was modified as desired, but the rest of the interface changed (which is not desirable).
I've been unable to find anything on the internet or the netdevice man page about preventing ioctl from automatically modifying other parts of the network interface.
I know I could set the values of struct ifreq so the Bcast and Mask components don't change, but I would prefer to be able to modify each component individually without worrying about the values of the other ones. I especially don't want to keep track of the default gateway and have to add it in every time I make a change to the IP address.
UPDATE
After doing more tests and research, I've found that this issue still occurs when running the system commands ifconfig or ip. For example, if instead of running the above code one ran ifconfig eth0 10.10.2.59 the result is the same.
Using the ip command is somewhat different, as changing the ip address requires running ip addr del 10.10.2.58/16 dev eth0 && ip addr add 10.10.2.59/16 dev eth0. Thus, you delete a known address/netmask combo and add another. Since the broadcast address was not specified, it is set to 0.0.0.0. However, this command still removes the default gateway from the routing table.
It seems (through experimentation) that there is some form of prioritized ordering of ioctl flags in how they affect the rest of the network interface:
SIOCSIFADDR forces a reset of the Netmask, Broadcast Address, and Routing Table entries for the given network interface
SIOCSIFNETMASK forces a reset of the Broadcast Address and Routing Table
SIOCSIFBRDADDR forces a reset of only the Routing Table
The implication of this is that the order in which you run ioctl calls matters - first you must set IP, then Subnet Mask, then Broadcast Address, then any Routes. Otherwise ioctl automatically overwrites changes you made previously.
Because of that, I ended up having to keep track of all sub-components that were affected when changing only part of the network interface.
For example, if the subnet mask is going to be changed I first read the old Broadcast Address (an ioctl call with the SIOCGIFBRDADDR flag) and Default Gateway (see here on reading that programatically) and store them. Then after changing the subnet mask with ioctl, I re-assigned the Broadcast Address and Default Gateway (in that order) and it appears to the user that only the subnet mask was changed.
This doesn't exactly answer the original question, but I couldn't find any other way of modifying only one component of a network interface without affecting the others. If anyone finds a better way of doing it I would be very happy to know.
I need to change my host IP Address on Linux app using Qt. I have readed documentation about QHostAddress and method setAddress in this class that say the following:
"Sets the IPv4 or IPv6 address specified by the string representation >specified by address (e.g. "127.0.0.1"). Returns true and sets the address >if the address was successfully parsed; otherwise returns false."
I know that it is possible using QProccess but I'm trying to use Qt-way in order to do that. I'm very confused becuase my app is not in running with root privileges so I find very difficult perform this action using Qt class directly. Then I try this:
QHostAddress hostAddress;
bool ipChange = hostAddress.setAddress("192.168.1.143");
if(ipChange) qDebug() << "IP ADDRESS CHANGED";
else qDebug() << "IP ADDRESS NOT CHANGED";
The result of this code is "IP ADDRESS CHANGED" but doing ifconfig in a terminal, my IP address has not been modified. So, my questions are:
How I can do that?
Why I can see IP ADDRESS CHANGED if this method obviously doesn't works?
You are changing the address stored in hostAddress. You can now use hostAddress to (e.g.) open a stream socket to a port on 192.168.1.143. This has no relation to any IP addresses of the host you happen to be running on - QHostAddress is just a representation of any IP address.
To set an address for a network interface on the host machine, you will need to be root, and to use the native facilities (or an external process - /sbin/ifconfig, for example).
In my application I want to get the hostname & MAC address from an IP address (in my LAN).
I used this code to find the hostname, but nothing appeared in lineedit.
QHostInfo HI;
QHostAddress HA("192.168.1.1");
QList<QHostAddress> List;
List.append(HA);
HI.setAddresses(List);
ui->ledHostname->setText(HI.hostName());
To retrieve the Hostname from an IP address you can call lookupHost(), which takes the host name or IP address, a receiver object, and a slot signature as arguments. The slot is invoked when the results are ready. The results are stored in a QHostInfo object. Call addresses() to get the list of IP addresses for the host, and hostName() to get the host name that was looked up.
QHostInfo::lookupHost("92.168.1.1",
this, SLOT(lookedUp(QHostInfo)));
void MyWidget::lookedUp(const QHostInfo &host)
{
if (host.error() != QHostInfo::NoError) {
qDebug() << "Lookup failed:" << host.errorString();
return;
}
foreach (const QHostAddress &address, host.addresses())
qDebug() << "Found address:" << address.toString();
}
For obtaining the MAC address of a remote IP you should use system commands and platform-specific code. There is no way in Qt to do that. For example on Windows it can be done by:
arp -a <IP>
I used this code:
QHostInfo HI = QHostInfo::fromName("192.168.1.50");
ui->ledHostname->setText(HI.hostName());
Worked for some ip addresses & shows the host name! For other ip addresses shows the ip address again.
For my MAC problem, I`m using ARP packet.
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 */
};