what is significance of bind to zero address - c

I was seeing EADDRNOTAVAIL errors in connect() calls. I dig deeper found that naming of sockets were being done of over zero ip addresses . See following where both calls were sucessful:-
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&y, sizeof(y)); /* y is int with value 1 */
bind(s, (struct sockaddr *)lockaddr, sizeof(rtinetaddr_tp));
where
lockaddr={.sin_family=2, .sin_port=0, .sin_addr={.s_addr=0}, .sin_zero=""}
This, I found in RH site and also I have the same kernel.
My question is what if I remove doing any bind() at client side of the application? Will that be a quick cure OR will lead to any disaster?
Other way I have running sample programs without bind at client. But the app I am talking about that establishes hundreds of connections. So what may happen in worst case?

Binding to a zero address is the same as binding to INADDR_ANY (which is defined as zero). This means you can make a connection on any local IP address (server side) or use the egress interface IP address (client side). This is quite normal.

If you are not interessed in using any particular address:port on the client side, the calls to bind() aren't necessary.
The Kernel will chose the suitable interface and a random port to establish the outgoing connection initiated by the client's call to connect().

Related

Connect to port using TCP with C

I'm 99% new to sockets and any sort of network programming, so please bear with me.
I am aiming to connect to a port (2111 in this case) on my local machine (192.168.0.1). From there, I'm planning on sending and receiving basic information, but that's for another day.
I've currently tried this:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv)
{
int sd;
int port;
int start;
int end;
int rval;
struct hostent *hostaddr;
struct sockaddr_in servaddr;
start = 2111;
end = 2112;
for(port = start; port <= end; port++)
{
sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sd == -1)
{
perror("Socket()\n");
return (errno);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
hostaddr = gethostbyname("192.168.0.1");
memcpy(&servaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);
rval = connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(rval == -1)
{
printf("Port %d is closed\n", port);
close(sd);
}
else printf("Port %d is open\n", port);
close(sd);
}
return 0;
}
However, my connect() call hangs for about 90 seconds, then returns -1.
The device is directly connected to my Mac Mini's ethernet port and the manufacturer has confirmed that the port is 2111 or 2112.
What am I doing wrong? Also, can it be in the ELI5 (explain like I'm 5) format? I'm much better off with examples.
When you call connect() to connect to a host, your computer sends a SYN packet to begin the three-way handshake of the TCP connection. From here, there are 3 possible scenarios:
If the peer is listening on that port, it responds with a SYN+ACK packet, your computer responds with a final ACK, and the connection is established—connect() returns successfully.
If the peer is not listening on that port, it responds with an ICMP packet with a type and code indicating that the port is closed, which causes your connect() call to fail almost immediately with the error ECONNREFUSED (connection refused). Under normal circumstances, this takes 1 network round-trip time (RTT) to happen, which is typically tens or hundreds if milliseconds.
If your computer never receives either an appropriate SYN+ACK TCP packet or connection refused ICMP packet, it assumes that its original SYN packet got dropped by the network somewhere and will try to resend the SYN packet several times until it gets one of those packets back or it hits an OS-dependent timeout, at which point the connect() call fails with ETIMEDOUT. This is typically 1–2 minutes, depending on the OS and its TCP settings.
You're clearly hitting case #3. This can be caused by a few different issues:
Your original SYN packets were getting lost in the network, possibly due to a faulty link, overloaded router, or firewall
The peer's SYN+ACK or ICMP response packets were getting lost in the network, possibly due to a faulty link, overloaded router, or firewall
The destination address may be unroutable/unreachable
The peer may be failing to properly respond at all with a SYN+ACK or ICMP packet
If you're directly connecting to the device over ethernet, than that rules out #1 and #2. #4 is possible, but I think #3 is the most likely explanation.
A brief aside on packet routing
Your computer has multiple network interfaces—ethernet (sometimes multiple ethernet interfaces), Wi-Fi, the loopback device, VPN tunnels, etc. Whenever you create a socket, it has to be bound to one or more particular network interfaces in order for the OS to know which NIC to actually send the packet through. For listen sockets for servers, you typically bind to all network interfaces (to listen for connections on all of them), but you can also bind to a particular network interface to only listen on that one.
For client sockets, when you connect them to other peers, you don't normally bind them to a particular interface. By default, your computer uses its internal routing tables along with the destination IP address to determine which network interface to use. For example, if you have a gateway machine with two NICs, one of which is connected to the public internet with IP 54.x.y.z and hte other of which is connected to an internal, private network with IP 192.168.1.1, then that machine will in all likelihood have routing tables that say "for packets destined to 192.168.0.0/16, use NIC 2, for all other packets, use NIC 1". If you want to bypass the routing tables, you can bind the socket to the network interface you want by calling bind() on the socket before the call to connect().
Putting it all together
So, what does that all mean for you?
First, make sure that 192.168.0.1 is in fact the correct destination address you should be connecting to. How is that address determined? Is your computer acting as a DHCP server to assign that address to the other host? Is that host using a static IP configuration?
Next, make sure that your routing tables are correct. If the other machine is assigning itself a static IP, chances are that your Mac isn't aware of how to route to that destination and is probably trying to route through the wrong interface. You can manually adjust the routes on Mac OS X with the route(8) utility, but these get reset every reboot; this blog post shows an example of using a startup item to automate adding the new route on startup. You'll want to use the IP address associated with the ethernet interface connected to the target host.
Alternatively, instead of using routing tables, you could call bind() on your socket before connect() to bind to the local address of the interface you want to use, but this won't work for other programs unless they also provide that functionality. For example, the curl(1) utility lets you pass the --interface <name> command line flag to direct it to bind to a particular interface.
Basically, connect() is failing (check errno for why).
You might consider a implementing some kind of time-out for the connect. To do this, set the socket to non-blocking mode. Then call connect(), and then use select() to wait for a response with a timeout.
SPOILER Example of ConnectWithTimeout() for Linux

Is there a way to to set a socket option to indicate which interface the packets go to

Currently, this is my predicament.
I have 2 fd's : x and y. When a write(x) happens, it must go to x-tunnel and a write(y) should go to y-tunnel. I cannot create a routing rule for each connection (Reasons not mentioned here)
Is there a sock opt which I can set when I accept a connection or is there any other way to do it?
Thanks
you can control in sock binding on specific interface for x, y tunnels
check for SO_DONTROUTE option in setsockopt
Your description is sketchy, I think setsockopt() by optname of SO_REUSEADDR and SO_REUSEPORT may help you, but I'm not sure.It can't 'indicate' which interface the packets go to,but you can indicate them by your implement.
Here some reference on Unix Network Programming, if you want to know more detail,you can read them and examples in chapter7.5.
The SO_REUSEADDR socket option serves four different purposes:
SO_REUSEADDR allows a listening server to start and bind its well-known port,
even if previously established connections exist that use this port as their local
port.
SO_REUSEADDR allows a new server to be started on the same port as an
existing server that is bound to the wildcard address, as long as each instance
binds a different local IP address.
SO_REUSEADDR allows a single process to bind the same port to multiple
sockets, as long as each bind specifies a different local IP address.
SO_REUSEADDR allows completely duplicate bindings: a bind of an IP address
and port, when that same IP address and port are already bound to another
socket, if the transport protocol supports it. Normally this feature is supported
only for UDP sockets.

Getting server ip 0.0.0.0:0 from getaddrinfo()

I'm following Beej's guide on NP.
I did a few modifications and am trying to obtain the IP of my server program through getaddrinfo().
(original can be found here http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#simpleserver)
Below is the parts I've changed/added.
if ((rv = getaddrinfo(NULL, "0", &hints, &servinfo)) != 0) { //0 for random port?
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
//... some code emitted ...
//freeaddrinfo(servinfo); //I still need it!
printf("ip: %s\nport: %d\n",
inet_ntop(AF_INET, &((struct sockaddr_in *)p->ai_addr)->sin_addr, ip4, INET_ADDRSTRLEN),
ntohs(((struct sockaddr_in *)p->ai_addr)->sin_port)
);
The problem is that I get results
ip: 0.0.0.0
port: 0
Q1:I've read from a couple of websites saying that setting "0" for the port tells the OS that you want the next available port, not actually 0. Is this true?
Q2:I've also read that gethostbyname(gethostname(...)) can give you the machine's ip, but Beej said that these are superseded by getaddrinfo(). So, am I supposed to use getaddrinfo? Or gethostbyname?
Q3:Is there anything else I'm doing wrong?
It's returning exactly what you would expect it to.
From man getaddrinfo:
If the AI_PASSIVE flag is specified in hints.ai_flags, and node is NULL, then the returned socket addresses will be suitable for bind(2)ing a socket that will accept(2) connections. The returned socket address will contain the "wildcard address" (INADDR_ANY for IPv4 addresses, IN6ADDR_ANY_INIT for IPv6 address). The wild‐card address is used by applications (typically servers) that intend to accept connections on any of the hosts's network addresses. If node is not NULL, then the AI_PASSIVE flag is ignored.
The code you link to sets hints.ai_flags to AI_PASSIVE and you're passing NULL for node. The wildcard address is 0.0.0.0. Working as specified. Binding to that address means you're binding to every IP address on your machine.
As for the port ... you're specifying "0" which ... is exactly what you're getting back. You need to set it an actual port you wish to listen on as the example code you link to does.
Q1:I've read from a couple of websites saying that setting "0" for the port tells the OS that you want the next available port, not actually 0. Is this true?
Yes, but only after you have used bind() to attach the address to a real socket. At that point, use getsockname() to get the bound address from the socket; the port will be part of that.
Q2:I've also read that gethostbyname(gethostname(...)) can give you the machine's ip, but Beej said that these are superseded by getaddrinfo(). So, am I supposed to use getaddrinfo? Or gethostbyname?
Use getaddrinfo(); it does all that gethostbyname() did and more, and the interface sucks a lot less. (For example, it's typically thread-safe.)
Q3:Is there anything else I'm doing wrong?
There's no good defined concept of the server's IP address. Servers can have many due to things like multiple network cards (much more common for servers than desktop systems), and the one that the outside world knows it by might not be one of them (thanks to NAT firewalls). Occasionally, you can know exactly where the messages are coming from before the client connects — the most common option is to know that the client will be on localhost — which is part of the information you set during bind() but that's rare. You can find the address of the client once the connection has happened by using getpeername() but NAT might still make that functionally useless. Such addresses are typically set in the app's config file during deployment.
If you only need the info for logging purposes, go right ahead. But beware of using it for anything else, as it doesn't actually tell you that much.

Streaming on UDP

I have the following issue here: I want to write a server streaming data on a UDP socket on a specific port, and clients should be able to connect to it and receive the data that is being sent out without too much hassle: they just connect, and from the moment they start they should get data using recvfrom from the server.
I have some problems with setting up the network related parts. So, here is a rough piece of code that I try to make work:
int udpSock = socket(AF_INET, SOCK_DGRAM, 0);
if(udpSock == -1)
{
perror("Could not create audio output socket");
exit(1);
}
struct sockaddr_in *sin = (struct sockaddr_in*)&gOutgoingAddr;
sin->sin_port = htons(40200);
if(bind(udpSock, (const sockaddr*)sin, sizeof(struct sockaddr_in)) == -1)
{
perror("Cannot bind audio socket");
exit(1);
}
int buffer_size = 0;
char* data = get_next_buffer(&buffer_size);
while(buffer_size > 0)
{
if(sendto(udpSock, (const void*)(data), buffer_size, 0, NULL, 0) == -1)
{
perror("sendto failure");
}
data = get_next_buffer(&buffer_size);
}
Do not worry about the gOutgoingAddr variable, it is obtained correctly using getifaddrs, it is valid. I am troubled regarding the parametrization of the sendto method, becasue right now the output of the application is:
sendto failure: Destination address required
That's true, because I don't have a destination address, since all the examples I have found till now show when the server gets a client connection, and there is the address. But since I don't have a client yet connected, I'd still want to stream out.
I appreciate all help, I have no idea what Ishould put for the parameters of sendto:
The gOutgoingAddress which is the address where I create the socket? I have tried this, but if I use the tcpdump linux command on the specified port, I get nothing.
Should I create a multicast socket? This somehow makes no sense...
Something else?
Thanks,
frc
You cannot stream out to "nowhere". Streaming data via UDP is not multicast. That means if you have 100 clients connected, you must send exactly the same data 100-times, once to each of the clients that shall receive it. Multicast was not really part of the initial IPv4 design. It has been added later on and is not widely supported. This is contrary to IPv6, where multicast has been part of the initial design. The only thing you could do it broadcast the traffic within your local network. This will only work if all clients are in your local network segment. To broadcast your server would simply send the data to 255.255.255.255 and to a fixed UDP port. All clients then have to listen on that specific port and will receive the data. Please note, that on most systems you need special permissions for broadcasting (e.g. it is not common that only programs running with root privileges are allowed to broadcast traffic, as broadcasts pollute your network, since all broadcast packets are sent to all clients in the network, whether they care for them or not). Without broadcasts, you have only unicast and unicast means one sender, one receiver. For one sender multiple receiver, you must send out the same data multiple times to multiple addresses.
What is audioUdpSock by the way?
Shoudn't you be using udpSock instead?
Do a recvfrom in your server, and have the client send a message (with whatever content you want, this is just a way to establish the connection, a greeting). Then the server will have the client address from the recvfrom, and can send packet to it.
As UDP socket are connection-less (there is no need for accept and connect when using UDP socket), you need to have another way to inform the server of the existence of the client (and the client need to have an out-of-bound way to know the server address, generally, the user give it, or it is hard-coded).
If you can have multiple clients, then you'll have to use select, poll, ... on the socket to know when it is safe to call recvfrom without blocking (or you could configure your socket to be non-blocking).
Edit: I highly recommend Beej's Guide to Network Programming to everyone, and for your question, you can directly go to the sample usage of Datagram Socket.

Unix Domain Socket: Using datagram communication between one server process and several client processes

I would like to establish an IPC connection between several processes on Linux. I have never used UNIX sockets before, and thus I don't know if this is the correct approach to this problem.
One process receives data (unformated, binary) and shall distribute this data via a local AF_UNIX socket using the datagram protocol (i.e. similar to UDP with AF_INET). The data sent from this process to a local Unix socket shall be received by multiple clients listening on the same socket. The number of receivers may vary.
To achieve this the following code is used to create a socket and send data to it (the server process):
struct sockaddr_un ipcFile;
memset(&ipcFile, 0, sizeof(ipcFile));
ipcFile.sun_family = AF_UNIX;
strcpy(ipcFile.sun_path, filename.c_str());
int socket = socket(AF_UNIX, SOCK_DGRAM, 0);
bind(socket, (struct sockaddr *) &ipcFile, sizeof(ipcFile));
...
// buf contains the data, buflen contains the number of bytes
int bytes = write(socket, buf, buflen);
...
close(socket);
unlink(ipcFile.sun_path);
This write returns -1 with errno reporting ENOTCONN ("Transport endpoint is not connected"). I guess this is because no receiving process is currently listening to this local socket, correct?
Then, I tried to create a client who connects to this socket.
struct sockaddr_un ipcFile;
memset(&ipcFile, 0, sizeof(ipcFile));
ipcFile.sun_family = AF_UNIX;
strcpy(ipcFile.sun_path, filename.c_str());
int socket = socket(AF_UNIX, SOCK_DGRAM, 0);
bind(socket, (struct sockaddr *) &ipcFile, sizeof(ipcFile));
...
char buf[1024];
int bytes = read(socket, buf, sizeof(buf));
...
close(socket);
Here, the bind fails ("Address already in use"). So, do I need to set some socket options, or is this generally the wrong approach?
Thanks in advance for any comments / solutions!
There's a trick to using Unix Domain Socket with datagram configuration. Unlike stream sockets (tcp or unix domain socket), datagram sockets need endpoints defined for both the server AND the client. When one establishes a connection in stream sockets, an endpoint for the client is implicitly created by the operating system. Whether this corresponds to an ephemeral TCP/UDP port, or a temporary inode for the unix domain, the endpoint for the client is created for you. Thats why you don't normally need to issue a call to bind() for stream sockets in the client.
The reason you're seeing "Address already in use" is because you're telling the client to bind to the same address as the server. bind() is about asserting external identity. Two sockets can't normally have the same name.
With datagram sockets, specifically unix domain datagram sockets, the client has to bind() to its own endpoint, then connect() to the server's endpoint. Here is your client code, slightly modified, with some other goodies thrown in:
char * server_filename = "/tmp/socket-server";
char * client_filename = "/tmp/socket-client";
struct sockaddr_un server_addr;
struct sockaddr_un client_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strncpy(server_addr.sun_path, server_filename, 104); // XXX: should be limited to about 104 characters, system dependent
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sun_family = AF_UNIX;
strncpy(client_addr.sun_path, client_filename, 104);
// get socket
int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
// bind client to client_filename
bind(sockfd, (struct sockaddr *) &client_addr, sizeof(client_addr));
// connect client to server_filename
connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
...
char buf[1024];
int bytes = read(sockfd, buf, sizeof(buf));
...
close(sockfd);
At this point your socket should be fully setup. I think theoretically you can use read()/write(), but usually I'd use send()/recv() for datagram sockets.
Normally you'll want to check error after each of these calls and issue a perror() afterwards. It will greatly aid you when things go wrong. In general, use a pattern like this:
if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("socket failed");
}
This goes for pretty much any C system calls.
The best reference for this is Steven's "Unix Network Programming". In the 3rd edition, section 15.4, pages 415-419 show some examples and lists many of the caveats.
By the way, in reference to
I guess this is because no receiving process is currently listening to this local socket, correct?
I think you're right about the ENOTCONN error from write() in the server. A UDP socket would normally not complain because it has no facility to know if the client process is listening. However, unix domain datagram sockets are different. In fact, the write() will actually block if the client's receive buffer is full rather than drop the packet. This makes unix domain datagram sockets much superior to UDP for IPC because UDP will most certainly drop packets when under load, even on localhost. On the other hand, it means you have to be careful with fast writers and slow readers.
The proximate cause of your error is that write() doesn't know where you want to send the data to. bind() sets the name of your side of the socket - ie. where the data is coming from. To set the destination side of the socket, you can either use connect(); or you can use sendto() instead of write().
The other error ("Address already in use") is because only one process can bind() to an address.
You will need to change your approach to take this into account. Your server will need to listen on a well-known address, set with bind(). Your clients will need to send a message to the server at this address to register their interest in receiving datagrams. The server will recieve the registration messages from clients using recvfrom(), and record the address used by each client. When it wants to send a message, it will have to loop over all the clients it knows about, using sendto() to send the message to each one in turn.
Alternatively, you could use local IP multicast instead of UNIX domain sockets (UNIX domain sockets don't support multicast).
If the question intended to be about broadcasting (as I understand it), then according to unix(4) - UNIX-domain protocol family, broadcasting it is not available with UNIX Domain Sockets:
The Unix Ns -domain protocol family does not support
broadcast addressing or any form of "wildcard" matching
on incoming messages. All addresses are absolute- or
relative-pathnames of other Unix Ns -domain sockets.
May be multicast could be an option, but I feel to know it's not available with POSIX, although Linux supports UNIX Domain Socket multicast.
Also see: Introducing multicast Unix sockets.
It will happen because of
server or client die before unlink/remove for bind() file associate.
any of client/server using this bind path, try to run server again.
solutions :
when you want to bind again just check that file is already associate then unlink that file.
How to step :
first check access of this file by access(2);
if yes then unlink(2) it.
put this peace of code before bind() call,position is independent.
if(!access(filename.c_str()))
unlink(filename.c_str());
for more reference read unix(7)
Wouldn't it be easier to use shared memory or named pipes? A socket is a connection between two processes (on the same or a different machine). It isn't a mass communication method.
If you want to give something to multiple clients, you create a server that waits for connections and then all the clients can connect and it gives them the information. You can accept concurrent connections by making the program multi-threaded or by forking processes. The server establishes multiple socket-based connections with multiple clients, rather than having one socket that multiple clients connect to.
You should look into IP multicasting instead of Unix-domain anything. At present you are just trying to write to nowhere. And if you connect to one client you will only be writing to that client.
This stuff doesn't work the way you seem to think it does.
You can solve the bind error with the following code:
int use = yesno;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&use, sizeof(int));
With UDP protocol, you must invoke connect() if you want to use write() or send(), otherwise you should use sendto() instead.
To achieve your requirements, the following pseudo code may be of help:
sockfd = socket(AF_INET, SOCK_DGRAM, 0)
set RESUSEADDR with setsockopt
bind()
while (1) {
recvfrom()
sendto()
}

Resources