How to choose a server socket address using getaddrinfo? - sockets

I would like to create a TCP server application which lets the user choose the local address that is used in the bind call. The user may provide a textual representation of a host name or IP address, so I thought of using the getaddrinfo function to translate the textual representation into one or several sockaddr structs (performing name lookup if necessary).
Now here's my problem: The getaddrinfo function does not seem to suit my needs, because it requires the AI_PASSIVE flag to be set in the hints structure in order to obtain socket addresses that may be used in bind calls. But if I use AI_PASSIVE, I can not use the nodename parameter anymore, which defeats the whole purpose of letting the user choose the local address. If I do not provide AI_PASSIVE, getaddrinfo will only return those addresses which can be used in connect, sendto and sendmsg calls, but there might be addresses that can be used for binding but not for connect, sendto or sendmsg calls, which would be omitted. See the POSIX specification regarding the getaddrinfo/freeaddrinfo functions.
To clarify my needs, here is a sketch of the application I am trying to create:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "Usage: %s address port\n", argv[0]);
return EXIT_FAILURE;
}
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
struct addrinfo *result;
/* The next line won't work as intended! It will behave as if the
AI_PASSIVE flag was not set. */
if (getaddrinfo(argv[1], argv[2], &hints, &result) != 0) {
perror("getaddrinfo");
return EXIT_FAILURE;
}
/* Iterate result list */
int sfd;
struct addrinfo *rp = result;
do {
sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
close(sfd);
rp = rp->ai_next;
} while (rp != NULL);
freeaddrinfo(result);
if (rp == NULL) { /* No address succeeded */
fprintf(stderr, "Could not bind\n");
return EXIT_FAILURE;
}
/* ... use socket bound to sfd ... */
close(sfd);
return EXIT_SUCCESS;
}
I actually have several questions regarding the topic. First off, why is the nodename parameter forbidden when using the AI_PASSIVE flag? What is the intention? It does not make any sense to me. Is there any (preferably POSIX-conform) way to find local addresses that I can bind to and that correspond to a given host name or IP address in textual representation at all? Provided that the given nodename corresponds to a local address and AI_PASSIVE is not set, will there be any addresses returned by getaddrinfo, that can not be used for binding? Or worse, will there be any addresses that are only suitable for binding and that will not be returned in this case?
Related (and not answered satisfactorily): getaddrinfo: in what way is AI_PASSIVE ignored if the nodename is specified?

