C++ Linux getpeername and getsockname return only port - c++

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

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.

POSIX UDP socket not binding to correct IP

I'm in the process of writing a project for college involving writing a chat client and server using POSIX sockets and C++.
The clients are supposed to converse with each other using P2P, such as each client has his own open UDP socket through which he sends and recieves messages from/to other clients.
My problem is 2-fold:
My UDPSocket class constructor seems to be ignoring the port number completely, binding to port 65535 regardless of the parameter.
The port is binding to IP 255.255.255.255 rather than my own IP (10.0.0.3), or at least that's what i get when I call getpeername.
To the best of my knowledge passing INADDR_ANY should bind to my local address, and passing port number 0 should make the OS choose a free port, what am I doing wrong?
This is the constructor of my UDPSocket class:
UDPSocket::UDPSocket(int port){
socket_fd = socket (AF_INET, SOCK_DGRAM, 0);
// clear the s_in struct
bzero((char *) &in, sizeof(in)); /* They say you must do this */
//sets the sin address
in.sin_family = (short)AF_INET;
in.sin_addr.s_addr = htonl(INADDR_ANY); /* WILDCARD */
in.sin_port = htons((u_short)port);
fsize = sizeof(from);
//bind the socket on the specified address
if(bind(socket_fd, (struct sockaddr *)&in, sizeof(in))<0){
perror ("Error naming channel");
}
}
This is the initialization:
m_Socket = new UDPSocket(0);
And this is the method I use to retrieve the binded address: (UDPSocket inherits Socket)
std::string Socket::GetSocketAddress()
{
struct sockaddr_in addr;
int len = sizeof(addr);
getpeername(socket_fd, (struct sockaddr*)&addr, (socklen_t*)&len);
char ipAddressBuffer[50];
memset(ipAddressBuffer, 0, sizeof(ipAddressBuffer));
sprintf(ipAddressBuffer, "%s:%d", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
return ipAddressBuffer;
}
Any help would be greatly appreciated,
Avi.
You're using getpeername , which gives you the remote address of a connected socket. If you check the return value of getpeername(), it should indicate failure.
You need to use getsockname() instead of getpeername() to get the address of your local socket
You need to check that getsockname() succeeds.
Note that your socket is bound to the special 0.0.0.0 address, which means "all local interfaces", so that's what getsockname() will also return.
Answering the more general question "How to set up peer-to-peer communications with UDP":
With UDP sockets, while you can use connect, you generally don't want to, as that restricts you to a single peer per socket. Instead, you want to use a single unconnected UDP socket in each peer with the sendto and recvfrom system calls to send and receive packets with a different address for each packet.
The sendto function takes a packet and a peer address to send it to, while the recvfrom function returns a packet and the peer address it came from. With a single socket, there's no need to multiplexing with select or poll -- you just call recvfrom to get the next packet from any source. When you get a packet, you also get the peer address to send packets (back) to.
On startup, your peer will create a single socket and bind it to INADDR_ANY (allowing it to receive packets on any interface or broadcast address on the machine) and either the specific port assigned to you program or port 0 (allowing the OS to pick any unused port). In the latter case, you'll need to use getsockname to get the port and report it to the user. Once the socket is set up, the peer program can sendto any peer it knows about, or recvfrom any peer at all (including those it does not yet know about).
So the only tricky part is bootstrapping -- getting the first packet(s) flowing so that peers can recieve them and figure out their peer addresses to talk to. One method is specifying peer addresses on the command line when you start each peer. You'll start the first one with no arguments (as it has no peers -- yet). It will just recvfrom (after socket setup) to get packets from peers. Start the second with the address of the first as an argument. It sends a packet (or several) to the first peer, which will then know about the new peer as soon as it gets the first packet. Now start a third client with the addresses of the first two on the command line...

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.

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 can I get IP information from a UDP socket (Windows 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().