How can I get IP information from a UDP socket (Windows C++)? - c++

I tried to find the IP address that my UDP socket is bound to (assuming I don't want to use another method to find the computer's IP address). How can this be done? The code below works for the PORT number, but always returns 0.0.0.0 for the address:
struct sockaddr_in sin;
int addrlen = sizeof(sin);
if(getsockname(clientSock, (struct sockaddr *)&sin, &addrlen) == 0 &&
sin.sin_family == AF_INET &&
addrlen == sizeof(sin)){
printf("RETURNING ADDR: %s: len = %d\n", inet_ntoa(sin.sin_addr),
strlen(inet_ntoa(sin.sin_addr)));
}
The socket was bound using the following code:
sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;//inet_addr("127.0.0.1");
local.sin_port = 0; //assign given port
result = bind(clientSock, (sockaddr*)&local, sizeof(local));
Thank you for any and all help. I appreciate your time!

0.0.0.0 is INADDR_ANY, meaning the socket is bound to all local addresses on the host, not just one address. You are asking for one address, but you are not bound to one address, so getsockname() cannot report a specific address.
If you want getsockname() to report a specific address, you have to bind() to that specific address. So use GetAdaptersAddresses to enumerate all interfaces on the local host and bind() a separate socket to each address, instead of binding INADDR_ANY on a single socket.
Otherwise, you can bind() a single socket to INADDR_ANY, and then use WSARecvMsg() (instead of recv(), recvfrom(), or WSARecvFrom()) to read the incoming packets. WSARecvMsg() can report details about each packet's arrival interface and destination address, if you enable the appropriate options with setsockopt().

Related

winsock2 client return self port number

I'm using lib winsock2 in Visual Studio community, using simple client example.
After executing connect() function, would like to know how can I get/return self/source port number of open connection.
In winsock2, when a connection is established you can bind socket port to some specific port you want to use. For example, let´s say you are creating an UDP or TCP socket and you want that a specific local port is used. In that case you can do that by calling bind function ( https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind )
if (port != 0) {
int rv = -1;
struct sockaddr_in recv_addr;
ZeroMemory(&recv_addr, sizeof(struct sockaddr_in));
recv_addr.sin_family = AF_INET;
recv_addr.sin_port = htons(port);
recv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
rv = bind(udp_channel->socket, (SOCKADDR *)& recv_addr, sizeof(recv_addr));
}
This is the same bind function you will use to set listening port when your socket will act as a server in UDP or TCP, it's the same.
In case you don't do socket binding, socket port will be assigned on connection. There seems to be the function getsockname that user207421 mentioned.
getsockname() or getpeername()
returning Structured data with short sin_family and char sa_data[] with different amount of data depending on the protocols used
after connection to server use fuction getsockname() or getpeername() to get the structured data we need to extract the data to get the port
you use function ntohs() for extracting the port data with macro function SS_PORT to convert struct to sockaddr_in
example:
sockaddr struc_;
int struc_len = sizeof(struc_);
/* connect function*/
getsockname(ConnectSocket, (LPSOCKADDR)&struc_, &struc_len);
int port_int_ = ntohs(SS_PORT(&struc_));
or you can define a ready-made structure / create your own, with a pointer to port number data.

c++ - What does ptr->ai_family do vs AF_INET

I am going through msdn's "Getting Started With Winsock" and they open a socket with the parameters
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
iResult = getaddrinfo(
argv[1],
DEFAULT_PORT,
&hints,
&result
);
ptr=result;
ConnectSocket = socket(
ptr->ai_family, // Address Family (address families like ipv6 ipv4)
ptr->ai_socktype, // Type (Like tcp, udp ect)
ptr->ai_protocol // Protocol to use (0 = service provider chooses)
);
But binarytides "Winsock tutorial" does it like this (They are using C but I have seen people do this in c++)
s = socket(
AF_INET ,
SOCK_STREAM ,
0
)
What does ptr-> do?
and why use it over just setting it like AF_INET?
Also If you have free time and know sockets well I would appreciate some help.
socket(ptr->ai_family,ptr->ai_socktype, ptr->ai_protocol);
passes in variables to create the socket, instead of hard coding the values. The advantage you get is that the code works for both IPv4 and IPv6.
ptr->ai_family is just an integer, a member of a struct addrinfo. (And if you are wondering about the particular syntax of ptr->, you can go through this question ), it will have a value of either AF_INET or AF_INET6 (Or in theory any other supported protocol)
The call to getaddrinfo() will look up the host name, and resolve it to either an IPv4 or IPv6, and you pass in the result to socket() to create a socket of the proper type. If the hostname resolves to an IPv4 host, you create a socket that can deal with IPv4, If it resolves to IPv6, you create an IPv6 socket.
If you instead hard coded the values, e.g. as AF_INET, you would only support IPv4, whilst ptr->ai_family could be either AF_INET or AF_INET6.

