2

Reading the https://www.amazon.com/Unix-Network-Programming-Sockets-Networking/dp/0131411551, chapter

8.14 Determining Outgoing Interface with UDP

A connected UDP socket can also be used to determine the outgoing interface that will be used to a particular destination. This is because of a side effect of the connect function when applied to a UDP socket: The kernel chooses the local IP address (assuming the process has not already called bind to explicitly assign this). This local IP address is chosen by searching the routing table for the destination IP address, and then using the primary IP address for the resulting interface.

If I try to run the example (udpcli01.c):

#define BSIZE 256
#define SERV_PORT 9877

typedef struct sockaddr SA;

//argv[1] = ip address
int main(int argc, char **argv)
{
    int sockfd;
    socklen_t servlen, clilen;
    struct sockaddr_in cliaddr, servaddr;
    char ip[BSIZE];

    if (argc < 2)
    {
        die("usage: %s <ip>\n", argv[0]);
    }

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    servlen = sizeof(servaddr);
    memset(&servaddr, 0, servlen);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    connect(sockfd, (SA *)&servaddr, servlen);

    clilen = sizeof(clilen);
    if (getsockname(sockfd, (SA *)&cliaddr, &clilen) < 0)
    {
        perror("getsockname");
    }

    inet_ntop(AF_INET, &cliaddr, ip, BSIZE);
    printf("address %s:%hd\n", ip, cliaddr.sin_port);
}

Now If i run the server in one terminal (which address INADDR_ANY and port 9877), and then run the client above:

terminal 1:
$ ./udpserv01

terminal 2:
$ #I will try localhost first
$ ./udpcli01 127.0.0.1
address 2.0.178.211:-11342
$ #did not work, now my host ip 10.0.0.11
$ ./udpcli01 10.0.0.11
address 2.0.193.86:22209

I always get some garbage even if the client is prior to printing its address and port, connected to server, which is listening. If i was trying to print server address and port, then it would work (that is, it would print 127.0.0.1:9877 and 10.0.0.11:9877 respectively -> I have already tried). So I know the inet_ntop is working correctly and also getting the port number. So where is issue in the client? Does the kernel really assigns address and port upon connect() according to the book? If so then why my example prints random garbage?

uname -a:
Linux Shepherd 5.8.0-36-generic #40-Ubuntu SMP Tue Jan 5 21:54:35 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • The [POSIX documentation for `connect()`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html) does state: "If the socket has not already been bound to a local address, `connect()` shall bind it to an address which, unless the socket's address family is `AF_UNIX`, is an unused local address." So you seem to have found something wrong. What OS are you using? What are **all** the IP address(es) assigned to your system? And port values are `unsigned short` in network byte order. – Andrew Henle Jan 18 '21 at 14:34
  • @AndrewHenle see edits. 1) using linux, 2) I have no idea how to find out what adresses are assigned to my system. 3) yes, my mistake, I forgot to use `ntohs()` to convert ports. But the address are garbage anyway – milanHrabos Jan 18 '21 at 16:07

1 Answers1

2

You're passing the address of a struct sockaddr_in to the inet_ntop function. For an AF_INET socket it expects a pointer to a struct in_addr.

So instead of this:

inet_ntop(AF_INET, &cliaddr, ip, BSIZE);

You want this:

inet_ntop(AF_INET, &cliaddr.sin_addr, ip, BSIZE);

Also, be sure to call ntohs on the sin_port member before you print it to get the proper value, and use %d instead of %hd:

printf("address %s:%d\n", ip, ntohs(cliaddr.sin_port));

And clilen is not initialized properly:

clilen = sizeof(clilen);

It should be:

clilen = sizeof(cliaddr);
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Now, after changes you suggest, both `./udpcli01 127.0.0.1` or `./udpcli01 10.0.0.11` gives output `address 0.0.0.0:-23170`. The address is always `0.0.0.0` and the port is still some garbage (even with `ntohs()`), I assume that is no what the book is showing. So what is the true solution? – milanHrabos Jan 18 '21 at 17:55
  • @milanHrabos For the port, use `%d` instead of `%hd` as the latter expects a `signed short`. The address is correct because you didn't bind to a specific IP. – dbush Jan 18 '21 at 18:10
  • I don't bind, just as is at book. They say side effect of the *connect* function to assign (not previously bind) the adress by kernel. So I should not have *0.0.0.0* because I `connect()` (and the kernel should give me one). So your comment is in contradiction of the book. – milanHrabos Jan 18 '21 at 18:57
  • @milanHrabos Ah, it seems there was another problem. `clilen` wasn't set correctly. – dbush Jan 18 '21 at 19:15