getaddrinfo returns 2 results with localhost - c

I am trying to use the function getaddrinfo with a passive socket/listener instead of filling directly the old sockaddr structure.
My purpose is to open a socket and bind it to a port. Essentially I am building a server.
I don't know why but getaddrinfo returns 2 results; because of this in the loop below the second time I call bind it exit with an error saying "address already in use".
Do you know how to fix this and if it is correct to have it in a loop ?
struct addrinfo addr;
memset(&addr,0,sizeof(addrinfo));
addr.ai_family = AF_INET;
addr.ai_socktype = SOCK_STREAM;
addr.ai_protocol = 0;
addr.ai_flags = AI_PASSIVE;
struct addrinfo * rp,* result;
getaddrinfo( "localhost","59001",&addr,&result );
for( rp = result; rp != NULL; rp = rp->ai_next ){
int sd = socket( AF_INET, SOCK_STREAM, 0 );
if(-1==sd ){
printf( "error creating the socket %m");
return -1;
}
if( -1==bind( sd, result->ai_addr, result->ai_addrlen ) ){
printf( "error binding %m")
return -1;
}
if( -1== listen(sd, MAX_BACKLOG ) ){
std::cerr << "listen didn't work" << std::endl;
return -1;
}
}

There are a couple of things going on here. For the multiple results from getaddrinfo(), that's because it returns an IPv4 address and a IPv6 address (you have both). As for the "address already in use", you need to set the SO_REUSEADDR option. Put this between your calls to socket() and bind():
int opval = 1;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

Is this on a redhat machine perchance? There is a wellknown bug on those, that because /etc/hosts/ lists localhost twice, when you request specifically AF_INET you actually get the same result twice; once for IPv4 and once for IPv6 translated a bit.
See also https://bugzilla.redhat.com/show_bug.cgi?id=496300

Related

How to bind socket to a particular interface in vxWorks