Receiving broadcast packet addressed to 255.255.255.255 in C++

I have a device that is discovered by sending a broadcast packet to 255.255.255.255 on port 4930 and the device responds by sending a packet back to 255.255.255.255 on port 4930.
I have a snippet of C++ code which can send a packet to 255.255.255.255 on port 4930 (both source and destination port), but it can't receive a packet back from the broadcast address 255.255.255.255.
I can see the device is working fine, wireshark can see the packets coming back and forth and the propriety software supplied with the device can discover the device just fine, the problem is with the C++ program so please keep on topic with your responses.
Now, as I have said I can send a packet just find, but firstly I can't bind to the IP address 255.255.255.255 to receive the packets. I can change the multicast address to 239.255.255.250 and the socket will bind but I need the address 255.255.255.255.
My snippet of code is below, I am using VC++2010
bool CPTUProgramDlg::FindPTU(u_short port, const char * Destaddress){
{
//Data to send
char packet_data[10] = {0x44,0x43,0x55,0x44,0x5f,0x50,0x49,0x4e,0x47,0x00};
int packet_size=10;
SOCKET sock;
struct sockaddr_in addr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
// set SO_BROADCAST on a socket to true (1): (so we can transmit to 255 addr)
//In order to use broadcast the options of socket must change
char broadcastON = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcastON, sizeof broadcastON);
if (sock < 0)
return false;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(Destaddress); // Specify dest IP
sendto(sock, packet_data, packet_size, 0, (struct sockaddr*)&addr, sizeof(addr));
if (bind(sock,(struct sockaddr*) &addr,sizeof(addr)) != -1){
char Buff[512];
recv(sock,Buff,512,0);
}
closesocket(sock);
}
return 1;
}
Wireshark screenshot to prove packets are being send:
From the wireshark output its seen that the special device is using broadcast to communicate and will use the same port number as source and destination.
Normal socket communication will require using matching port numbers but broadcast messages cannot be exchanged over the same socket, especially when the port numbers do not match as seen with wireshark.
Binding on 255.255.255.255 (INADDR_BROADCAST) should generally work but may be limited by your OS privileges and permissions.
You may try to solve the problem by using two sockets - one for receiving and one for sending. Of course the listening socket have to be setup first and bound to 0.0.0.0 (INADDR_ANY) and port 4930. In this case there is no easy way to filter by destination address (as I wrongly written in my comment) because most standard socket APIs do not provide a way to get the destination addess from the socket. On Linux there is an exception - IP_PKTINFO at SOL_IP...
By using recvfrom you will get the source unicast address of the responding device(s). You have to note that if you have more that one such device on your network you will get more than one response.

Where does winsock store ip address of a socket?

Suppose I have a simple winsock server that has a listening socket, and then when a connection is accepted, it stores the socket in an array of sockets (to allow multiple connections).
How can I get the IP address of a specific connection? Is it stored in the socket handle?
As long, as the socket stays connected, you can get both own socket address and peer one.
getsockname will give you local name (i.e. from your side of a pipe)
getpeername will give you peer name (i.e. distant side of a pipe)
This information is available only when the socket is opened/connected, so it is good to to store it somewhere if it can be used after peer disconnects.
Yes it is stored in the socketaddr_in struct, you can extract it using:
SOCKADDR_IN client_info = {0};
int addrsize = sizeof(client_info);
// get it during the accept call
SOCKET client_sock = accept(serv, (struct sockaddr*)&client_info, &addrsize);
// or get it from the socket itself at any time
getpeername(client_sock, &client_info, sizeof(client_info));
char *ip = inet_ntoa(client_info.sin_addr);
printf("%s", ip);

C++ Linux getpeername and getsockname return only port

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().