0

I have a server listening on multiple devices (LAN, WiFi and, possibly, some VPNs).

Listening binds a socket (AF_INET) binds INADDR_ANY to be exposed on all interfaces.

A client then connects and interchange starts.

At this point, how can I get information about which device was used for connection?

Equivalently: how can I get the IP address client used to connect?

EDIT: Following @Barmar advice I tried the following:

#include <sys/socket.h>
#include <netinet/ip.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)

void error(char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
    int sockfd, newsockfd, portno;
    unsigned clilen;
    struct sockaddr_in serv_addr, cli_addr;
    struct sockaddr_in myaddr;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR creating socket");
    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = 5666;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);
    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR on binding");
    listen(sockfd, 5);
    clilen = sizeof(cli_addr);
    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
    if (newsockfd < 0)
        error("ERROR on accept");

    clilen = sizeof(myaddr);
    if (getsockname(newsockfd, (struct sockaddr *)&myaddr, &clilen))
        error("ERROR in getsockname");

    unsigned int i = myaddr.sin_addr.s_addr;
    int p = myaddr.sin_port;
    int a = i & 0xff;
    i >>= 8;
    int b = i & 0xff;
    i >>= 8;
    int c = i & 0xff;
    i >>= 8;
    int d = i & 0xff;
    printf("my address is: %d.%d.%d.%d:%d\n", a, b,c, d, p);

    return 0;
}

... and (after a bit of debugging) it works.

Note that Port is not the value expected (5666) but consistently 8726.

ZioByte
  • 2,690
  • 1
  • 32
  • 68

1 Answers1

1

If you're using a SOCK_STREAM socket (i.e. TCP), not SOCK_DGRAM (i.e. UDP), call getsockname() on the socket returned by accept().

If you're using a datagram socket, I don't think there's a portable way to do it with a single socket. The traditional solution is to bind a separate socket for each local address, rather than using INADDR_ANY, and wait for a message on all of them using epoll() or select(). You can tell which address they connected to based on which socket the message was received on.

Linux and Windows have an extension that allows you to do this more easily, by setting the IP_PKTINFO option on the socket and using recvmsg() to receive a packet. In the msgheader.msg_control you can get the destination address of the packet. See Get destination address of a received UDP packet

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • I tried to follow your advice, but I get always the INADDR_ANY (0.0.0.0) address (see my edit to the question). – ZioByte Sep 19 '18 at 21:24
  • You should be calling it on `newsockfd`, not `sockfd`. – Barmar Sep 19 '18 at 21:25
  • Of course, my bad! Answer accepted. Can You explain why the Port No is always 8726 (instead of the expected 5666)? This happens also with `newsocket`. – ZioByte Sep 19 '18 at 21:30
  • 1
    You know how you have to call `htons()` when assigning to `sin_port`? You need to call `ntohs()` when reading it to reverse the process. – Barmar Sep 19 '18 at 21:32
  • 1
    5666 is 0x1622, 8726 is 0x2216 -- notice the relationship? – Barmar Sep 19 '18 at 21:33
  • "*Linux has an extension that allows you to ... get the destination address of the packet*" - Windows has a [`WSARecvMsg()`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms741687(v=vs.85).aspx) function which supports a similar [`IP_PKTINFO` socket option](https://learn.microsoft.com/en-us/windows/desktop/winsock/ip-pktinfo) – Remy Lebeau Sep 20 '18 at 00:54
  • @RemyLebeau That requires using winsock, not regular sockets, right? – Barmar Sep 20 '18 at 05:15
  • @Barmar They are the same thing. Winsock is the official socket library on Windows. It supports the usual BSD-style sockets that most platforms support, as well as its own WSA extensions. – Remy Lebeau Sep 20 '18 at 06:39
  • Thanks for the clarification, I've updated the answer. – Barmar Sep 20 '18 at 14:58