Per this question:
How to get local IP address of Windows system?
#Remy Lebeau answered that GetAdaptersAddresses() was a way to get the IP address of the local machine in Windows using C++.
I compiled the example and the example does not print out the local IP address of the machine. I took a look at the struct that the function returns (IP_ADAPTER_ADDRESSES_LH) and I was surprised to find that I did not see any references to where the actual IP address is.
My question is, where is the IP address located in the IP_ADAPTER_ADDRESSES_LH struct?
I compiled the example and the example does not print out the local IP address of the machine.
GetAdaptersAddresses() has always worked fine for me.
where is the IP address located in the IP_ADAPTER_ADDRESSES_LH struct?
There are potentially many IP addresses in the struct, depending on what kind of IP address you are interested in - Unicast, Anycast, Multicast, or DnsServer. For local assigned IPs, you would typically use the Unicast addresses only:
The IP_ADAPTER_ADDRESSES_LH::FirstUnicastAddress field points to a linked list of IP_ADAPTER_UNICAST_ADDRESS_LH structs, one entry for each IP address. Use the IP_ADAPTER_UNICAST_ADDRESS_LH::Next field to loop through the list (the MSDN example shows such a loop, but it only counts the number of elements in the list, it does not print out the content of the list).
The IP_ADAPTER_UNICAST_ADDRESS_LH::Address field contains the actual IP address, in SOCKET_ADDRESS format.
the SOCKET_ADDRESS::lpSockaddr field is a SOCKADDR* pointer. You can pass it as-is to socket APIs like bind().
If you want to do something with the IP address (like display it), you have to type-cast the SOCKET_ADDRESS::lpSockAddr pointer to either sockaddr_in* or sockaddr_in6*, depending on whether the IP address is IPv4 or IPv6, respectively (use the SOCKADDR::sa_family field to determine the correct type - AF_INET for sockaddr_in, AF_INET6 for sockaddr_in6). Then you can access the sockaddr_in::sin_addr or sockaddr_in6::sin6_addr field as needed, which contain the actual bytes of the IP address.
For example:
PIP_ADAPTER_ADDRESSES pAddresses = ...; // allocate buffer as needed...
ULONG ulSize = ...; // size of allocated buffer...
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, pAddresses, &ulSize) == 0)
{
for (PIP_ADAPTER_UNICAST_ADDRESS_LH *pAddress = pAddresses->FirstUnicastAddress;
pAddress != NULL;
pAddress = pAddress->Next)
{
SOCKADDR *pSockAddr = pAddress->Address.lpSockaddr;
switch (pSockAddr->sa_family)
{
case AF_INET: {
sockaddr_in *pInAddr = (sockaddr_in*) pSockAddr;
// use pInAddr->sin_addr as needed...
break;
}
case AF_INET6: {
sockaddr_in6 *pIn6Addr = (sockaddr_in6*) pSockAddr;
// use pIn6Addr->sin6_addr as needed...
break;
}
}
}
}
// free pAddresses as needed ...
Related
Hello i am having some problem getting network card address information from the WinPcap driver. I have noticed that whenever IPv6 is enabled for a particular NIC, the address information for it becomes 0.0.0.0. How can i fix this?
pcap_addr* address = GetDeviceAddress(1);
cout<<"IP address "<<iptos(((struct sockaddr_in *)address->addr)->sin_addr.s_addr)<<endl;
/////////////
pcap_addr* GetDeviceAddress(int index)//Gets the name of a device using the zero based index of its location in the list
{
int i=0;
if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&AllDevices,errbuf) != -1)
for(Iterator=AllDevices;Iterator!=NULL;Iterator=Iterator->next)
{
if(i==index)
{
return Iterator->addresses;
}
i++;
}
return NULL;
}
What does your code that's looking at the address information look like? Perhaps it has a bug that causes it not to properly handle IPv6 addresses.
The address information may have more than one IP address - libpcap/WinPcap's pcap_findalldevs() call and WinPcap's pcap_findalldevs_ex() call supply a pointer to a list of pcap_if_t structures, one for each interface that it found, and each of those structures includes a list of address information items of type struct pcap_addr. Each of those address information items includes a network address, which is of type struct sockaddr; that structure includes a member named sa_family, which will have the value AF_INET for IPv4 and AF_INET6 for IPv6. If the member has the value AF_INET6, the address data will be 128 bits of IPv6 address, NOT 32 bits of IPv4 address.
I have a socket server that listens and accepts connections from client, which works as follow:
... do some pre-processing (socket, binds, etc)
//listen to client
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof client_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
//do something .....
.....
}
How can I restrict the server so it only accepts connection from specific IP addresses? For instance, I can create a text file containing a white list of IP addresses to accept, in the following format:
202.168.2.5 - 202.168.2.127
92.104.3.1 - 92.104.4.254
//and so on
So basically I want to reject connection from all the IP addresses not included in the whitelist. If the socket library API does not support this, I am okay with the idea of accepting the connections first, then just immediately close the socketfd if the peeraddress is not in the whitelist. But how to perform this, how can I check that a specific IP address is within the range specified in my whitelist? Any examples would be appreciated.
You want to call getpeername to get the address information from the client. Then check if their IP address is found in the whitelist. If not, disconnect them.
In order to check that their ip address lies within a given range, you want to convert the address bytes into one number. You can do that with the following:
unsigned int n = bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3];
If the lower bound of the address range is A, and the upper bound is B, and the client's ip address is X, then they are white listed if (A <= X && X <= B).
If each range of ip addresses tests false, then they aren't on the white list and you should disconnect them.
Not sure what the question is here, or rather what the problem is. The client's address will be in their_addr, so just search your whitelist for that. If not found, close. You will probably want to either convert their_addr into the same format as your whitelist entries, or possibly vice versa.
On Windows only, you can use WSAAccept() instead of accept(). WSAAccept() has a parameter that you can pass a callback function to. Before a new connection is accepted, the callback is invoked with the addresses and QOS values for that connection. The callback can then return CF_ACCEPT, CF_DEFER, or CF_REJECT as needed.
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.
In my Linux C++ application I'm using getpeername and getsockname.
when IPv6 enabled on the OS, both getpeername and getsockname return only port!
code:
int GetSockAndPeer(int sock)
{
struct sockaddr_storage ss;
socklen_t salen = sizeof(ss);
struct sockaddr *sa;
struct addrinfo hints, *paddr, *paddrp;
sa = (struct sockaddr *)&ss;
if (getpeername(sock, sa, &salen) != 0)
{
return -1;
}
if (getsockname(sock, sa, &salen) != 0)
{
return -1;
}
}
sa variable hold after the systemcalls in sa_data only the sa_data[0] and sa_data[1] which means port. all the other bytes are 0;
Any help???
Related to RFC2553 you have to use the IN6_IS_ADDR_V4MAPPED and IN6_IS_ADDR_V4COMPAT macros to identify if there is any usable IPv4 information available within yours socket_storage, or to be exact the sockaddr_in6 structure:
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 traffic class & flow info */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* set of interfaces for a scope */
};
If both macros returns true, the IPv4 address is in sockaddr_in6.sin6_addr[12-15]:
printf("%u.%u.%u.%u\n", sockaddr_in6.sin6_addr[12], sockaddr_in6.sin6_addr[13], \
sockaddr_in6.sin6_addr[14], sockaddr_in6.sin6_addr[15])
It's important to remember that, unless a socket is connected (or, for a connectionless socket, has transferred data), there may not be any IP addresses, local or remote, associated with the socket.
Let's say the computer is multihomed and has both local and Internet IP addresses. Maybe even multiple local network IP addresses. If you choose to bind a socket to "any" local address (using an INADDR_ANY-type flag), or never call bind() in the first place, the socket API does not have a single local IP address associated with the socket, just a port number at the most. When you call connect() on a socket, the system chooses which local IP to use based on who you are connecting to. So if you connect to a machine over the Internet, your Internet IP is associated with the socket, and if you connect to a machine on the local network, your LAN IP address is used.
So may sure that you connect() to a remote computer or bind() to a specific local IP before you use getsockname(). I wonder if enabling IPv6 has caused your machine to see multiple potential local IPs to use. Obviously you much be connected to a machine to use getpeername().
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 */
};