Invalid connect() call unexpectedly succeeds - why? - c

I expect the following code* to fail since the server address hasn't been set with a valid value (verified in debugger - the whole struct is indeed initialized to 0, making address family AF_UNSPEC).
* incomplete illustrative snippet
static struct sockaddr_in g_server_addr;
int main(void)
{
int hdl;
hdl = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == hdl)
{
printf("Client Socket creation failed.");
return -1;
}
if(-1 == connect(hdl, (struct sockaddr *) &g_server_addr, sizeof(g_server_addr)))
{
printf("Connect() on socket failed.");
return -1;
}
return 0;
}
I need connect() to fail when called with incorrect input.
(This code is being tested on an Ubuntu machine.)

From the connect manpage:
Connectionless sockets may dissolve the association by connecting to an address with the sa_family member of sockaddr set to AF_UNSPEC (supported on Linux since kernel 2.2).
The manpage is a bit outdated, it will work on any socket, which can be disconnected at all, as for example TCP sockets.
In practice, there is no error when trying to dissolve the association on a stream socket, which is not yet connected. This is, why you don't get an error.
If you need to get an error, initialize the address family with an invalid family:
static struct sockaddr_in g_server_addr = { -1 };
This will yield the error -1 EAFNOSUPPORT (Address family not supported by protocol)
See also net/ipv4/af_inet.c of a recent linux kernel:
int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags) {
...
if (uaddr->sa_family == AF_UNSPEC) {
err = sk->sk_prot->disconnect(sk, flags);
sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
goto out;
}
...
out:
return err;

Related

Server socket: get own IP direction after accept [duplicate]

I want to get the IP address of the computer my program is launched on, to be able then to send it to a client, but I always get 0.0.0.1 instead of the real IP address (like 127.0.0.1 for instance).
I'm currently able to get the port, but not the IP address.
How can I get it?
The best solution would be to be able to get it with a sockaddr_in. Here's what I'm currently doing:
int open_connection(char* ip, int* port)
{
int sock;
struct sockaddr_in sin;
socklen_t len;
int i;
i = 0;
len = sizeof(sin);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return (-1);
bzero(&sin, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0)
perror("Error on bind");
if (getsockname(sock, (struct sockaddr *)&sin, &len) != 0)
perror("Error on getsockname");
strcpy(ip, inet_ntoa(sin.sin_addr)); // IP = 0.0.0.0
*port = sin.sin_port;
return (sock);
}
EDIT: I understand I was going on the wrong way with my way of thinking. So my question is: What's the best way to get your own IP address?
When you bind() a socket to 0.0.0.0, that is the only IP the socket has available when calling getsockname(). It means the socket is bound to all local interfaces. In order to get a specific IP from a socket, it has to be bound to a specific IP.
Using the socket API to get the machine's local IP(s) is the wrong approach anyway. A common mistake is to use gethostname() with gethostbyname() or getaddrinfo() to get the local IP list. Usually that works, but it has some hidden gotchas that can cause false information, but people tend to ignore that fact, or don't even know about it in the first place (I didn't know about it for years, but then I learned better).
Instead, you really should use platform-specific APIs for enumerating the local networking interfaces. That will provide more reliable information. Windows has GetAdaptersInfo() and GetAdaptersAddresses(). Other platforms have getifaddrs(). Those will tell you what local IPs are available. You can then bind() a socket to 0.0.0.0 in order to accept clients on any of those IPs, or bind() to a specific IP to accept clients only on that IP.
The sockets API allows you to enumerate the IP addresses assigned to your network interfaces, but it will not tell you what you "real IP" is if you are connecting to the Internet from behind a router.
The only way to know it is by asking someone outside. Thats how servers like FileZilla FTP Server do that. They instruct you to configure the URL to a "ip.php" script like this one in the server's settings so it can ask the Internet whats its public IP address, to use in Passive Mode.
You can also consider using STUN, a protocol widely used in VoIP to discover public IP.
You could call ioctl(sock, SIOCGIFADDR, adr)
see netdevice(7)
Following #Remy Lebeau's answer I wrote a function that return current machine's address. I have only tested this on macOS High Sierra.
interfaec can be anything among lo0, en0, etc.
ipVersion can be AF_INET or AF_INET6.
long int getInternalAddress(char* interface, sa_family_t ipVersion)
{
struct ifaddrs *ifaddrHead, *ifaddr;
/* int_8 */
sa_family_t family;
int n;
char *interfaceName;
if (getifaddrs(&ifaddrHead) != 0)
{
fprintf(stderr, "ifaddrs error");
}
/* iterate through address list */
for (ifaddr = ifaddrHead, n = 0; ifaddr != NULL; ifaddr = ifaddr->ifa_next, n++)
{
family = ifaddr->ifa_addr->sa_family;
interfaceName = ifaddr->ifa_name;
if (!family || family != ipVersion || strcmp(interfaceName, interface)) continue;
struct sockaddr *addr = ifaddr->ifa_addr;
struct sockaddr_in* addr_in = (struct sockaddr_in*) addr;
long int address = addr_in->sin_addr.s_addr;
freeifaddrs(ifaddrHead);
return address;
}
freeifaddrs(ifaddrHead);
return 0;
}
To use it,
int main()
{
long int address = getInternalAddress((char*) &"en0", AF_INET);
printf("%li\n", address);
return 0;
}
I'm still a beginner in C, if there is anything wrong please tell me.

Reuse socket after connect() fails with WSAETIMEDOUT?

Our software had problems connecting to a SIEMENS PLC. We created a socket and continually did connect() on it, always receiveing WSAETIMEDOUT. Telnetting to the PLC on the exact same IP and port worked. Pseudocode below:
// Does not work
SOCKET reconnect(char* ip) {
SOCKET sock = socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in addr = make_addr();
int err;
while(1) {
err = connect(sock,(struct sockaddr FAR*) &addr,sizeof(addr));
if( err==SOCKET_ERROR ) {
log() // WSAETIMEDOUT logged here
continue;
}
return sock;
}
}
After changing the code to create a new socket for each connect() call, it seems to work...
// Works
SOCKET reconnect(char* ip) {
struct sockaddr_in addr = make_addr();
int err;
while(1) {
SOCKET sock = socket(PF_INET,SOCK_STREAM,0);
err = connect(sock,(struct sockaddr FAR*) &addr,sizeof(addr));
if( err==SOCKET_ERROR ) {
log() // WSAETIMEDOUT logged here
closesocket(sock);
continue;
}
return sock;
}
}
The first snippet has been running successfully in production for ~20 years, across multiple versions of Windows. I suspect it doesn't follow the specs though... Has there been any changes/updates to Windows Server 2012 R2 (which is what the customer is running) that changes this behavior?
EDIT
According to the docs,
If the error code returned indicates the connection attempt failed
(that is, WSAECONNREFUSED, WSAENETUNREACH, WSAETIMEDOUT) the
application can call connect again for the same socket.
... which makes this even more puzzling.
Your code was always wrong. A failed connect() always hoses the socket. You were lucky it worked so long.

Bind failed: : Address family not supported by protocol family - C

I've declared a struct sockaddr_in server in my main function.
I pass it into this function to return a socket file descriptor
int openSocket(char* ip_addr, int port, struct sockaddr_in* server){
int sockfd, len;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0){
perror("Failed to open socket: ");
exit(-1);
}
len = sizeof(server);
bzero(server, len);
server->sin_family= AF_INET;
inet_pton(AF_INET, ip_addr, &server->sin_addr);
server->sin_port= htons(port);
if((bind(sockfd, (struct sockaddr*)&server, len)) < 0){
perror("Bind failed: ");
exit(-1);
}
return sockfd;
}
The struct is passed into the function using this call
sockfd = openSocket(vector->ip_addr, vector->port, &server);
However I get the following error.
Bind failed: : Address family not supported by protocol family
len = sizeof(server);
This determines the size of the pointer. You want the size of the structure it points to, so this should be:
len = sizeof(*server);
int openSocket(char* ip_addr, int port, struct sockaddr_in* server){
[...]
if((bind(sockfd, (struct sockaddr*)&server, len)) < 0){
server is already declared as a pointer, so I think the & operator
isn't needed in the bind() call.
int openSocket(char* ip_addr, int port, struct sockaddr_in* server){
This is interesting - you use sockaddr_in which has the following properties you need to set:
sun_family -- This must equal AF_INET.
sin_addr -- This is another structure with an unsigned long named s_addr which you'll probably want to set to INADDR_ANY +.
sin_port -- This requires a server port which needs converting with htons(). (Make sure this port is not in use ++! My go-to port is 3333 for testing...)
It seems you're using inet_pton() but I'm not sure it has the same affect for setting sin_port. Other that it looks fine.
The other area is there might be an issue is this line:
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
The 0 on the end specifies [1]:
"Specifying a protocol of 0 causes socket() to use an unspecified default protocol appropriate for the requested socket type."
I could imagine a possibility where the unspecified default is either not correct for your Operating System or that there is no default. I would try SOCK_STREAM to test whether this works.
+ NOTE: This would technically need htonl() but my understanding an this does nothing for this particular case.
++ NOTE: If you are writing client and server applications remember to be careful about which owns the port.
[1] http://pubs.opengroup.org/onlinepubs/009695399/functions/socket.html

inet_ntop returns 0.0.0.0 in first usage

I'm trying to write server application. I want to get client's ip. My problem is with inet_ntop function. I can't get it working properly. Here is my usage :
void client_loop(uint16_t port) {
int cfd;
int socket = bind_socket(port, INADDR_ANY);
char ip[INET_ADDRSTRLEN];
struct sockaddr client_addr;
struct sockaddr_in *addr_in;
socklen_t cli_len;
while (work) {
if ((cfd = TEMP_FAILURE_RETRY(accept(socket, &client_addr, &cli_len))) < 0) {
if (EAGAIN == errno || EWOULDBLOCK == errno)
continue;
ERR("accept");
}
addr_in=(struct sockaddr_in *)&client_addr;
inet_ntop(AF_INET, &(addr_in->sin_addr),ip, sizeof(ip));
printf("INET_NTOP: %s\n",ip);
}
}
First usage of inet_ntop always returns 0.0.0.0 Then it works properly. When im moving inet_ntop to outside function it works properly only for connections from localhost. Thanks.
EDIT:
Fixed. The problem was that cli_len wasn't initialized so accept wasn't filling client_addr.
With this line its working like a boss:
socklen_t cli_len=sizeof(struct sockaddr);
i couldn't find the issue, but i see that you have given cli_len as argument, but you have to use sizeof(ip) in place of the 4 th argument (cli_len) of inet_ntop. and also please check for the cli_len also before calling inet_ntop

getpeername() Returns Wrong Data

I am writing my first sockets program on Linux and am trying to print the IP address and port of the peer I have connected to. I use getpeername() along with inet_ntop() and ntohs() to get the data out of the sockaddr_in struct. When I look at the results, I get an IP address that does not go to any server that I know of (ping fails) and says that I am listening to a port that netstat says is not being used.
What am I doing wrong? I should be getting 130.215.28.181:39000, but instead I am getting 209.94.72.137:18825 every time I run the program. Looking at netstat shows that I am indeed listening on port 39000.
Here is a snippet from my client program:
connect(sockfd,&serv_addr,sizeof(serv_addr))
// print welcome message
char ipstr[INET6_ADDRSTRLEN];
bzero(ipstr, 50);
struct sockaddr_in *address;
socklen_t address_len = sizeof(*address);
getpeername(sockfd, (struct sockaddr *) address, &address_len);
inet_ntop(AF_INET, &address->sin_addr, ipstr, sizeof(ipstr));
printf("Connection established successfully with %s:%i!\n", ipstr, ntohs(address->sin_port));
You're not allocating any memory for your sockaddr_in structure, you's just passing a pointer to some random memory location. Instead, allocate the address structure on the stack:
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
int err = getpeername(sockfd, (struct sockaddr *) &addr, &addr_len);
if (err != 0) {
// error
}
You should also be checking the return value of every function that is documented to return an error code. In particular, both connect and getpeername return error codes that you should be checking.

Resources