How to find ip addresses with BSD sockets? - c++

I am using BSD sockets over a wlan. I have noticed that my server computer's ip address changes occasionally when I connect to it. The problem is that I enter the ip address into my code as a literal string. So whenever it changes I have to go into the code and change it there. How can I change the code so that it will use whatever the ip is at the time? This is the call in the server code
if ((status = getaddrinfo("192.168.2.2", port, &hints, &servinfo)) != 0)
and the client side is the same. I tried NULL for the address on both sides, but the client will not connect and just gives me a "Connection refused" error.
Thanks for any help.

Use a domain name that can be looked up in your hosts file or in DNS, rather than an IP address.

How about a command line parameter?
int main( inr argc, char* argv[] ) {
const char* addr = "myfancyhost.domain.com"; /* default address */
if ( argc > 1 ) {
addr = argv[1]; /* explicit address */
}
if ((status = getaddrinfo(addr, ...

Give your server a name, and use gethostbyname to find its address (and, generally, put the server name into a configuration file instead of hard-coding it, though hard-coding a default if you can't find the config file doesn't hurt).

Related

How to fix 'Cannot assign requested address?'

Both the IP address and Port are confirmed not used by netstat -a -n. When I use gdb and break in the method calling bind I see that the correct IP address and Port are being used along with a reasonable socket address length of 16. This is for a UDP Listener. The remote IP is static and read from a configuration file.
This is the code,
void CSocket::Bind(IpEndPoint& endPoint)
{
int bindResult = bind( socketHandle, endPoint.GetSockAddrPtr(),
endPoint.GetAddrLength());
if(bindResult < 0)
{
TRACE_ERROR("Failed to bind to socket. %s. IpAddress %s Port %d AddrLength %d",
strerror(errno), endPoint.GetIpAddressString(),
ntohs(endPoint.GetPort()), endPoint.GetAddrLength());
this is from gdb,
Breakpoint 1, CSocket::Bind (this=0x819fa24, ipAddress="192.0.2.77",
port=4185) at Socket.cpp:126
and this is the TRACE_ERROR from the code above
ERROR: Failed to bind to socket. errno 99 (Cannot assign requested address).
IpAddress 192.0.2.77 Port 4185 AddrLength 16
I've been re-reading Beej's Guide to Network Programming but not finding a clue. This is UDP so a connection should not be required to bind. The firewall is off. Where else should I be looking?
Following on what #Aconcagua said: You want to bind an address that is local (not one that's "not in use"). You can't just make up a local address. You either use INADDR_ANY to bind to any address, or you need to bind one that is assigned to one of your local interfaces. This is likely the problem. (bind sets the local address, connect sets the remote address -- or, with UDP, you can specify the remote address per packet with sendto.) – Gil Hamilton

How to check if a TCP connection is local?

For a project, I need to know whether the network connection is from the local computer or from a remote computer.
How to achieve this?
This can be achieved by utilizing the getpeername and the getsockname functions.
This snipped does exactly what I need it to:
bool checkForLocalConnection(SOCKET Sock) {
sockaddr_in RemAddr, LocAddr;
int Len = sizeof(RemAddr);
getpeername(Sock, (sockaddr *)&RemAddr, &Len);
getsockname(Sock, (sockaddr *)&LocAddr, &Len);
return (RemAddr.sin_addr.S_un.S_addr == LocAddr.sin_addr.S_un.S_addr);
}
The endianess of the result is always the same, which is why you don't even have to convert it to native endianess.
Why this works and why it's necessary:
If you connect to localhost or 127.0.0.1, getpeername will always yield the address 127.0.0.1 (converted to an unsigned long, obviously).
That means, you could just check for htonl(2130706433); and be done with it (Minding the endianess). However if you enter the actual address...or any of your other local addresses your NIC might have, getpeername will return that address, instead of 127.0.0.1.
getsockname will return the local interface this socket is connected on, which means it will choose the correct interface and tell you its address, which is equal only if you're connected from a local machine.
I hope this will help someone, since I had to search forever to find that little info.
It should work for most common cases. (There are some exceptions)
List of exceptions:
Multi-Address network cards. These are on the same machine but either not on the same NIC or bound to a different IP. There isn't that much you can do about that.
Calling localhost on a different IP than 127.0.0.1. getsockname will always return 127.0.0.1, regardless of which 127.x.x.x you're calling. As a 'guard' against that, you can check specifically for the 127 in the first octet of the peer address.
Many thanks for the help with this goes to harper.

How to get local IP address of a computer using QT [duplicate]

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;
}
}
}
}

How to know if a TCP connection is between two processes on the same machine?

Using socket programming APIs (e.g., socket(), connect(), accept() ...), how can I know if a TCP connection is between two processes on the same machine? Say, I have the socket file descriptor, and the remote ip. Can I simply inspect if the remote ip is 127.0.0.1?
There's no really reliable way to determine this - you can connect to local processes using a globally routed IP address (ie, local processes can use IPs other than 127.0.0.1). It's also possible for a process to run in a different virtual machine on the same physical hardware, if you're in a virtualized environment.
Note, however, that if the remote IP (via getpeername) or local IP (via getsockname) starts with 127 (including 127.0.0.1), then it is indeed a local connection; however, you can't rule out the possibility that it might be a local connection if it's a different pair of addresses.
Use getsockname() and getpeername() to retreive the two IPs associated with the connection, then use gethostname() and gethostbyname() (or other platform-specific APIs, like GetAdaptersInfo() and GetAdapterAddresses() on Windows) to determine the IPs that belong to the local machine, then you can compare the connection IPs to the local machine IPs to see if they both match. A machine can have multiple IPs assigned to it, and multiple IPs on the same machine can communicate with each other.
Here is the approach I have used. The idea is to attempt to bind a listener to that IP address and use the failure/success codes to decide whether the address is local.
I am not claiming this is particularly efficient, but it should be fairly reliable, and for my application it was appropriate.
#include <sys/socket.h>
#include <errno.h>
/* ...probably need some other headers I am forgetting... */
int
is_local(const struct sockaddr *addr, socklen_t addr_len)
{
const char *func = "is_local()";
int result = 0;
int tmp = socket(addr->sa_family, SOCK_STREAM, 0);
if (tmp < 0) {
printf("%s: socket(%d,SOCK_STREAM,0) failed, errno %d\n",
func, addr->sa_family);
goto out;
}
/* If bind() succeeds, or if it fails because the address is in
use, then the address must be local to this system. */
if (bind(tmp, addr, addr_len) < 0) {
if (errno == EADDRINUSE)
result = 1;
else if (errno == EADDRNOTAVAIL)
; /* do nothing; address is remote */
else
printf("%s: bind() unexpected error %d\n", func, errno);
}
else {
result = 1;
}
close(tmp);
out:
return result;
}
You call it like this:
struct sockaddr_storage client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int fd = accept(listener, &client_addr, &client_addr_len);
if (is_local((struct sockaddr *)&client_addr, client_addr_len))
/* peer is local */
If you already have the remote ip address, you can check if it is the loopback address or if it is the ip address of the host, because, as cnicutar points out, it doesn't have to be over the loopback address to be a local connection.

How to get Current System IP in C under Windows XP [duplicate]

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 */
};