Sender IP/Port for UDP Socket - c

Is it possible to obtain the sender IP and (dynamically obtained) port with C sockets? I have the following:
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(NULL, DATABASEPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
exit(1);
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("socket");
continue;
}
break;
}
Which is pretty much taken from a guide (though I kind of get it?). But I'm having trouble identifying which information I would use to find out the client data.
Any and all help is appreciated, thanks!

Generally you get the local address/port information with the getsockname(2), but here you don't have it yet - the socket is not connected and nothing has been sent. If this is a simple UDP client - consider using connected UDP sockets - you'd be able to see local IP/port right after the connect(2).

For non-connected UDP sockets, there's no way to get the local address. You can of course get the remote address by using recvfrom instead of read/recv to read packets. If you'll only be communicating with a single server, just go ahead and use connect. If you need to communicate with more than one server, you can probably just make a dummy connect (on a new socket) to one of the servers to get your local address, but it's possible (if the host uses nontrivial routing) that connecting to different remote hosts will result in different local addresses. This can even happen in a fairly trivial environment if you connect both to localhost (127.0.0.1) and remote servers.

Related

Understanding code open_clientfd(char *hostname, char *port) from book CSAPP? : issue for argument hostname and port

As reading the book < Computer Systems: A Programmer's Perspective > and in the chapter of Network Programming, I saw a this function:
int open_clientfd(char *hostname, char *port) {
int clientfd;
struct addrinfo hints, *listp, *p;
/* Get a list of potential server addresses */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; /* Open a connection */
hints.ai_flags = AI_NUMERICSERV; /* ... using a numeric port arg. */
hints.ai_flags |= AI_ADDRCONFIG; /* Recommended for connections */
Getaddrinfo(hostname, port, &hints, &listp);
/* Walk the list for one that we can successfully connect to */
for (p = listp; p; p = p->ai_next) {
/* Create the socket descriptor */
if ((clientfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue; /* Socket failed, try the next */
if (connect(clientfd, p->ai_addr, p->ai_addrlen) != -1)
break; /* Success */
Close(clientfd); /* Connect failed, try another */
}
/* Clean up */
Freeaddrinfo(listp);
if (!p) /* All connects failed */
return -1;
else /* The last connect succeeded */
return clientfd
}
In this function, the book said "The open_clientfd function establishes a connection with a server running on host hostname and listening for connection requests on port number port."
Therefore, I understand that hostname is for client and port is for server on client-server transaction.
My doubt come from the code, Getaddrinfo(hostname, port, &hints, &listp);
Since getaddrinfo's host and service arguments are the two components of a socket address(as the book said), I think this open_clientfd function only work when client and server are on the same host.
Am I right?
What's wrong with me?
Your understanding of the significance of host and port is not correct.
A server runs an specific host and a specific port. So the combination of host and port identifies a single server.
Getaddrinfo returns a list of (ip, port) combinations to try, using DNS to translate the host name to a list of IP addresses if needed. The function then tries to connect them one by one until one succeeds.
And it works no matter where the server runs.

How to connect a TCP server to another TCP server

I'm working with TCP servers. Let's say I have a server running with a specific port, but then I want to connect a client to it, I would simply go through the typical procedure of socket, bind, listen, accept for the server and then socket, connect for the client. So let's say our server port is 4000, and our client port 4001. Now, I want to create a new client that will connect to my client on port 4001, but to my limited understanding, I cannot do this as a client. Port 4001 would have to pertain to a server and not a client (i.e. it would have to be listening). The issue arises because I don't think you can use the same port for both the server and client.
I've decided to attempt this through the sample code I've provided below. I call the program on the command line as follows:
If this is the first call of the server, then I simply call the program without any arguments and it will automatically run on port 3000. i.e. ./serverprogram
If I would like to connect a client on port 3001 to our server on port 3000. Then I would call the command line with two arguments, the first being 3001 and the second being 3000. i.e. ./serverprogram 3001 3000
#define PORT 3000
int main (int argc, char * argv[]){
int sfd = socket(AF_INET, SOCK_STREAM, 0);
int my_port = (argc == 3) ? atoi(argv[1]) : PORT;
if (argc > 2){
struct sockaddr_in c_addr;
c_addr.sin_family = AF_INET;
memset(&c_addr.sin_zero, 0, 8);
c_addr.sin_port = htons(atoi(argv[2]));
struct addrinfo *result = NULL;
getaddrinfo("AcaciaLinux", NULL, NULL, &result);
struct sockaddr_in *x = (struct sockaddr_in*) result->ai_addr;
c_addr.sin_addr = x->sin_addr;
freeaddrinfo(result);
if(connect(sfd, (struct sockaddr *) &c_addr, sizeof(struct sockaddr_in)) == -1){
perror("connect");
exit(1);
}
printf("We have connected to a server.");
}
if (sfd == -1){
perror("socket");
exit(1);
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(my_port);
saddr.sin_addr.s_addr = INADDR_ANY;
memset(&(saddr.sin_zero), 0, 8);
if(bind(sfd, (struct sockaddr*) &saddr, sizeof(struct sockaddr_in)) == -1){
perror("bind");
close(sfd);
exit(1);
}
if (listen(sfd, 5) < 0){
perror("listen");
exit(1);
}
struct sockaddr_in caddr;
saddr.sin_family = AF_INET;
int cfd;
unsigned int c_len = sizeof(struct sockaddr_in);
if ((cfd = accept(sfd, (struct sockaddr*) &caddr, &c_len)) == -1){
perror("accept");
exit(1);
}
printf("Alas, we have finally connected to a client.");
return 0;
}
Upon running the second instance of the program I receive the error "bind: Invalid argument". I am assuming that this is due to the fact that the port is already in use. Is there any way to bypass this, or is there any way to connect a server to a client, and allow the client to also act as a server using the same port
You cannot open a socket which can do the both listen and connect.
A TCP connection is identified by its two endpoints. Each of those, in turn, is identified by an (IP address, port) pair. Therefore, you cannot simultaneously have two distinct connections between the same two IP addresses with the same ports on each end -- if all of those properties are the same, then they are the same connection.
From the perspective of system interfaces, you cannot create that situation because the system will not allow you to bind an address / port pair that is already in use to any socket (a stronger constraint than is strictly required). This means that one machine cannot use the same port simultaneously for both a client socket and a server socket, even for different remote endpoints.
You can, however, have any number of simultaneous TCP connections that each differ from all the others in at least one of those parameters. In particular, you can have any number of connections between the same two machines, with the same port on one side, and different ports on the other. This is extremely common, in fact, as web browsers often open multiple simultaneous connections to a web server to download multiple resources concurrently. All of those connections have the same server address, server port, and client address, but different client port.
If you want to have multiple simultaneous connections that are associated with one another in some way that goes beyond IP addresses, then you'll need to develop a protocol for it that involves multiple ports at at least one end. If the machines make reciprocal connections, with A connecting to B and then B connecting, separately, to A, then you'll need different ports on both sides. The port numbers to use might be fixed by the protocol or negotiated in some way, at your discretion, but the specifics described in the question are not an option.

recvfrom not receiving depending on ai_family used

I'm trying to get my head around socket programming and have encountered some unexpected (for me) behaviour.
When I try to send data to "localhost" and set addrinfo.ai_family to AF_INET the message I send isn't coming through from my client process to my host process (recvfrom() doesn't return). If I set it to AF_INET6 all is fine. Same for AF_UNSPEC in which case it picks the IPv6 addrinfo (first in the list). Both host and client use the same ai_family of course.
I've also tried this with code copy pasted from beej's guide to network programming which had the same result. I'm using DGRAM sockets.
I tried connecting from a different pc I got the opposite results, IPv4 worked fine, IPv6 did not. I gather this may be due to me using a '6to4 gateway'. I really have no idea what this means.
The problem is related to my own machine as the code does work over IPv4 on another machine I tested it on.
I can't say if it's a sending or receiving problem.
What could prevent me from sending or receiving data to/from localhost using AF_INET sockets?
I'm on a windows7 64bit machine compiling with MingW.
If it makes any difference I'm running the same program for host and client processes with different arguments. I ran the release and debug programs together (so it's not the same program twice) but got the same results.
Thanks in advance and apologies if this is considered a stupid question.
code:
typedef struct addrinfo addrinfo_t;
typedef struct sockaddr_storage sockaddr_storage_t;
typedef struct sockaddr_in sockaddr_in_t;
typedef struct sockaddr_in6 sockaddr_in6_t;
void connect_to_server(const char* server_name, const char* message)
{
int status;
init_networking();
addrinfo_t hints;
addrinfo_t* res;
memset(&hints, 0, sizeof(addrinfo_t));
hints.ai_family = AF_INET; //or AF_INET6
hints.ai_socktype = SOCK_DGRAM;
if ((status = getaddrinfo(server_name, "4950", &hints, &res)) != 0)
{
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == -1)
{
fprintf(stderr, "could not create a socket, errno: %u\n", errno);
exit(1);
}
int bytes_sent = sendto(s, message, strlen(message), 0, res->ai_addr, res->ai_addrlen);
close(s);
printf("Sent %i bytes to port %i\n", bytes_sent, ((sockaddr_in_t*)res->ai_addr)->sin_port);
freeaddrinfo(res);
}
void setup_server()
{
int status;
init_networking();
addrinfo_t hints;
addrinfo_t* res;
memset(&hints, 0, sizeof(addrinfo_t));
hints.ai_family = AF_INET; //or AF_INET6
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((status = getaddrinfo(NULL, "4950", &hints, &res)) != 0)
{
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s == -1)
{
fprintf(stderr, "could not create a socket, errno: %u\n", errno);
exit(1);
}
//Bind the socket to own address (mostly the port number contained in the address)
if (bind(s, res->ai_addr, res->ai_addrlen) < 0)
{
fprintf(stderr, "failed to bind, errno: %u\n", errno);
exit(1);
}
freeaddrinfo(res);
const size_t read_buffer_size = 1024;
void* read_buffer = malloc(read_buffer_size);
sockaddr_storage_t peer_address;
int peer_address_length = sizeof(sockaddr_storage_t);
sockaddr_storage_t own_sock_addr;
int own_sock_addr_len = sizeof(sockaddr_storage_t);
getsockname(s, (struct sockaddr*)&own_sock_addr, &own_sock_addr_len);
printf("Listening on port %i\n", ((sockaddr_in_t*)&own_sock_addr)->sin_port);
int bytes_received = recvfrom(s,
read_buffer,
read_buffer_size-1,
0,
(struct sockaddr*)&peer_address,
&peer_address_length );
printf("Received %i byte message:\n%s\n", bytes_received, (char*)read_buffer);
}
AF_INET is for IPv4, and AF_INET6 is for IPv6. When sending an IPv4 datagram, the receiver must be receiving data on the destination IP/port using either an IPv4 socket or an IPv6 dual stack socket (an IPv6 socket that accepts both IPv4 and IPv6 traffic). When sending an IPv6 datagram, the receiver must be receiving data using an IPv6 socket. Otherwise, the datagram will be ignored, So it sounds like the one machine is using an IPv6 socket that ignores your IPv4 datagram, and the other machine is using an IPv4 socket that ignores your IPv6 datagram.
When you are calling getaddrinfo(), you are specifying AF_UNSPEC as the address family in both client and server. AF_UNSPEC tells getaddrinfo() that you want both IPv4 and IPv6 addresses, so it returns a linked list that potentially contains multiple entries for all of the available IPv4 and IPv6 addresses. On the server side, you are creating a single listening socket for only the first entry in the list, which may be IPv4 or IPv6. On the client side, you are creating a single sending socket for only the first entry in the list, which may be IPv4 or IPv6. So the actual address families used in both operations are going to be random and may mismatch each other at times.
On the server side, you need to either:
use AF_INET or AF_INET6 directly, instead of AF_UNSPEC, and then code the client accordingly to match.
loop through the entire addrinfo list creating a separate listening socket for every entry. That way, clients can send data to any IP/Port family the server is listening on.
use AF_INET6 only when creating the listening socket(s), but then enable dual stack functionality on them (Vista+ only) so they can receive both IPv4 and IPv6 datagrams. You will then have to pay attention to the address family reported by the sockaddr that recvfrom() returns in order to know whether any given datagram is using IPv4 or IPv6.
On the client side, you need to use AF_INET or AF_INET6 directly, instead of AF_UNSPEC, depending on what the server is actually listening on. It does not make sense to use AF_UNSPEC for a UDP client socket (it does make sense for a TCP client socket), unless the UDP protocol you are implementing replies to each datagram with an ack. Without that, the client has no way to know whether the server is accepting IPv4 or IPv6 datagrams (unless the user tells the app). With acks, the client could loop through the returned addrinfo list, sending a datagram to an entry in the list, wait a few seconds for an ack, and if not received then move on to the next entry in the list, repeating as needed until an ack actually arrives or the list is exhausted.

How to get my own IP address from a struct addrinfo

I am binding a socket to my address to listen to connections. To do this, I get my address information using getaddrinfo() syscall, which grants me an ip independent way of doing what I want. The problem is that the structs which this syscall returns have the ip address field all blank. For example:
struct addrinfo hints, *servinfo, *p;
int sock;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, port, &hints, &servinfo)
for (p = servinfo; p != NULL; p = p->ai_next)
if (p->ai_family == AF_INET6)
break;
sock = socket(p->ai_family, p->sock_type, p->protocol);
bind(sock, p->ai_addr, p->ai_addrlen)
In the code above. the *p variable should have some kind of information on an IPv6 address of my machine since the bind succeeds, but the field p->ai_addr->sin6_addr (assume this would work without a casting) is blank. How can I know exactly what address I will be using?
The address is not blank - it is all zeroes, which is 0::0. This is the special wildcard address which means to bind to all local interfaces.
You should not care what address(es) your host has - this list might change at any time, including immediately after you check it.
Once you have a client connected, you can use getsockname() on the socket returned by accept() to determine which one of your local addresses that client connected to.
Why don't you bind to localhost (i.e. IPV4 127.0.0.1) or to ip6-localhost (i.e. IPV6 ::1) if you want to listen to local connections only?
Otherwise, leave all zeroes in the address like #caf suggested.

