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.
Related
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.
In prep for my first time coding UDP, I'm trying out some example client and server code copied and lightly modified from here. Everything seems to be working except that the value returned by recvfrom() is always the size of the buffer instead of the number of bytes read (if I change my buffer size and recompile, the reported bytes received changes to match the new buffer size although the bytes sent are the same 10 bytes in every test).
Does anyone see any error(s) in this code that would explain the problem (some error-checking removed here for conciseness)? In case it's relevant, I'm compiling and running in bash in a Terminal window on a Macbook Pro running Yosemite 10.10.5:
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFLEN 1024
#define PORT 9930
int main(void) {
struct sockaddr_in si_me, si_other;
int s, i, slen=sizeof(si_other);
int nrecv;
char buf[BUFLEN];
s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
bind(s, &si_me, sizeof(si_me));
while (1) {
nrecv = recvfrom(s, buf, BUFLEN, 0, &si_other, &slen);
printf("Received packet from %s:%d\n%d bytes rec'd\n\n",
inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), nrecv);
}
}
recvfrom truncates datagrams to the size of your buffer when the buffer is not large enough.
The fact that recvfrom returns the buffer size implies that your buffer size is not big enough, try increasing it to, say, 65535 bytes - the maximum theoretical UDP datagram size.
There is very useful funcion call getifaddrs which retrieves all machin network addresses. The problem is that I'm using old glibc version which doesn't have this function. Is there any replacement for it? I was looking and found getipnodebyname but it is unuseful when address isn't mapped in /etc/hosts file.
To add to the previous answer, here is an example for the SIOCGIFCONF-approach. You have to do something like this:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
int fd;
int get_iface_list(struct ifconf *ifconf)
{
int rval;
if((rval = ioctl(fd, SIOCGIFCONF , (char*) ifconf )) < 0 )
perror("ioctl(SIOGIFCONF)");
return rval;
}
int main()
{
static struct ifreq ifreqs[100];
static struct ifconf ifc;
char *ptr;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
return 1;
ifc.ifc_buf = (char*) (ifreqs);
ifc.ifc_len = sizeof(ifreqs);
if(get_iface_list(&ifc) < 0) return -1;
/* Go through the list of interfaces */
for (ptr = ifc.ifc_buf; ptr < ifc.ifc_buf + ifc.ifc_len;)
{
struct ifreq *ifr = (struct ifreq*)ptr;
int len = (sizeof(struct sockaddr) > ifr->ifr_addr.sa_len) ?
sizeof(struct sockaddr) : ifr->ifr_addr.sa_len;
ptr += sizeof(ifr->ifr_name) + len;
/* Do what you need with the ifr-structure.
* ifr->ifr_addr contains either sockaddr_dl,
* sockaddr_in or sockaddr_in6 depending on
* what addresses and L2 protocols the interface
* has associated in it.
*/
}
close(fd);
return 0;
}
There are some gotchas, of course. According to Unix Network Programming chapter 17.6 ioctl(fd, SIOCGIFCONF, array) may not return an error on some platforms if the array pointed in the argument is too small. The data will then be concatenated. Only way to work around this is to call ioctl() in a loop until you get same result length twice while increasing the size of the array. Of course, since this is 2012, I'm not sure how relevant this is anymore.
Size of ifreqs array is purely a guess in this case. Keep in mind though that the array will contain one struct ifreq for every L2 and L3 address associated with a interface. For example, assuming you have also IPv6 addresses, for lo-interface you'd get three entries: ethernet, IPv4 and IPv6. Therefore reserve enough space or apply the kludge.
To get broadcast addresses and other additional information, you will need to additional ioctl() calls in the loop. All possible options depends on what your OS provides, of course.
For more information I'd recommend reading Unix Network Programming by W. Richard Stevens. It is the most comprehesive book about this subject.
The traditional way to do the equivalent was with the SIOCGIFCONF operation to ioctl. Any socket can be used for the operation. It's not as easy as a single function call though.
is there a way to get the IP number from gethostname()?
We are randomly generating IP addresses for the computers in the lab we are in. We use gethostbyname(<random ip>) to get the IP of a computer.
What we want to do essentially is compare the ip that we get from gethostbyname() with what we get from gethostname().
We tried:
struct hostent* host;
char temp[MAX_LEN];
gethostname(temp, MAX_LEN);
host = gethostbyname(<random ip address>)
if(host->h_name == temp) printf("They are the same\n");
The problem is, is that host->h_name is '172.125.45.1' (i made that
up) and temp is 'u-my_comp'
so we cant compare the strings cause one gives us the name of the computer (u-my_comp), and the other gives the ip...
Is there anyway to make these functions return the same type of value?
we have tried doing something like
gethostname(temp, 24)
temp_host = gethostbyname(temp)
in hopes that now we could compare temp_host->h_name with host->h_name...but yeah, that didnt work either.
Any ideas?
thanks!
gethostbyname() is for converting a hostname into a socket address. If the "hostname" you supply is a dotted-quad IPv4 address, that will be all you get in the h_name parameter of the result.
To convert a socket address back into a name what you want is the companion function gethostbyaddr() - except that you don't, because both gethostbyname() and gethostbyaddr() are deprecated. Instead, you should be using getaddrinfo() and getnameinfo().
For example:
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in sa;
char host[1024];
int gni_err;
sa.sin_family = AF_INET;
sa.sin_port = 0;
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
gni_err = getnameinfo((struct sockaddr *)&sa, sizeof sa, host, sizeof host, NULL, 0, NI_NAMEREQD | NI_NOFQDN);
if (gni_err == 0) {
printf("host is: %s\n", host);
} else {
fprintf(stderr, "Error looking up host: %s\n", gai_strerror(gni_err));
}
return 0;
}
If you call:
myhost = gethostbyname(temp);
(having allocated myhost) then you will have two hostent structures you will compare - you will have the IP address lists for both the target query host and the current host (not just the hostname for the current host).
Working on creating a server/client system in C right now, and I'm having a little trouble with the client part. From what I've seen, I need to use sockaddr_in so I can connect to the server. However, I've been getting a segfault every time. I believe that sockaddr_in has something to do with it, as commenting it and it's references later in the program fixes the segfault.
code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
int main(int argc, char** argv)
{
int Csock;
int con;
char *data = 0;
char buf[101] = "";
struct sockaddr_in addr;
Csock = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(3435);
con = connect(Csock, (struct sockaddr*) &addr, sizeof(addr));
write(con, "Text", sizeof("Text"));
*data = read(con, buf, 100);
puts(data);
return 0;
}
sadly, I am rather new to C, so that's as much as I can figure... can anyone tell me a way to go about eliminating the segfault?
Thanks!
Quick comment:
data is a pointer to char which does not point to an allocated memory, so:
*data = read(con, buf, 100);
is invalid! You cannot dereference a NULL pointer.
Also, read returns ssize_t, and not a char, so perhaps:
ssize_t nread = read(con, buf, 100);
and then print out the nread with printf.
One immediately-apparent thing that's wrong is taking sizeof &addr when you mean sizeof addr. Also you never set the address you want to connect to, only the port. On most systems neither of these errors would cause a crash, but they will keep the program from working.
Also it's advisable never to setup sockaddr structures directly, but instead use getaddrinfo.
The problem I think, lies with your connect statement. You need
con = connect(Csock, (struct sockaddr*) &addr, sizeof(addr));
sizeof() returns the size of the object. I don't offhand know what the size of the addr structure is, but the statement sizeof(&addr) will return 4 (assuming 32 bit system) and I'm quite sure that the size of the addr structure is > 4 bytes.
The & is the reference operator (or address of) and gives you the address of a particular structure. Address (in 32 bit systems) are 4 bytes. Usually the types of functions (like the connect function) want the actual size of the structure. This is often done for backwards compatibility purposes so that if the size of the structure changes in some future version of the SDK or library, older code doesn't need to change in order to work with the newer libraries.