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

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.

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.

client socket is bindable but not connectable, because already in use

I write a client, where i have to bind the client socket. This works fine. After that i try to connect the Socket and i get error 10048. (Address already in use.) I don't understand how this is possible.
I have to implement a client speaking with multiple server. Every server only accepts messages from a specific port. (every Server expects a different port). so i have to bind my client socket. The code above is to create one of these sockets.
My code works some times. But very often the connect gives me the error 10048, while the binding before was fine. I know that bind can give also the error 10048 if the socket is already in use. But it doesn't. It returns 0. So i guess the port is free. Immediate after binding i call connect and get error 10048. I do not understand why? At the moment of the binding the port was obviously free.
bool TEthernetSocket::Open()
{
WSADATA wsaData;
if (WSAStartup((MAKEWORD(2, 0)), &wsaData) == SOCKET_ERROR)
{
return IsConnected();
}
Socket = socket(AF_INET, SOCK_STREAM, 0); // TCP
if (Socket == INVALID_SOCKET)
{
return false;
}
//bind Socket
struct sockaddr_in sa_loc;
memset(&sa_loc, 0, sizeof(struct sockaddr_in));
sa_loc.sin_family = AF_INET;
sa_loc.sin_port = htons(ClientPort);
sa_loc.sin_addr.s_addr = inet_addr(IPClient.substr(0, 15).c_str());
CALL_TRACE_CB("ethernetSocket connected");
if (!(bind(Socket, (struct sockaddr*)&sa_loc, sizeof(struct
sockaddr))))
{
CALL_TRACE_CB("Bind works");
}
else
{
AnsiString msg = AnsiString().sprintf("EN: error socket Bind:
%d", WSAGetLastError());
CALL_ERROR_CB(ERROR_NO_PORT_HANDLE, msg.c_str());
Close();
}
// TCP
SOCKADDR_IN sAdd;
sAdd.sin_family = AF_INET;
sAdd.sin_port = htons(Port);
sAdd.sin_addr.s_addr = inet_addr(IP.substr(0, 15).c_str());
if (connect(Socket, (SOCKADDR*)&sAdd, sizeof(SOCKADDR_IN)) ==
SOCKET_ERROR)
{
AnsiString msg = AnsiString().sprintf("EN: error connect
errorcode: %d", WSAGetLastError());
}
}
I expect that bind() returns 10048 before connect returns this error, but actual only connect() returns this error
I have to implement a client speaking with multiple server. The server only accepts messages from a specific port, so i have to bind my client socket.
This is an unsolvable problem. When you make an outbound TCP connection, the combination of local IP address and port is reserved exclusively for that particular outbound TCP connection. If you need to make multiple outbound TCP connections from the same port, each will have to be bound to its own local IP address. That is going to be extremely inconvenient.
There are other problems too. Say you finish one connection and then try to start a new one. The new one will have the same local IP address, local port (since the server only tolerates one), remote IP address, and remote port. How will packets from the new connection be distinguished from old, stale packets from the old one?
This is why you get the error when you try to connect. It's not until all four parameters of the connection (local and remote address and local and remote port) are known that the collision with the prior connection is detectable. That's not known until you call connect.
You need to fix the server to ignore the source port. If that absolutely cannot be done, you'll have to adopt a wait and retry mechanism to handle collisions with past connections.

Get socket domain from descriptor

I have a socket descriptor descriptor, which has been initialized by either of the following:
- int descriptor = socket(PF_INET, SOCK_STREAM, 0);
- int descriptor = socket(PF_INET6, SOCK_STREAM, 0);
But I don't know which of the two. I would like to determine whether the socket corresponding to descriptor is an IPv4 or an IPv6 socket. I know that, for example, one can use getsockopt to determine the socket type (e.g., SOCK_STREAM vs SOCK_DGRAM) but I cannot seem to find a way to determine the socket domain (e.g., PF_INET vs PF_INET6).

connect() returns "invalid argument" with ipv6 address

I have this simple client-server application pair. The code is pretty simple, I'm using only new, advised methods like getaddinfo etc and everything works just fine for ipv4. Even for ipv6 loopback (::1) it works. Problems start when it comes to some other ipv6 addresses... I have two machines in a network, everything works fine when I pass their ipv4 addresses, but when I give my client ipv6 address, I get an error on connect function: invalid argument.
Hey, don't I already know this? I do! When I try to ping6 this ipv6 address, I get the same error:
connect: Invalid argument
But there is a way to overcome this block - one should choose an interface with a -I switch and it all runs smoothly since then. But how can I achieve the same in my client app? What should I do? My client code looks like this:
struct addrinfo hints;
struct addrinfo *server;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
int status;
if((status = getaddrinfo(argv[1], argv[2], &hints, &server) != 0))
{
perror("getaddrinfo error");
return 1;
}
int sock_fd;
struct addrinfo *ptr;
for(ptr=server;ptr!=NULL;ptr=ptr->ai_next)
{
if( (sock_fd = socket(ptr->ai_family,ptr->ai_socktype,ptr->ai_protocol)) == -1)
{
perror("socket error");
continue;
}
if( connect(sock_fd, ptr->ai_addr,ptr->ai_addrlen) == -1 )
{
perror("connect error");
continue;
}
break;
}
You need to specify the interface for IPv6 ping (i.e. -I eth0):
ping6 -I eth0 fe80::208:54ff:fe34:22ae
Using link-local addresses for an IPv6 ping, requires to define what device it must send/receive the packet - each device has a link-local address.
Trying without this, will result in error message like:
--> # ping6 fe80::208:54ff:fe34:22ae
connect: Invalid argument
In this case you have to specify the interface additionally like shown here:
--> # ping6 -I eth0 fe80::208:54ff:fe34:22ae
PING fe80::208:54ff:fe34:22ae(fe80::208:54ff:fe34:22ae) from fe80::208:54ff:fe34:22ae eth0: 56 data bytes
64 bytes from fe80::208:54ff:fe34:22ae: icmp_seq=0 ttl=64 time=0.027 ms
64 bytes from fe80::208:54ff:fe34:22ae: icmp_seq=1 ttl=64 time=0.030 ms
64 bytes from fe80::208:54ff:fe34:22ae: icmp_seq=2 ttl=64 time=0.036 ms
One similar approach you must to follow in your client APP..
Addresses starting with ff... are multicast addresses. Connecting a stream to a multicast address does not work.
Addresses starting with fe80... are link-local addresses, which have an interface identifier associated with them. Try looking at the sockaddr returned from getaddrinfo, is the scope field filled out?
My recommendation is that you turn on the IP6 protocol in the interface/network connection, in addition throw out the ip4 protocol if you still have the error.
On my Linux Box this happened too when i had an ip4-interface active and my application tried to use the ip4-interface with ip6 settings. The same should also be valid for windows.
If something is not clear ask.

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