I agree the reference question was not answered satisfactorily and IMO the whole feature is poorly specified.
I believe the AI_PASSIVE flag is misnamed. It should be called something like AI_ANY_ADDRESS. It just means "give me a token I can use to bind to any node address on this system (within the address family, etc)" so that you don't have to hard-code INADDR_ANY and IN6ADDR_ANY_INIT. Hence, if you supply a node name argument, you're obviating the point of AI_PASSIVE. You're saying "Here's the address(es) I want to bind to".
In practice, this all works out fine because the structure of addresses accepted by bind and connect is fundamentally the same in all cases (except that you can't connect to the special "any" address). That's why -- when you specify the node name -- it doesn't matter whether you specify AI_PASSIVE or not. You're going to get the parameters appropriate for a bind or connect call on the specified name/address.
Obtaining that "address info" doesn't mean the bind or connect will succeed. You could successfully obtain an address that you can't bind to -- because it's not an address of the local machine -- or for other reasons as well. (And obviously, the connect could fail for many reasons as well -- there might be no machine answering at that address or no server listening on the port, etc.)
If you supply a node name that is not an IP address and name resolution provides multiple IP addresses in different families (that are all available on the local machine), you should be able to bind to all of those addresses.
So, bottom line: If you want to allow the user to specify the address, just provide that to getaddrinfo. The address(es) you get back (if any) should be usable in a bind call.

Related

On Windows, when will a 'socket()' call return WSAEAFNOSUPPORT?

Suppose our client application does as below:
WSADATA wsa_data;
int ret;
SOCKET sock;
ret = WSAStartup (MAKEWORD (2, 2), &wsa_data);
if (ret ==0) {
sock = socket (AF_INET6, SOCK_DGRAM, IPROTO_UDP);
}
When will it happen that sock == INVALID_SOCKET and WSAGetLastError() would return WSAEAFNOSUPPORT?
Microsoft's documentation for the socket() function says it can return WSAEAFNOSUPPORT when:
The specified address family is not supported
I have seen that this error mainly comes if I am explicitly passing a wrong address family for the given protocol.
Is there a possibility that the address family is not supported by the OS itself (ie, it is not configured in the system itself) and this error can come during a socket() call?
If yes, why? Under what circumstances? And how can it be remedied?
Examples of base transport providers include base protocol stacks such as TCP/IP or IPX/SPX. Transport and namespace service providers must be registered with the Ws2_32.dll at the time they are installed.
Applications calling either socket or WSASocket trigger that Ws2_32.dll loads the provider DLLs using the standard Microsoft Windows dynamic library loading mechanisms and then invokes WSPStartup and WSPSocket.
When one of the providers doesn't support the passed AddressFamily , WSPSocket returns WSAEAFNOSUPPORT.

Getaddrinfo - why send additional information?

Can anyone please suggest, since it is not very clear from MSDN why it is necessary to fill in the following fields of the addrinfo structure to transfer this data along with a call to the Getaddinfo () function:
hints.ai_family = AF_UNSPEC; //or AF_INET ... AF_INET6
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
It's just that there are two incomprehensible moments to me:
-I can set all members of the addrinfo structure to zero and getaddrinfo exits without error and returns data.
-I can't understand why they need to be specified at all, because I just request information about the ip-address by domain name, and do not provide it to the DNS server.
The same applies to the second parameter of getaddrinfo - this is the service number (like http / https) or the port number (80/443) - if you nullify and pass them to Getaddinfo, it also works.
And here, too, I cannot understand why the DNS server needs this information?
Without this information, wouldn't DNS be able to find all the information it stores by domain name alone?

Unix Domain Sockets datagram client with receive only

I have a simulator application which Unix Domain datagram sockets, which sends data to socket path for.ex /var/lib/XYZ.
sendto is returning -2 which is due to other end no peer is there(no other unix domian socket application is running)
I would like to write a datagram client/peer application using Unix Domain Sockets for receiving data from the server/simulator(which is sending data to /var/lib/XYZ).
My code is as follows:
#define BUF_SIZE 1024
#define SV_SOCK_PATH "/var/lib/XYZ"
#define SV_SOCK_PATH2 "/var/lib/ABC"
creation of Unix Domain sockets as below:
struct sockaddr_un svaddr, claddr;
....
sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (sfd == -1)
printf("socket creation failed");
memset(&claddr, 0, sizeof(struct sockaddr_un));
claddr.sun_family = AF_UNIX;
strncpy(claddr.sun_path, SV_SOCK_PATH2, sizeof(claddr.sun_path) - 1);
if (bind(sfd, (struct sockaddr *) &claddr, sizeof(struct sockaddr_un)) == -1)
printf("bind failed");
/* Construct address of server */
memset(&svaddr, 0, sizeof(struct sockaddr_un));
svaddr.sun_family = AF_UNIX;
strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
while(1)
{
int len=sizeof(struct sockaddr_un);
numBytes = recvfrom(sfd, resp, BUF_SIZE, 0, (struct sockaddr*)&svaddr,&len);
if (numBytes == -1)
printf("recvfrom error");
else{
printf("no of bytes received from server: %d",(int)numBytes);
printf("Response %d: %s\n", (int) numBytes, resp);
}
}
remove(claddr.sun_path);
//exit(EXIT_SUCCESS);
}
but the program is not receiving anything...is there anything missed out??
When it comes to datagrams, there is no real client or server. Either side attempting to send is responsible for addressing datagrams to the other. So, in your code, the setup is all wrong. You're apparently attempting to direct the "server" (but really not a server, just the other peer) to send to you via svaddr but that isn't how it works.
For a datagram AF_UNIX socket, the sender either needs to explicitly specify the receiver's address in a sendto call, or it needs to first connect its socket to the receiver's address. (In the latter case, it can then use send instead of sendto since the peer address has been specified via connect.)
You can't specify the sending peer's address in the recvfrom call. The socket address argument in the recvfrom is intended to return to you the address from which the datagram was sent. Whatever is in that variable will be overwritten on successful return from recvfrom.
One way datagram peer programs are often structured: the "server" creates a well-known path and binds to it, then a "client" creates its own endpoint and binds to it (constructing a unique socket address for itself), then the client can sendto the server's well-known socket. The server, by using recvfrom to obtain the client's address along with the datagram, can then use sendto along with the address to return a message to the client (without needing to connect its socket). This provides a sort of client-server paradigm on top of the fundamentally equal-peer orientation of the datagram socket.
Finally, I should mention that it's usually a good idea to use fully specified pathnames to ensure both peers are using the same address even if started from different directories. (Normally, with AF_UNIX, the address is a path name in the file system used to "rendezvous" between the two peers -- so without a full path "some_socket" is "./some_socket" in the current working directory. Some systems, such as linux, also support an abstract "hidden" namespace that doesn't require a full path, but you must use an initial null byte in the name to specify that.)

