First of all, i understand what the code (see at the end of the post) does in general and i dont want an overall explanation.
What i don't understand is this particular line of code:
memset(&hints, 0, sizeof(struct addrinfo));
What i get so far is that a memset() is a function to fill the string which it is pointed to up.
It has three parameters, first the pointer to the string, second the value to be set and third the number of bytes set to the value.
In this case the the value to be filled up is &hints which would be the adress of the variable hints. The value which should be set is 0 so it is filled with zeroes. And last it is filled to the size of the struct addrinfo.
So in this case memset() generates for the variable hints zeroes to the size of the struct. Did i get this right?
If yes, why is this needed in my example?
#include <stdlib.h>/* EXIT_SUCCESS */
#include <stdio.h>/* printf */
#include <string.h>/* memset() */
#include <errno.h>/* int errno */
#include <sys/types.h>/* socket defines */
#include <sys/socket.h>/* socket() */
#include <netdb.h>/* getaddrinfo() */
#define ECHO_PORT "7"
int main (int argc, char* argv[]){
if (argc != 2) {
printf ("Usage: %s HOSTNAME\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Resolve host addresses: */
struct addrinfo hints;
struct addrinfo* result, *rp;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;/* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;/* Stream socket */
hints.ai_flags = 0;
hints.ai_protocol = 0;/* Any protocol */
int err = getaddrinfo(argv[1], ECHO_PORT, &hints, &result);
/* Handle potential error: */
if (err) {
printf("Error: getaddrinfo: %s\n", gai_strerror(err));
exit(EXIT_FAILURE);
}
/* Print names found: */
printf("Addresses for %s:\n", argv[1]);
for (rp = result; rp != NULL; rp = rp->ai_next) {
int af = rp->ai_family;
char* address = NULL;
int ok;
if (AF_INET == rp->ai_family) {
uint8_t in_addr =((struct sockaddr_in*)rp->ai_addr)->sin_addr.s_addr;
address = malloc(INET_ADDRSTRLEN);
ok = inet_ntop(af, &in_addr, address, INET_ADDRSTRLEN);
}
if (AF_INET6 == rp->ai_family) {
char* in6_addr =((struct sockaddr_in6*)rp->ai_addr)->sin6_addr.s6_addr;
address = malloc(INET6_ADDRSTRLEN);
ok = inet_ntop(af, in6_addr, address, INET6_ADDRSTRLEN);
}
if (ok) {
printf("%s\n", address);
}
else {
perror("inet_ntop");
}
free(address);
}
freeaddrinfo(result);
return EXIT_SUCCESS;
}
Yes, you understood it correctly.
It's needed in the code below because in
struct addrinfo hints;
hints is left uninitialized and the programmer wanted to make sure all the fields a zeroed.
An easier solution would be to initialize it directly:
addrinfo hints{}; // C++11 and later
struct addrinfo hints = {0}; /* C and C++ */
and skip memset.
Another option is to initialize it with the correct values using designated initializers (C99 and C++20). In C you can specify the fields out-of-order, but not in C++, so this order would work in both:
struct addrinfo hints = { /* "struct" not needed in C++ */
.ai_flags = 0,
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
/* .ai_protocol and the rest will be zeroed */
};
For older standards without designated initializers:
struct addrinfo hints = {
0,
AF_UNSPEC,
SOCK_STREAM
};
Yes, your understanding is basically correct. The code is simply filling the entire hints variable with 0x00 bytes before passing it to getaddrinfo(). This is needed to initialize the hints to a default state, which is important because addrinfo contains flags and memory pointers to control getaddrinfo()'s behavior. So you can't just leave the hints in an uninitialized state, it will contain random garbage that will cause undefined behavior, confusing getaddrinfo() and/or even leading to corrupted memory, crashes, etc.
Using the memset() is a quick way to initialize all of the fields of the hints to zeros in one quick operation, instead of initializing each field individually. This way, you can focus on assigning values to just the fields you are actually interested in.
An easier way to initialize the hints is like this instead:
struct addrinfo hints = {0};
This will value-initialize the first field (ai_flags) to 0, and default-initialize the remaining fields to their default values, which in this case is also zeros.
You're correct that memset is being used here to set all bytes of hints to zero.
This is being done so that any field that is not explicitly set later has the value 0. Because hints is uninitialized, its fields have indeterminate values, so doing this sets all fields to 0. If you look at the definition of struct addrinfo:
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
You can see that the program only explicitly sets the first 4 fields. The call to memset first takes care of the rest.
Related
We're currently developing a small tool that reads CAN-messages and evaluates the data contained. The bind() command doesn't return 0 (for a successfull bind) when the char array empty is too short, too long or not present. This bug is present in the shortened program below.
#include <sys/socket.h> // socket, bind, PF_CAN, SOCK_RAW
#include <linux/if.h> // ifreq
#include <linux/can.h> // sockaddr_can, CAN_RAW
#include <stdio.h> // printf
int main()
{
struct sockaddr_can addr;
struct ifreq ifr;
int socketFd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
addr.can_ifindex = ifr.ifr_ifindex;
char empty[5]; // change length or comment out to provoke the error
if(bind(socketFd, (struct sockaddr *) &addr, sizeof(addr)) != 0)
{
printf("Unable to bind the CAN-socket.\n");
}
}
The behaviour changes with the length of our char array empty.
For example [5] works, while [24] doesn't.
We tested this with GCC 5.4.0 and 7.2.0 which both showed this behaviour.
Under GCC 5.3.0 the presence and lentgh of empty do not influence our bind().
For reference we used
gcc main.c
./a.out
Why do we need an unused char array with GCC 5.4.0 and 7.2.0 for a successfull bind?
You're causing all sorts of undefined behavior due to use of uninitialized variables.
You never initialize ifr, then you use ifr.ifr_ifindex.
You never initialize addr.tp, then you pass addr as the sockaddr parameter to bind().
That it ever succeeds at all is more surprising than the fact that the failure depends on the size of the empty array. What address are you expecting the socket to be bound to?
We followed the documentation found at kernel.org.
In "4. How to use SocketCAN" (approx. line 60) this example is given:
int s;
struct sockaddr_can addr;
struct ifreq ifr;
s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
strcpy(ifr.ifr_name, "can0" );
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
bind(s, (struct sockaddr *)&addr, sizeof(addr));
(..)
While it states that no error checking has been implemented neither addr nor ifr have been fully initialised (like all of you mentioned here). Therefore the given example encounters the same problem as our program.
Initialising them with memset like #zwol mentioned to ensure all values are set to 0 before working with them fixes this unexpected behaviour completely.
Thanks everyone.
I'm trying to print an ip address from inet_ntop, but the output seems pretty weird.
The program seems to work well, I succeed to connect the socket but it print this:
H��H9�u�H�[]A\A]A^A_�ff.�
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]){
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct addrinfo hints, *result;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_flags = 0;
int s = getaddrinfo("irc.root-me.org", "6667", &hints, &result);
if( s != 0){
printf("erreur\n");
exit(EXIT_FAILURE);
}
int f = connect(sock, result->ai_addr, result->ai_addrlen);
if(f != 0){
printf("erreur connect\n");
}
struct sockaddr_in *sockin;
sockin = (struct sockaddr_in *)result->ai_addr;
char *dst;
inet_ntop(AF_INET, &sockin->sin_addr, dst, sizeof(char *));
printf("%s\n", dst);
freeaddrinfo(result);
exit(0);
}
Two issues here:
The 3rd parameter to inet_ntop() should be a char-array or a pointer to the 1st element of a char-array.
The 4th parameter to inet_ntop() should be the size of the destination buffer, whose address is passed as 3rd parameter.
What you do is to pass the uninitialised char-pointer dst as destination and tell the function it would point to sizeof(char*) bytes memory.
An AF_INET address (which has the form xxx.xxx.xxx.xxx) uses a maximum of 4x3 characters plus 3 delimiting . chars which sums up to 15 chars plus 1 additional char used as 0-terminator to make it a C-"string", so the corrected code would look like this:
char dst[16] = ""; /* inet_ntop() does not necessarily 0-terminates the result. */
inet_ntop(AF_INET, &sockin->sin_addr, dst, sizeof dst);
As pointed out by Filipe Gonçalves in his comment one can use INET_ADDRSTRLEN (if available) instead of the hard coded "magic number" 16 to define the buffer size for the textual representation of a IPv4 address. It's a good thing.
Documentation for inet_ntop() is here:
POSIX in general
Specific to Linux
The Problem
The problem is with the size of the destination char* you're trying to save the result in, and the fact that it is uninitialized, what you want to do is save it in a char[].
sizeof(char*) on x86 is 4B and 8B on x64, IP Addresses are usually larger than that (an IPv4 address is between 7 to 15 bytes) + 1 for null terminator.
Solution
You can fix the code as follows:
char dst[16] = {0};
inet_ntop(AF_INET, &sockin->sin_addr, dst, sizeof(dst));
After the fix:
$ ./main
212.83.153.145
Source Code
If you want the full fixed main.c, I've uploaded it to github.
I am trying to send a buffer via UDP sockets in C but I always get an invalid argument error in sendto. I just don't find the error. Could anyone maybe help me. Thanks in advance.
Here's my code:
/**/ void IPCSend(char *pazClientAddress, int iClientPort, char *pazBuffer )
{
int iSocket;
/* */
if ((iSocket = socket(AF_INET, SOCK_DGRAM, 0)) != -1)
{
int iSendToReturn;
struct sockaddr_in sinServerAddress;
struct hostent *pstHost;
/* */
pstHost = (struct hostent *)gethostbyname((char *)pazClientAddress);
/* */
sinServerAddress.sin_family = AF_INET;
sinServerAddress.sin_addr = *((struct in_addr *)pstHost->h_addr);
sinServerAddress.sin_port = iPortNumber;
bzero(&(sinServerAddress.sin_zero),8);
/* */
fprintf(stdout,"sending '%s' to client '%s:%d'\n",pazBuffer,pazClientAddress,iClientPort);
iSendToReturn = sendto(iSocket, pazBuffer, sizeof(pazBuffer), 0, (struct sockaddr *)&sinServerAddress, sizeof(sinServerAddress));
/* */
if(iSendToReturn != -1)
fprintf(stdout,"%d bytes sent\n",iSendToReturn);
else
perror("SendTo");
/* */
close(iSocket);
}
else
fprintf(stdout,"could not connect to server\n");
}
Firstly, you're making the perennial error of novice C programmers: using sizeof to get the size of a pointer. The variable pazBuffer is a pointer, not an array, so the sizeof operator will not return the array length, which is what you want. Your IPCSend function needs to take in the length of pazBuffer as a separate parameter:
void IPCSend(char *pazClientAddress, int iClientPort, char *pazBuffer, size_t len)
As for the error you're getting - it might be related to something else. Invalid argument means that one of the parameters you're passing to sendto is somehow invalid. Since I see that you are properly initializing the socket descriptor, the problem might be that the send buffer is somehow not valid - possibly a null pointer or bad address. This means that the problem is not apparent from the code you posted, and is likely in some code that is calling IPCSend.
I'm trying to understand what the getaddrinfo function returns :
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
int main (int argc, char *argv[])
{
struct addrinfo *res = 0 ;
getaddrinfo("localhost", NULL ,NULL,&res);
printf("ai_flags -> %i\n", res->ai_flags) ;
printf("ai_family -> %i\n", res->ai_family) ;
printf("ai_socktype -> %i\n", res->ai_socktype) ;
printf("ai_protocol -> %i\n", res->ai_protocol) ;
printf("ai_addrlen -> %i\n", res->ai_addrlen) ;
struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
printf("ai_addr hostname -> %s\n", inet_ntoa(saddr->sin_addr));
freeaddrinfo(res);
return 0 ;
}
results :
ai_flags -> 40
ai_family -> 2
ai_socktype -> 1
ai_protocol -> 6
ai_addrlen -> 16
ai_addr hostname -> 127.0.0.1
In /etc/hosts, I 've got :
127.0.0.1 localhost
::1 localhost
Getaddrinfo returns only 127.0.0.1 and not ::1 ? I don't understand why ?
The second question is where can I find the meaning of those ints (40,2,1,6 etc) ? I've read the man but there is nothing about that.
I also wanted to know if it's possible to give a IPv6 adress (for example ::1) and the function returns the name : localhost ?
Thanks a lot !!
#jwodder and #onteria_ covered the IPv6 portion well, so I'll just tackle the numbers portion:
ai_flags -> 40
Probably this is going to be the sum of the following two in /usr/include/netdb.h:
# define AI_V4MAPPED 0x0008 /* IPv4 mapped addresses are acceptable. */
# define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose
This is the protocol family, inet, inet6, apx, unix, etc.:
ai_family -> 2
bits/socket.h:78:#define PF_INET 2 /* IP protocol family. */
bits/socket.h:119:#define AF_INET PF_INET
This is the socket type, stream, dgram, packet, rdm, seqpacket:
ai_socktype -> 1
bits/socket.h:42: SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
The higher-level protocol, TCP, UDP, TCP6, UDP6, UDPlite, ospf, icmp, etc:
ai_protocol -> 6
Funny enough, in /etc/protocols:
tcp 6 TCP # transmission control protocol
The size of the struct sockaddr. (Differs based on the address family! Ugh.)
ai_addrlen -> 16
This is because you're getting back a struct sockaddr_in, see linux/in.h:
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
And the last one, from /etc/hosts :)
ai_addr hostname -> 127.0.0.1
res also contains a field struct addrinfo *ai_next;, which is a pointer to additional entries found by getaddrinfo, or NULL if there were no other entries. If you examine res->ai_next, you should find the IPv6 entry.
As for the integer fields in a struct addrinfo, they correspond to predefined constants with implementation-defined values, and the integer values themselves are not of general interest. If you want to know what a given field means, compare it against the constants that can be assigned to that field (SOCK_STREAM, SOCK_DGRAM, etc. for ai_socktype; IPPROTO_TCP, IPPROTO_UDP, etc. for ai_protocol; and so forth) or, for ai_flags, test each bit corresponding to a predefined constant (e.g., if (res->ai_flags & AI_NUMERICHOST) {printf("ai_flags has AI_NUMERICHOST\n"); }).
extern struct sockaddr_in6 create_socket6(int port, const char * address) {
struct addrinfo hints, *res, *resalloc;
struct sockaddr_in6 input_socket6;
int errcode;
/* 0 out our structs to be on the safe side */
memset (&hints, 0, sizeof (hints));
memset (&input_socket6, 0, sizeof(struct sockaddr_in6));
/* We only care about IPV6 results */
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
errcode = getaddrinfo (address, NULL, &hints, &res);
if (errcode != 0)
{
perror ("[ERROR] getaddrinfo ");
return input_socket6;
}
resalloc = res;
while (res)
{
/* Check to make sure we have a valid AF_INET6 address */
if(res->ai_family == AF_INET6) {
/* Use memcpy since we're going to free the res variable later */
memcpy (&input_socket6, res->ai_addr, res->ai_addrlen);
/* Here we convert the port to network byte order */
input_socket6.sin6_port = htons (port);
input_socket6.sin6_family = AF_INET6;
break;
}
res = res->ai_next;
}
freeaddrinfo(resalloc);
return input_socket6;
}
Here is some code that explains it. Basically unless you give getaddrinfo some hints to tell it to only work with IPV6, it will also give IPV4 results. That is why you have to loop through the results as shown.
Other answers have been given to most parts, but to answer this final part:
I also wanted to know if it's possible to give a IPv6 adress (for example ::1) and the function returns the name : localhost ?
The function you want there is getnameinfo(); given a socket address it returns a string name.
An often overlooked function that requires no external library, but basically has no documentation whatsoever.
UPDATE (2010-10-11): The linux man-pages now have documentation of the getaddrinfo_a, you can find it here: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html
As a disclaimer I should add that I'm quite new to C but not exactly a newbie, so there might be bugs, or bad coding practices, please do correct me (and my grammar sucks too).
I personally didn't know about it until I came upon this post by Adam Langley, I shall give a few code snippets to illustrate the usage of it and clarify some things that might not be that clear on first use. The benefits of using this is that you get back data readily usable in socket(), listen() and other functions, and if done right you won't have to worry about ipv4/v6 either.
So to start off with the basics, as taken from the link above (you will need to link against libanl (-lanl)) :
Here is the function prototype:
int getaddrinfo_a(int mode, struct gaicb *list[], int ent,
struct sigevent *);
The mode is either GAI_WAIT (which is probably not what you want) and GAI_NOWAIT for async lookups
The gaicb argument accepts an array of hosts to lookup with the ent argument specifying how many elements the array has
The sigevent will be responsible for telling the function how we are to be notified, more on this in a moment
A gaicb struct looks like this:
struct gaicb {
const char *ar_name;
const char *ar_service;
const struct addrinfo *ar_request;
struct addrinfo *ar_result;
};
If you're familiar with getaddrinfo, then these fields correspond to them like so:
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
The node is the ar_name field, service is the port, the hints argument corresponds to the ar_request member and the result is stored in the rest.
Now you specify how you want to be notified through the sigevent structure:
struct sigevent {
sigval_t sigev_value;
int sigev_signo;
int sigev_notify;
void (*sigev_notify_function) (sigval_t);
pthread_addr_t *sigev_notify_attributes;
};
You can ignore the notification via setting _sigev_notify_ to SIGEV_NONE
You can trigger a signal via setting sigev_notify to SIGEV_SIGNAL and sigev_signo to the desired signal. Note that when using a real-time signal (SIGRTMIN-SIGRTMAX, always use it via the macros and addition SIGRTMIN+2 etc.) you can pass along a pointer or value in the sigev_value.sival_ptr or sigev_value.sival_int member respectivley
You can request a callback in a new thread via setting sigev_notify to SIGEV_NONE
So basically if you want to look up a hostname you set ar_name to the host and set everything else to NULL, if you want to connect to a host you set ar_name and ar_service , and if you want to create a server you specify ar_service and the ar_result field. You can of course customize the ar_request member to your hearts content, look at man getaddrinfo for more info.
If you have an event loop with select/poll/epoll/kqueue you might want to use signalfd for convenience. Signalfd creates a file descriptor on which you can use the usuall event polling mechanisms like so:
#define _GNU_SOURCE //yes this will not be so standardish
#include <netdb.h>
#include <signal.h>
#include <sys/signalfd.h>
void signalfd_setup(void) {
int sfd;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &mask, NULL); //we block the signal
sfd = signalfd(-1, &mask, 0);
//add it to the event queue
}
void signalfd_read(int fd) {
ssize_t s;
struct signalfd_siginfo fdsi;
struct gaicb *host;
while((s = read(fd, &fdsi, sizeof(struct signalfd_siginfo))) > 0){
if (s != sizeof(struct signalfd_siginfo)) return; //thats bad
host = fdsi.ssi_ptr; //the pointer passed to the sigevent structure
//the result is in the host->ar_result member
create_server(host);
}
}
void create_server(struct gaicb *host) {
struct addrinfo *rp, *result;
int fd;
result = host->ar_result;
for(rp = result; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
bind(fd, rp->ai_addr, rp->ai_addrlen);
listen(fd, SOMAXCONN);
//error checks are missing!
freeaddrinfo(host->ar_request);
freeaddrinfo(result);
//you should free everything you put into the gaicb
}
}
int main(int argc, char *argv[]) {
struct gaicb *host;
struct addrinfo *hints;
struct sigevent sig;
host = calloc(1, sizeof(struct gaicb));
hints = calloc(1, sizeof(struct addrinfo));
hints->ai_family = AF_UNSPEC; //we dont care if its v4 or v6
hints->ai_socktype = SOCK_STREAM;
hints->ai_flags = AI_PASSIVE;
//every other field is NULL-d by calloc
host->ar_service = "8888"; //the port we will listen on
host->ar_request = hints;
sig.sigev_notify = SIGEV_SIGNAL;
sig.sigev_value.sival_ptr = host;
sig.sigev_signo = SIGRTMIN;
getaddrinfo_a(GAI_NOWAIT, &host, 1, &sig);
signalfd_setup();
//start your event loop
return 0;
}
You can of course use a simple signal handler for this job too, look at man sigaction for more info.