I am trying to bind a socket to a particular network interface on my computer. I have two network interfaces named interf0 and interf1. I want bind socket to a particular interface say interf0. My OS is vxWorks 6.2.
I am trying following code:
struct sockaddr_in fromAddr;
struct sockaddr_in sin;
int fromLen;
struct ip_mreq ipMreq;
int sockDesc;
STATUS temp;
if ((sockDesc = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf (" cannot open recv socket\n");
return ERROR;
}
bzero ((char *)&sin, sizeof (sin));
bzero ((char *) &fromAddr, sizeof(fromAddr));
fromLen = sizeof(fromAddr);
#if 1
if ((temp = setsockopt(sockDesc, SOL_SOCKET, SO_BINDTODEVICE, "interf0", 7)) < 0)
{
perror("Server-setsockopt() error for SO_BINDTODEVICE");
printf("%s\n", strerror(errno));
close(sockDesc);
return ERROR;
}
#endif
sin.sin_len = (u_char) sizeof(sin);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
//sin.sin_addr.s_addr = inet_addr(ifAddr);
/* UDP port number to match for the received packets */
sin.sin_port = htons (mcastPort);
/* bind a port number to the socket */
if (bind(sockDesc, (struct sockaddr *)&sin, sizeof(sin)) != 0)
{
perror("bind");
if (sockDesc != ERROR)
{
close (sockDesc);
}
return ERROR;
}
Here, it gives an error saying SO_BINDTODEVICE is not defined.
Is there any other way using which I can bind to a particular interface in vxWorks.
Other ref:
bind socket to network interface
Thank you.
It seems that SO_BINDTODEVICE is not part of POSIX, it's a Linux extension. So VxWorks won't necessarily implement interface binding the same way, if it does it at all. A quick look in the VxWorks manuals looks unpromising.
If you have VxWorks you likely also have access to WindRiver's support, assuming you've kept up with the support fees. If so, ask them too, that's what it's there for.

Need IPv6 Multicast C code that works on iOS 9

Apple now requires iOS 9 apps to be IPv6 compliant. We're mostly OK, except for a bit of code which sends a UDP broadcast - this now fails in iOS 9.
Everything I read tells me that UDP multicast is the right way to do this in IPv6. I've found some example code, but it doesn't work on any version of iOS or Mac OS X I've tried.
This code is being called from a C/C++ lib inside our program - difficult to make a callback into Swift, Obj-C, Java, etc. And this code will be shared by a Mac OS X and Android version of our app. One would think it's possible to do IPv6 multicast in C in any POSIX environment!
In the sample below, execution succeeds up to the final sendto() call, which actually sends the UDP message. That sendto() fails, with errno set to EBROKENPIPE (22) after the failure.
My best guess is that I'm missing some required setsockopt() call, or am using the wrong multicast address. Right now, I'm stumped.
Here's the function call I'm making (to multicast "Is anybody out there?" on UDP port 4031):
char *msg = "Is anybody out there?";
err = multicast_udp_msg ( "FF01::1111", 4031, msg, strlen(msg) );
Here's the code that is being called:
// Multicasts a message on a specific UDP port.
// myhost - IPv6 address on which to multicast the message (i.e., ourself)
// port - UDP port on which to broadcast the mssage
// msg - message contents to broadcast
// msgsize - length of message in bytes
// Return value is zero if successful, or nonzero on error.
int multicast_udp_msg ( char *myhost, short port, char *msg, size_t msgsize )
{
int sockfd, n;
char service[16] = { 0 };
int err = 0;
struct addrinfo hints = { 0 }, *res, *ressave;
struct sockaddr_storage addr = { 0 };
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
sprintf ( service, "%hd", port );
n = getaddrinfo ( myhost, service, &hints, &res );
if ( n < 0 )
{
fprintf(stderr, "getaddrinfo error:: [%s]\n", gai_strerror(n));
return -1;
}
ressave = res;
sockfd = socket ( res->ai_family, res->ai_socktype, res->ai_protocol );
if ( sockfd >= 0 )
{
memcpy ( &addr, res->ai_addr, sizeof ( addr ) );
if ( joinGroup ( sockfd, 0, 8, &addr ) == 0 )
if ( bind ( sockfd, res->ai_addr, res->ai_addrlen ) == 0 )
if ( sendto ( sockfd, msg, msgsize, 0, (struct sockaddr *) &addr, sizeof ( addr ) ) < 0 )
err = errno;
close ( sockfd );
res = res->ai_next;
}
freeaddrinfo ( ressave );
return err;
}
int
joinGroup(int sockfd, int loopBack, int mcastTTL,
struct sockaddr_storage *addr)
{
int r1, r2, r3, retval;
retval=-1;
switch (addr->ss_family) {
case AF_INET: {
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr=
((struct sockaddr_in *)addr)->sin_addr.s_addr;
mreq.imr_interface.s_addr= INADDR_ANY;
r1= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
&loopBack, sizeof(loopBack));
if (r1<0)
perror("joinGroup:: IP_MULTICAST_LOOP:: ");
r2= setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
&mcastTTL, sizeof(mcastTTL));
if (r2<0)
perror("joinGroup:: IP_MULTICAST_TTL:: ");
r3= setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const void *)&mreq, sizeof(mreq));
if (r3<0)
perror("joinGroup:: IP_ADD_MEMBERSHIP:: ");
} break;
case AF_INET6: {
struct ipv6_mreq mreq6;
memcpy(&mreq6.ipv6mr_multiaddr,
&(((struct sockaddr_in6 *)addr)->sin6_addr),
sizeof(struct in6_addr));
mreq6.ipv6mr_interface= 0; // cualquier interfaz
r1= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&loopBack, sizeof(loopBack));
if (r1<0)
perror("joinGroup:: IPV6_MULTICAST_LOOP:: ");
r2= setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&mcastTTL, sizeof(mcastTTL));
if (r2<0)
perror("joinGroup:: IPV6_MULTICAST_HOPS:: ");
r3= setsockopt(sockfd, IPPROTO_IPV6,
IPV6_JOIN_GROUP, &mreq6, sizeof(mreq6));
if (r3<0)
perror("joinGroup:: IPV6_ADD_MEMBERSHIP:: ");
} break;
default:
r1=r2=r3=-1;
}
if ((r1>=0) && (r2>=0) && (r3>=0))
retval=0;
return retval;
}
Thoughts welcome!
-Tim
After some back-and-forth with Apple, and some additional context, we have an answer. But it's not the answer to my original question. First, here's the Apple thread for context:
https://forums.developer.apple.com/message/71107
It turns out that IPv6 multicast was not actually what we needed to solve the real problem at hand - namely, finding a legacy embedded device on a local Wi-Fi network. We really had to use IPv4 UDP broadcast to do that. Our embedded device ignores IPv6 multicast packets like the Earth ignores neutrinos flying through it.
Apple gave us a setsockopt() call which enabled IPv4 UDP broadcast to work in iOS 9 on an infrastructure Wi-Fi network. That is the intended use case for this feature. And Apple also gave us a likely cause of failure when that broadcast failed to work in an Ad Hoc Wi-Fi network (that appears to be a known iOS 9 issue).
So, although my original question is not answered here, the underlying issue has been resolved.