Bidirectional communication using a single UNIX socket

I have the situation where a service running in the background makes itself available for ASCII-based commands via a socket (SOCK_DGRAM) placed on the file system. I am able to successfully send commands to this interface but cannot receive any response generated by the background service.
As I understand it, the reason I am not receiving the service's response is because the underlying IPC is not technically between two processes, but is rather between to addresses. As such, it is necessary to bind my endpoint to a particular address location so the service knows were to send its response. However, the problem is that I do not want to pollute the directory space with too many additional socket files.
That is to say, I can make this work by simply doing something like:
struct sockaddr_un local;
int len;
s = socket(AF_UNIX, SOCK_DGRAM, 0);
local.sun_family = AF_UNIX;
strcpy(local.sun_path, "/path/to/some/dir/mySocketFile");
len = strlen(local.sun_path) + sizeof(local.sun_family);
bind(s, (struct sockaddr *)&local, len);
//Send commands to control interface of background service
And all is well, because by binding to mySocketFile the service has an address to which is will respond.
In short, is there a way to communicate to the service through its available socket interface and receive the response without binding the local endpoint such that it creates another socket-type file on the file system? i.e. some kind of a nameless socket, of sorts?
Of course, if anyone spots any misconceptions or misunderstandings in my logic please point them out.
If the client does not bind its socket to an filesystem address, it still has a notional address assigned by the system (which may exist in the filesystem in /tmp somewhere, or may not exist in the filesystem at all, depends on the OS). The server can get this address by using the recvfrom(2) call to receive the incoming packets from clients -- this call takes additional sockaddr * and socklen_t * arguments that it fills in with the client socket address. You then use sendto(2) to send the reply back to the client.

IP address in TCP sockets

I have a root node(server) connected to many other nodes(clients) through TCP sockets. I want to send some data from server to client, but the data is different for each node and depends on the ip address of that node.
Thus I should have ip address of each node connected to server. How can I have that information?
When you call accept(2) you can choose to retrieve the address of the client.
int accept(int socket, struct sockaddr *restrict address,
socklen_t *restrict address_len);
You need to store those addresses and then send(2) to each what you need to send.
So the workflow should be something like this:
Keep a list of connected clients. Initially the list is empty, of course
When you accept a connection, push its details into that list (the address and the socket returned by accept(2)).
When you need to send something to every client, simply walk the list and send it (using the stored socket)
The one tricky part is that socklen_t *restrict address_len is a value-result argument, so you need to be careful with that.
This is a more nuanced question than it first appears.
If the clients are sitting behind a NAT, you may get the same IP from more than one client. This is perfectly natural and expected behavior. If you need to distinguish between multiple clients behind the same NAT, you'll need some other form of unique client id (say, IP address and port).
As long as you have access to the list of file descriptors for the connected TCP sockets, it is easy to retrieve the addresses of the remote hosts. The key is the getpeername() system call, which allows you to find out the address of the remote end of a socket. Sample C code:
// This is ugly, but simpler than the alternative
union {
struct sockaddr sa;
struct sockaddr_in sa4;
struct sockaddr_storage sas;
} address;
socklen_t size = sizeof(address);
// Assume the file descriptor is in the var 'fd':
if (getpeername(fd, &address.sa, &size) < 0) {
// Deal with error here...
}
if (address.sa.family == AF_INET) {
// IP address now in address.sa4.sin_addr, port in address.sa4.sin_port
} else {
// Some other kind of socket...
}