I have a conventional, C language, Linux, TCP server application which needs to accept connections on multiple local addresses, and the program must be able to discover which specific local address is in use for each different session. The system call getsockname() is superficially appropriate, however this API returns only the wildcard address 0.0.0.0 in the specific case where that was the address bound for the listen socket. The option of explicitly listening on multiple sockets bound to explicit addresses is not useful, since the requirement is correctly operate with addresses that cannot be known in advance such as variations on loopback addresses (127.0.0.2, 127.0.0.3, ...) and also dynamically assigned 'secondary' addresses. For clarity, the execution flow is 'socket' , 'bind' , 'listen' , 'accept'; and the socket in question is the one returned by 'accept'.
-
1The behavior you describe seems to conflict with https://stackoverflow.com/questions/4046616/sockets-how-to-find-out-what-port-and-address-im-assigned (of which this question otherwise would be a dupe). Can you present an actual [mcve] that demonstrates the issue? – John Bollinger Apr 08 '19 at 14:51
-
`the program must be able to discover which specific local address is in use` I do not understand this part. Could someone explain me? Does the OP need to find the Ip address of the interface where the socket server runs....or he need to find the IP address of the client connected to this address ? – Michi Apr 08 '19 at 14:58
-
1@Michi, the OP will need to confirm, but I'm interpreting as follows. The server is listening to some port at all addresses. A client program connects to the server. The server wants to know the address of *its own* end of the connection, which it does not automatically know because it is listening to all addresses. AND that address may be a loopback address, in which case the server wants to know which specific one. – John Bollinger Apr 08 '19 at 15:07
-
@michi - every TCP session has a unique 4-tuple of local/remote x IP / port. Which can easily be discovered externally by inspecting packets on the interface. I seek the local IP member. The remote/peer information is easily available using getpeername() or simply from the address structure filled in by the accept(). – hdb3 Apr 08 '19 at 15:09
-
@JohnBollinger Well I think he needs to create a separate socket for all those interfaces. – Michi Apr 08 '19 at 15:15
-
@hdb3 Please show us a [mcve]. – Michi Apr 08 '19 at 15:16
-
1No, @Michi, separate listening sockets should not be necessary, and it is anyway not feasible for the OP if, as they claim, they must accommodate arbitrary loopback addresses (there being nearly 2^24 of those). The IP stack has a feature, which the OP is using, to bind a listening socket to *all* machine addresses, and when one accepts a connection using such a socket, one ought to be able to determine which local address it involves. There is an existing API for exactly that purpose, but the OP claims it is not working as documented. – John Bollinger Apr 08 '19 at 15:28
-
@JohnBollinger The OP seems to not be interested in posting a [mcve]. – Michi Apr 08 '19 at 15:36
-
That does seem to be the case, @Michi. Voting to close. – John Bollinger Apr 08 '19 at 15:40
-
@JohnBollinger Yes. That should be the case. I was also wondering if OP uses `serv_addr.sin_addr.s_addr = INADDR_ANY;` or not. – Michi Apr 08 '19 at 15:42
-
FWIW, I wrote a client and server to test this behavior, and it worked as expected for me on CentOS 7.6: the server listened on `INADDR_ANY`, and `getsockname()` applied to connected sockets received from `accept()` correctly reported the specific loopback addresses to which the client connected. This included loopback addresses different from 127.0.0.1. – John Bollinger Apr 08 '19 at 16:19
-
@JohnBollinger That was the reason way I was mention about `serv_addr.sin_addr.s_addr = INADDR_ANY`. – Michi Apr 08 '19 at 16:20
-
@hdb3 [Does this help](https://ideone.com/gB5e03)? – Michi Apr 08 '19 at 16:28
1 Answers
Answering my own question - and the answer is that, done correctly, the operation DOES work as I originally hoped. The solution lies in strictly observing the usage of the 'gettpeername' and 'getsockname' system calls , with particular care to the 'socklen' parameter being set before entry - not only does the service return the number of bytes written, the same parameter is also used to indicate how much space the caller has provided to receive the response. Debugging was further complicated by stacking calls to inet_ntoa() within a single printf(), inet_ntoa() overwrites its previous return values on each invocation. I post an example of a working snippet, and thank @John Bollinger for encouraging me to produce a 'minimal complete and verifiable' example. I did, and it (nearly) worked first time. Thank you John.
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define MAXPENDING 5 // Max connection requests
#define SOCKADDRSIZE (sizeof (struct sockaddr_in))
int main (int argc, char** argv) {
socklen_t addrsize;
int serversock,
peersock;
struct sockaddr_in sockaddr;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); // local server addr - wildcard
sockaddr.sin_port = htons(179); // BGP server port
serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(serversock, &sockaddr, SOCKADDRSIZE);
printf("\nlistening\n");
listen(serversock, MAXPENDING);
while (1) {
// without the following statement the return value appears to show the wildcard address
// (actually it is simply unmodified memory which may happen to be zeros)
addrsize = SOCKADDRSIZE;
peersock = accept(serversock, &sockaddr, &addrsize );
printf("\nconnected\n");
printf("accept address %s\n", inet_ntoa(sockaddr.sin_addr));
addrsize = SOCKADDRSIZE;
getsockname(peersock,&sockaddr,&addrsize);
printf("local address %s\n", inet_ntoa(sockaddr.sin_addr));
addrsize = SOCKADDRSIZE;
getpeername(peersock,&sockaddr,&addrsize);
printf("peer address %s\n", inet_ntoa(sockaddr.sin_addr));
close(peersock);
};
};

- 239
- 1
- 10