BSD getaddrinfo() endianness - c

I am trying to familiarize myself with the networking BSD API. I understand the fact that multi bytes data submitted to the BSD API (like address and port) must be in network byte order and that we should use converting functions like htons() and htonl() to do this. This snippet of code shows that:
#define IP_ADDRESS(a, b, c, d) ((((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)c) << 8) | ((uint32_t)d))
/* Set server port in BSD format */
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(IP_ADDRESS(192,168,1,1));
serverAddr.sin_port = htons(21);
/* Connect to server */
status = connect(H->bsdSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
Now I am trying to make use of getaddrinfo() in order to retrieve the address information from a server name. I tried that following code:
memset( &hints, 0, sizeof( hints ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo( "srvdc01", "21", &hints, &addr_list );
if (addr_list != NULL)
{
ctx->fd = (int) socket( addr_list->ai_family, addr_list->ai_socktype,
addr_list->ai_protocol );
if (ctx->fd >= 0)
status = connect( ctx->fd, addr_list->ai_addr, addr_list->ai_addrlen );
}
I would have expected that the address returned by getaddrinfo() would be in network byte order in such a way it could be fed directly to the connect() function. Unfortunately, this does not work with the library I am using as the resulting connect is reverting the address bytes (I use Wireshark to investigate this issue).
Is it allowed to safely use addresses returned by getaddrinfo() for feeding the socket() and connect() calls?
Thanks, Franck
=== Update ===
Thanks guys for your comments. I could clarify things a little more. I ran the following code snippet with the following results:
struct addrinfo hints, *addr_list, *cur;
struct sockaddr_in addr_in;
memset(&addr_in, 0, sizeof(addr_in));
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(21);
addr_in.sin_addr.s_addr = htonl(0xC0A80101UL); // 192.168.1.1
myprintf("%08x\n", addr_in.sin_addr.s_addr);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if( getaddrinfo( "srvdc01", "21", &hints, &addr_list ) == 0 )
{
for( cur = addr_list; cur != NULL; cur = cur->ai_next )
{
ctx->fd = (int) socket( cur->ai_family, cur->ai_socktype,
cur->ai_protocol );
if( ctx->fd >= 0 )
{
myprintf("%08x\n", ((struct sockaddr_in *)cur->ai_addr)->sin_addr.s_addr);
}
}
}
freeaddrinfo( addr_list );
The first printf output (after htonl) gives c0a80101 while the second one (after socket creation) gives 0101a8c0. This was surprising because on my little endian platform, I would have expected the htonl() call to swap bytes to bring them in network byte order.
After reviewing documentation of our embedded networking library (third party commercial library), we could find that the BSD API they provide is actually a (quite) limited wrapper on top of their vendor specific network library API. There is a small notice about the fact that their library always work with the native system endianness so their htonl() function is always a dummy call that does nothing.
Now, looking at the link provided by Martin R. http://pubs.opengroup.org/onlinepubs/009695399/functions/freeaddrinfo.html, I agree with him that getaddrinfo() should return an address information suitable for a call to connect() which is visibly not the case with this embedded library.

The POSIX documentation for getaddrinfo()
states that
Upon successful return of getaddrinfo(), the location to which res points shall refer to a linked list of addrinfo structures, each of which shall specify a socket address and information for use in creating a socket with which to use that socket address. The list shall include at least one addrinfo structure. The ai_next field of each structure contains a pointer to the next structure on the list, or a null pointer if it is the last structure on the list. Each structure on the list shall include values for use with a call to the socket() function, and a socket address for use with the connect() function or, if the AI_PASSIVE flag was specified, for use with the bind() function. The fields ai_family, ai_socktype, and ai_protocol shall be usable as the arguments to the socket() function to create a socket suitable for use with the returned address. The fields ai_addr and ai_addrlen are usable as the arguments to the connect() or bind() functions with such a socket, according to the AI_PASSIVE flag.
This implies that – on a POSIX compliant system – IP address and port number in ai_addr are
in network byteorder, as expected by the connect() function.

Related

Send Raw arp reply packet in windows

I am currently learning how to use the windows raw sockets.
I created a raw arp reply frame(includes all headers (ethernet+arp headers)), and when I send it using sendto function,
It fails and return SOCKET_ERROR with error code 10047.
The parameters I used to create the socket are as follows:
socket s = socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
and also I changed the socket options as follows:
int on=1;
setsockopt(s,IPPROTO_IP, 2,(char*)&on,sizeof(on));
(By the way, '2' is equal to IP_HDRINCL, for some reason, visual studio didn't recognize it..)
I try to send the packet as follows:
socketaddr sa = { 0 };
int SentBytesCount = sendto(s, (char*)&arp_raw_msg,sizeof(Arp_Frame),0,&sa,sizeof(sa));
Where Arp_Frame is a struct that includes ethernet header+arp header+18 Bytes for padding.
After this call I get that SentBytesCount is equal to SOCKET_ERROR(-1), and no packet is sent.
Thank you for your help!
Winsock error 10047 is WSAEAFNOSUPPORT:
Address family not supported by protocol family.
An address incompatible with the requested protocol was used. All sockets are created with an associated address family (that is, AF_INET for Internet Protocols) and a generic protocol type (that is, SOCK_STREAM). This error is returned if an incorrect protocol is explicitly requested in the socket call, or if an address of the wrong family is used for a socket, for example, in sendto.
You created an AF_INET (IPv4) socket, but you are not passing sendto() a valid sockaddr_in containing an IPv4 address and port, hence the error. You are passing it an empty socketaddr (what is that?) instead.
Any sockaddr_... struct you use with a socket must match what the socket's address family expects, as set by the socket() call (in your case, AF_INET, which uses sockaddr_in addresses).
sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("destination IP address");
sa.sin_port = htons(Destination port number);
int SentBytesCount = sendto(s, (char*)&arp_raw_msg, sizeof(Arp_Frame), 0, (struct sockaddr*)&sa, sizeof(sa));
As for IP_HDRINCL, it is defined in ws2tcpip.h.

Cannot open FTP connection when server address is FQDN

My FTP program written in C only works when the server address is an IP address. But the connection fails when the server address is a Fully Qualified Domain Name (FQDN).
When ftp_host is FQDN, the ftp connection fails to open. Please help.
I'd strongly recommend using the getaddrinfo() function, as gethostbyname() is obsolete. This also has the advantage that it's easy to convert to IPv6, either now or later.
I'm assuming you only care about IPv4 addresses, so here's an example of a function which will take a hostname as its parameter and fill in a struct sockaddr_in for you:
int get_ftp_addr(const char *hostname, struct sockaddr_in *addr)
{
char host_buffer[256];
struct addrinfo hints;
struct addrinfo *result;
struct sockaddr_in *res_addr;
int error = -1;
char *colon;
snprintf(host_buffer, sizeof(host_buffer), "%s", hostname);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
colon = strchr(host_buffer, ':');
if (colon) {
*colon = '\0';
error = getaddrinfo(host_buffer, colon + 1, &hints, &result);
} else {
error = getaddrinfo(host_buffer, "ftp", &hints, &result);
}
if (error != 0 || !result) {
return error;
}
res_addr = (struct sockaddr_in*)(result->ai_addr);
memcpy(addr, res_addr, sizeof(struct sockaddr_in));
freeaddrinfo(result);
return 0;
}
Note that I only take a copy of the string to avoid modifying the version in the caller - don't feel obliged to keep that part if you don't want to, but personally I think it makes a cleaner interface.
This function will accept both IP addresses in dotted quad notation and fully-qualified hostnames, as getaddrinfo() accepts both of those. If a port is specified with a colon it will be used, otherwise the default FTP port will be used.
A zero return value indicates success. A positive return can be passed into gai_strerror() to get a string error code, or you can check the getaddrinfo() man page for the possible error codes. A return of -1 indicates a successful result from getaddrinfo() but no result structure - I don't think this can happen, but I don't like leaving any possible return code unhandled.
There are several caveats here, the two most important are:
This code only support IPv4 as it stands, although getaddrinfo() makes it really easy to support IPv6 as well. If you want to support both then change the AF_INET in the hints structure to AF_UNSPEC and you'll get all address families. You'll need to iterate over addresses, though, and filter out only the IPv4 and IPv6 ones (see my next point).
A DNS lookup can potentially look up to multiple IP addresses - this is quite common with large sites such as Google as they use this feature for load-balancing across hosts, and also redundancy. Ideally you code should iterate across all the returned addresses and try connecting to each one until one works. For an FTP client this is perhaps overkill, but I think it's important to be aware of it.
If you want to support IPv6, or support multi-A-records (i.e. multiple addresses back from the DNS query), then you need to follow the ai_next pointer in the struct addrinfo structure - something like this:
struct addrinfo *res;
/* Assume result is initialised as above via getaddrinfo() */
for (res = result; res != NULL; res = res->ai_next) {
...
}

Error from socket() call

I wanted to add IPv6 support for my program and came across odd error. I want to open socket which accepts connections. Here is my scenario:
First I use getaddrinfo() to find socket configuration:
struct addrinfo hint, *info = NULL;
memset((void *)&hint, 0, sizeof(hint));
hint.ai_family = AF_INET6;
hint.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
hint.ai_socktype = SOCK_STREAM;
if ((error = getaddrinfo(NULL, "1234", &hint, &info))) {
/* some error handling */
}
I gen non-NULL result in info. Then I want to create socket:
socket(info->ai_family, info->ai_socktype, info->ai_protocol)
Here I get -1
strerror(errno)
returns Address family not supported by protocol
I thought that getaddrinfo() returns only addresses that can be bound and I would expect it to return NULL in this case. What am I missing?
I want to add that my eth0 does not have IPv6 address right now. I am using uClibc 0.9.32
You need to add AI_ADDRCONFIG to ai_flags to make sure it doesn't return address families unsupported by the kernel. I pretty much always add that flag nowadays.
If you want to support kernels without IPv6 you'll have to avoid setting ai_family to AF_INET6. Instead you have to listen on all the returned addresses, and make sure to set IPV6_V6ONLY on the AF_INET6 ones. There's no guarantee that the AF_INET6 address will be returned first, unfortunately.
getaddrinfo returns a linked list of struct addrinfo objects, which you can traverse using the ai_next member. You should try each returned addrinfo object in turn until you get a connection (or hit NULL/end of list).

Unix sockets with getaddrinfo() in C

Does anyone know if it's possible to use getaddrinfo with unix sockets in C (AF_UNIX). I've tried a couple of things but I can't make it work.
This is basically what I'm trying:
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNIX;
hints.ai_socktype = SOCK_STREAM;
if(getaddrinfo("What should I put here?", "What should I put here?", &hints, &res) != 0){
//do sth about
}
My question is how to fill the node and service fields, in case that is possible to use it with unix sockets.
Thanks in advance.
Some implementations of getaddrinfo() have supported AF_UNIX socket addresses, but they no longer do so due to security concerns.
You don't really need a function to "look up" an AF_UNIX address anyway - if you know the socket path, then you can just copy it straight into a sockaddr_un of sufficient size. There's no resolution step for AF_UNIX addresses - the socket name is the socket address.
From man 3 getaddrinfo:
The hints parameter specifies the preferred socket type, or protocol. A NULL hints specifies that any network address or protocol is acceptable. If this parameter is not NULL it points to an addrinfo structure whose ai_family, ai_socktype, and ai_protocol members specify the preferred socket type. AF_UNSPEC in ai_family specifies any protocol family (either IPv4 or IPv6, for example). 0 in ai_socktype or ai_protocol specifies that any socket type or protocol is acceptable as well. The ai_flags member specifies additional options, defined below. Multiple flags are specified by logically OR-ing them together. All the other members in the hints parameter must contain either 0, or a null pointer.
So it looks like you need to pass in the hints parameter to tell it you want a unix socket.
A lot late, but just for the records, did you try this -
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; //is the address family(IPV4 or 6) or UNSPEC for either
hints.ai_socktype = SOCK_STREAM;
if(getaddrinfo(parm1, parm2, &hints, &res) != 0){
//do sth about
}
parm1 -> is the host name or address to connect to. A call with NULL here puts the address of local host, in which case you specify hints.ai_flags=AI_PASSIVE.
parm2 -> is the name of the intended service (http, ftp etc) or the corresponding port number for that service the socket is used for

Why can't a bind linux service to the loop-back only?

I am writing a server application that will provide a service on an ephemeral port that I only want accessible on the loopback interface. In order to do this, I am writing code like the following:
struct sockaddr_in bind_addr;
memset(&bind_addr,0,sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = 0;
bind_addr.sin_addr.s_addr = htonl(inet_addr("127.0.0.1"));
rcd = ::bind(
socket_handle,
reinterpret_cast<struct sockaddr *>(&bind_addr),
sizeof(bind_addr));
The return value for this call to bind() is -1 and the value of errno is 99 (Cannot assign requested address). Is this failing because inet_addr() already returns its result in network order or is there some other reason?
inet_addr should be avoided, for there is a much saner method of constructing struct sockaddrs (which means it also obsoletes gethostby*):
#include <netdb.h>
/* Error checking omitted for brevity */
struct addrinfo hints = {.ai_flags = AI_PASSIVE};
struct addrinfo *res;
getaddrinfo("::1", NULL, &hints, &res); /* or 127.0.0.1 if you are 60+ */
bind(fd, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
Is this failing because inet_addr() already returns its result in network order
Yes.
So remove the htonl call.

Resources