Why cannot bind to port with specified iP address, but it works well with localhost?

I am creating a server that send message to all clients if one client send message to server. I tested my server with telnet and two other clients in the in my computer. It works find if I use "localhost" as value in the first argument of function getaddrinfo. BUt when I replace it with my ip address, which I obtain by searching on google, it failed to bind socket to the port. Why that happens?
This works well when I test my telnet.
if ((rv = getaddrinfo("localhost", PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
The program fails to bind if I specified the IP addresss
Note: that I replaced my IP addresss with the "xx.xxx.xxx.xxx"
if ((rv = getaddrinfo("xx.xxx.xxx.xxx", PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
It is most unusual to want to bind to "localhost" or to your external IP address. Most computers in this day and age don't have an external IP address (the one that Google sees), because there's a router and a demilitarized zone between them and the internet. If you want to expose a service that's visible to your LAN to the internet, then you'll need to configure your router to forward connections, which is a topic that's more suitable for the networking variant of Stack Overflow.
Providing your hints.ai_flags = AI_PASSIVE, you should be able to use NULL in place of "localhost" and getaddrinfo will return a list of all suitable interfaces that you can bind on. If your hints.ai_flags doesn't contain AI_PASSIVE, then there's no guarantee that you can bind on the interfaces returned. Should you choose to use a string here, the AI_PASSIVE flag will be ignored anyway...
In your code, you're creating multiple sockets one by one, discarding the previously created as you create the next one... That's a resource leak, and possibly the cause for the problem you've noticed too. Perhaps you intended to store your multiple socket descriptors into an array, and bind them one at a time?
Don't forget to freeaddrinfo once you're done with ai. In the example below I've used PF_UNSPEC together with SOCK_STREAM to listen on both IPV4 and IPV6 interfaces (as well as any other stream types), but these can be changed to bind to other types of addresses.
#define BIND_ADDR NULL
#define BIND_PORT "1234"
struct addrinfo *ai;
int rv = getaddrinfo(BIND_ADDR,
BIND_PORT,
&(struct addrinfo){ .ai_family = PF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_flags = AI_PASSIVE },
&ai)
if (rv != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
size_t ai_size = 0;
for (struct addrinfo *a = ai; a != NULL; a = a->ai_next) {
ai_size++;
}
int socket[ai_size] = { 0 };
size_t x = 0;
for (struct addrinfo *a = ai; a != NULL; a = a->ai_next) {
socket[x++] = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
/* ... */
}
freeaddrinfo(ai);
/* ... */

UDP Socket in C - Setting it up wrong?

I'm working on a project that involves sending various requests to a server through UDP. However, I seem to be setting up the socket entirely wrong, as the server does not respond to any of my requests. We were provided with a server binary to test against, and the code below ellicits no response. Am I setting up the UDP socket correctly? If so, am I somehow using sendto wrong? I have confirmed that I am sending the correct number of bits.
The input for the program is: ./client [URL] [port] [username], and I always test with ./client localhost 8080 user. Here is the struct I am sending and the code.
struct request_login {
int req_type; /* = REQ_LOGIN */
char req_username[32];
} packed;
Code:
struct sockaddr_in sa;
int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd == -1){
printf("Could not create socket.");
exit(EXIT_FAILURE);
}
// Prepare the socket address
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(argv[1]);
// Convert to network order
sa.sin_port = htonl(atoi(argv[2]));
// Assemble and send login request
struct request_login * reqlogin = (struct request_login *) malloc(sizeof(struct request_login));
reqlogin->req_type = REQ_LOGIN;
strcpy(reqlogin->req_username, argv[3]);
int res = sendto(sockfd, reqlogin, sizeof (struct request_login), 0, (struct sockaddr*)&sa, sizeof sa);
free(reqlogin)
Huh?
This:
sa.sin_addr.s_addr = inet_addr(argv[1]);
certainly won't do the right thing if, as you say, argv[1] is typically "localhost". You need to look up the host name, so that you get an IP address. You can only use inet_addr() if the input is a dotted IP address, not a host name.
Look at getaddrinfo().
After re-reading your code a couple of times, I think I know what one cause of the error may be:
sa.sin_port = htonl(atoi(argv[2]));
The port number is a short so you should use htons instead. It's very small and easy to miss.
try this instead:
struct addrinfo hint;
memset(&chk,0,sizeof(chk));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
struct addrinfo* servAddr = NULL;
int ret = getaddrinfo(argv[1],atoi(argv[2]),&hint,&servAddr);
if (-1 == ret)
{
perror("getaddrinfo failed");
exit(EXIT_FAILURE);
}
int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd == -1){
printf("Could not create socket.");
exit(EXIT_FAILURE);
}
// Assemble and send login request
struct request_login reqlogin;
reqlogin.req_type = REQ_LOGIN;
strcpy(reqlogin.req_username, argv[3]);
int res = sendto(sockfd, &reqlogin, sizeof (struct request_login), 0, servAddr->ai_addr, servAddr->ai_addrlen);

Error "No such device" in call setsockopt when joining multicast group

I have a code in which send multicast datagrams.
A critical piece of code:
uint32_t port;
int sockfd, err_ip;
const uint32_t sizebuff = 65535 - (20 + 8);
unsigned char *buff = (unsigned char *) malloc(sizebuff);
struct sockaddr_in servaddr, cliaddr;
struct in_addr serv_in_addr;
struct ip_mreq req;
port = str2uint16(cmdsrv->ipport);
bzero(buff, (size_t)sizebuff);
bzero(&servaddr, sizeof(servaddr));
bzero(&serv_in_addr, sizeof(serv_in_addr));
err_ip = inet_aton(cmdsrv->ipaddr, &serv_in_addr);
if(( err_ip != 0 ) && ( port != 0 )) {
servaddr.sin_family = AF_INET;
servaddr.sin_addr = serv_in_addr;
servaddr.sin_port = htons(port);
memcpy(&req.imr_multiaddr,&serv_in_addr,sizeof(req.imr_multiaddr));
req.imr_interface.s_addr = INADDR_ANY;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if( sockfd == -1 ) {
int outerror = errno;
char *retstr = "Couldn't open socket\n";
pthread_exit(retstr);
}
else {
struct in_addr ifaddr;
ifaddr.s_addr = INADDR_ANY;
int optres3 =
setsockopt( sockfd, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr,
sizeof( ifaddr ));
if( optres3 == -1 ) {
int perrno = errno;
char *retstr = "Can't set IP_MULTICAST_IF for socket\n";
printf( "Error setsockopt: ERRNO = %s\n", strerror( perrno ));
printf( "%s",retstr );
pthread_exit(retstr);
}
unsigned char ttl = 32;
int optres2 =
setsockopt( sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
sizeof( ttl ));
if( optres2 == -1 ) {
int perrno = errno;
char *retstr = "Can't set IP_MULTICAST_TTL for socket\n";
printf("Error setsockopt: ERRNO = %s\n",strerror(perrno));
printf("%s",retstr);
pthread_exit(retstr);
}
int optres =
setsockopt( sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req,
sizeof( req ));
if( optres == -1 ) {
int perrno = errno;
char *retstr = "Can't join to multicast-group\n";
printf("Error setsockopt: ERRNO = %s\n",strerror(perrno));
printf("%s",retstr);
pthread_exit(retstr);
}
// Bind port with socket
uint16_t cliport;
cliaddr.sin_family = AF_INET;
cliaddr.sin_addr.s_addr = INADDR_ANY;
if( strcmp( cmdsrv->ipport, "16011" ) == 0 ) {
cliport = str2uint16("16003");
cliaddr.sin_port = htons(cliport);
}
else if( strcmp( cmdsrv->ipport, "16012" ) == 0 ) {
cliport = str2uint16("16004");
cliaddr.sin_port = htons(cliport);
}
else {
printf("Device hasn't such port");
pthread_exit(NULL);
}
int bindres =
bind( sockfd, (struct sockaddr*)&cliaddr, sizeof( cliaddr ));
if( bindres == -1 ) {
int perrno = errno;
perror("Error in bind\n");
}
// ADD 1 BYTE
data rawdata;
rawdata.desc = 23;
printf( "SIZEOF = %d\n", sizeof( *( cmdsrv->cmd )));
memcpy( &rawdata.cmd, cmdsrv->cmd, sizeof( *( cmdsrv->cmd )));
printf( "RAWDATA: desc = %d, cmd = %d\n", rawdata.desc, rawdata.cmd );
int outerror = 0;
printf( "Send command to IP:\n addr = %s, port = %d\n",
inet_ntoa( servaddr.sin_addr ), ntohs( servaddr.sin_port ));
int size = sendto( sockfd, &rawdata, sizeof( rawdata ), 0,
(struct sockaddr*)&servaddr, sizeof( servaddr ));
if( size == -1 ) {
perror("Can't send command to socket");
}
...
Sometimes program executes successfully (at this moment I have IP - 192.168.80.122). I can capture my multicast datagram by wireshark. That's all OK.
But if I change my IP to 192.168.1.2, I get error when is called
int optres =
setsockopt( sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req,
sizeof( req ));
And I can't even capture my multicast packet. Nothing is sent.
Where's bug?
If it works for one IP but not for another, maybe this can help.
What does "IP_ADD_MEMBERSHIP: No such device" mean?
It means that the tool is trying to use multicast but the network interface doesn't support it There are two likely causes:
Your machine doesn't have multicast support enabled. For example, on Linux and FreeBSD it is possible to compile a kernel which doesn't support multicast.
You don't have a route for multicast traffic. Some systems don't add this by default, and you need to run. route add -net 224.0.0.0 netmask 224.0.0.0 eth0 (or similar). If you wish to use RAT in unicast mode only, it is possible to add the multicast route on the loopback interface.
IP_ADD_MEMBERSHIP and bind() are only required for receiving multicast, use IP_MULTICAST_IF instead for effectively a "send-only membership" of a multicast group.
IP_MULTICAST_IF sets the kernel to send multicast packets for a given group on a given interface, it is effectively "send-only" as you will not be able to receive traffic on that group after setting. This varies by platform: Posix platforms generally function this way as an optimisation, whilst Win32 will perform software level routing to propagate locally generated packets.
You might not use the interface address as INADDR_ANY.
req.imr_interface.s_addr = INADDR_ANY;
check where the loopback is running using ifconfig.
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
You need to use interface address as inet addr i.e. 127.0.0.1
Check your loopback interface address and use that address only.

Resources