sin_port returned in getaddrinfo wrong

I am writing a server-client program and in the server I use getaddrinfo and getsockname on the server to get info about the local IP addr and locally bound port number .
Using this info, I start my client program and use the getaddrinfo and then just print out the returned values in the servinfo data structure:
getaddrinfo(argc[1], argc[2], &hints, &servinfo); >> server hostname and server port number are passed via command line.
But I notice that the sin_port in servinfo is not the port I am passing via the command line.
1) Does this getaddrinfo return the port number being used by the client as the source port number ?
2) My connect call after the getaddrinfo and socket calls in failing. I do not know why. How do I debug it ?
My client code snippet:
memset(&hints, 0 ,sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME | AI_NUMERICSERV;
getaddrinfo(argc[1], argc[2], &hints, &servinfo);
for (p = servinfo till p!=NULL)
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)
connect(sockfd, p->ai_addr, p->ai_addrlen) >>>>> Connect not going through.
I start my client program like this:
./a.out myservername 18844
Thanks !
New answer: You are invoking your program with only one argument, so argv[1] contains "18844" and argv[2] is a null pointer. This is going to try to connect to a host with numeric IP 18844 and an unspecified port number (which will end up being 0 and failing).
Old answer: (relevant but not your problem) sin_port and the whole sockaddr_in structure is in network byte order. You'll need to convert the port with ntohl to use it as a number, but you would be better off never touching sockaddr structures' internals whatsoever. Instead, use getnameinfo with NI_NUMERICHOST and NI_NUMERICSERV to get the address back into a string-based numeric form, then strtol to read the port ("service") number into an integer. This works even for non-IPv4 network address/protocol families (like IPv6) and requires no additional code for supporting new ones.

Resources