C socket programming: Invalid argument error on connect() - c

I'm writing a client side as part of a TCP client server program.
My code reaches the connect part and throws an Invalid argument error, I have gone through the code several times and I couldn't find the problem.
The code receives 3 arguments, first one is an IP address or a hostname, second one is port and the third is the maximum length of the message to be sent.
My code uses getaddrinfo in order to convert the ip address or hostname, creates the needed variables, starts a connection, read from file, send data and receive data.
I run the code with:
gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c
./pcc_client 127.0.0.1 2233 4
The output is:
sockaddr_in initialized
Error starting connection : Invalid argument
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
//Get command line arguments
unsigned int port = atoi(argv[2]);
int length = atoi(argv[3]); //Number of bytes to read
char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in *serv_addr;
int rv;
char ip[100];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
serv_addr = (struct sockaddr_in *) p->ai_addr;
strcpy(ip, inet_ntoa(serv_addr->sin_addr));
}
// inet_aton(ip, &h.sin_addr);
freeaddrinfo(servinfo);
//Initialize socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) //Error creating socket
{
perror("Error creating socket \n");
exit(1);
}
printf("socket created\n");
//Initialize sockaddr_in structure
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
//Initialize connection
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { //Error connecting
perror("Error starting connection \n");
exit(1);
}
printf("connect succesful\n");
exit(0);
}

You are using serv_addr all wrong.
You have declared serv_addr as a sockaddr_in* pointer. After getaddrinfo() exits successfully, you are looping through the output list, assigning serv_addr to point at every ai_addr in the list, and then you free the list, leaving serv_addr pointing at invalid memory. You then trash memory when you try to populate serv_addr with data. And then you end up not even passing a valid pointer to a sockaddr_in to connect() at all, you are actually passing a pointer to a pointer to a sockaddr_in, which is why it complains about an "invalid argument".
In fact, you are going about this situation all wrong in general. When using getaddrinfo(), since it returns a linked list of potentially multiple socket addresses, you need to loop through the list attempting to connect() to every address until one of them is successful. This is especially important if you ever want to upgrade the code to support both IPv4 and IPv6 (by setting hints.ai_family = AF_UNSPEC;).
Try something more like this instead:
int main(int argc, char *argv[])
{
if (argc != 4)
{
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
struct addrinfo hints, *servinfo, *p;
int sockfd = -1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // or AF_UNSPEC
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0)
{
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0) continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0) break;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(servinfo);
if (sockfd < 0) //Error creating/connecting socket
{
perror("Error creating/connecting socket \n");
exit(1);
}
printf("connect successful\n");
...
close(sockfd);
exit(0);
}

You define serv_addr
struct sockaddr_in *serv_addr;
Then you use it
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
But nowhere in between those two places in the code do you initialize the pointer! That means serv_addr is uninitialized and its value is indeterminate and will point to some seemingly random location. Dereferencing the pointer will lead to undefined behavior.
The simple and natural and de facto standard solution is to make serv_addr not a pointer, but a structure object:
struct sockaddr_in serv_addr;
Then when you need a pointer you use the address-of operator &.
The issue above is further complicated by you actually using the & operator when calling connect. With serv_addr being a pointer, then &serv_addr is a pointer to the pointer. It will be of type struct sockaddr_in **. It is this issue, with the pointer to the pointer, that leads to the error message, since the pointer you send in is not a pointer to a sockaddr_in structure object.
By using a structure object as shown above will solve this problem as well.

Related

inet_ntop always returns the same IP

Spending way too much time trying to figure out why inet_ntop is always returning the same IP address of 2.0.19.86 inside of my barebones C UDP socket program.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SERVERPORT "4950" // the port users will be connecting to
int main(int argc, char *argv[])
{
int sock;
struct addrinfo addr_type, *server_info, *p;
int err;
int numbytes;
if (argc != 3) {
fprintf(stderr,"usage: talker hostname message\n");
exit(1);
}
//Specify type of response we want to git
memset(&addr_type, 0, sizeof addr_type);
addr_type.ai_family = AF_INET; // set to AF_INET to use IPv4
addr_type.ai_socktype = SOCK_DGRAM;
//Get the address info (like IP address) and store in server_info struct
if ((err = getaddrinfo(argv[1], SERVERPORT, &addr_type, &server_info)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
return 1;
}
// There might be multiple IP addresses...loop through and use the first one that works
for(p = server_info; p != NULL; p = p->ai_next) {
if ((sock = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("Error when creating socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "Client failed to create socket\n");
return 2;
}
char s[INET_ADDRSTRLEN];
inet_ntop(AF_INET,(struct sockaddr_in *)p->ai_addr,s, sizeof s);
printf("sending to %s....\n",s);
if ((numbytes = sendto(sock, argv[2], strlen(argv[2]), 0,
p->ai_addr, p->ai_addrlen)) == -1) {
perror("Error sending message");
exit(1);
}
printf("client sent %d bytes to %s\n", numbytes, argv[1]);
freeaddrinfo(server_info);
close(sock);
return 0;
}
The lines I am particularly stuck on is:
char s[INET_ADDRSTRLEN];
inet_ntop(AF_INET,(struct sockaddr_in *)p->ai_addr,s, sizeof s);
printf("sending to %s....\n",s);
For example I run the program with ./client www.google.com hello and get the following:
sending to 2.0.19.86....
client sent 5 bytes to www.google.com
I run the program again with ./client localhost hello and inet_ntop still returns the same IP.
sending to 2.0.19.86....
client sent 5 bytes to localhost
No errors are being thrown when I am creating the socket, and the message sends successfully when I send it to the receiving program over localhost, why is inet_ntop still outputting this weird address?
In your call to inet_ntop:
inet_ntop(AF_INET,(struct sockaddr_in *)p->ai_addr,s, sizeof s);
You're not passing in the correct structure. When AF_INET is passed as the first argument, the second argument should have type struct in_addr *, not struct sockaddr_in *.
You need to call out the sin_addr member which is of this type.
inet_ntop(AF_INET, &((struct sockaddr_in *)p->ai_addr)->sin_addr, s, sizeof s);

Cannot connect to IPv6 address if its not ::1 in C

I'd like to connect to a ssh server which has an ipv6 address with libssh2.
It works but when I give an ip that isn't the localhost it fails to connect.
The ip is correct because I can connect to it with ssh <ipv6> -p 22.
const char *ip = "::1";
struct sockaddr_storage storage;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &storage;
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(22);
int sock;
if(inet_pton(AF_INET6, ip, &addr6->sin6_addr) == 1)
{
if((sock = socket(AF_INET6, SOCK_STREAM, 0)) != -1)
{
if(connect(sock, (struct sockaddr *)(&storage),
sizeof(struct sockaddr_in6)) == 0)
{
printf("works\n");
}
close(sock);
}
}
Edit:
The suggestion (memset(&storage, 0, sizeof(storage))) by #idz seems to have resolved the problem.
Non-static structures in C are not zero-initialized, so to avoid the possibility of garbage in the memory causing errors, you should zero them out.
Adding:
memset(&storage, 0, sizeof(storage));
just after the storage declaration will do the trick.
While not directly related to the OP's error, it's always easier to find out what's going wrong if you do not throw away the error information available to you.
Exactly how you do this will depend on the environment you're coding in, but for a simple command line program you might do something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// https://stackoverflow.com/questions/70157711/cannot-connect-to-ipv6-address-if-its-not-1-in-c
int main(int argc, const char * argv[]) {
const char *ip = "::1";
struct sockaddr_storage storage;
memset(&storage, 0, sizeof(storage));
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &storage;
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(22);
int result = inet_pton(AF_INET6, ip, &addr6->sin6_addr);
if (result != 1) {
perror("inet_pton");
exit(EXIT_FAILURE);
}
int sock = socket(AF_INET6, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
result = connect(sock, (struct sockaddr *)(&storage), sizeof(struct sockaddr_in6));
if (result != 0) {
perror("connect");
exit(EXIT_FAILURE);
}
printf("It worked...\n");
close(sock);
return EXIT_SUCCESS;
}
In the case of garbage in the address this would report:
connect: Invalid argument
whereas a network routing issue would result in:
connect: No route to host
This makes it much easier to figure out what is going on!

sockaddr value changes unexpectedly after calling getaddrinfo()

I am programming an UDP client. I want to bind the socket to a given port on the client machine, so that the same port is always used for all sends. I get the sockaddr for the server using getaddrinfo, and I do the same to get the sockaddr which I pass to the call to getaddrinfo. However, after the second call to getaddrinfo the address of the server machine changes, and I end up sending the packet from the client machine to the client machine itself.
The following code is a standalone example that reproduces the error:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_HOST "www.google.com"
#define UDP_PORT "4000"
static struct sockaddr_in *destination_addr = NULL;
static int client_port;
int main(){
uint8_t bytes[5] = { 0xaa, 0xab, 0xac, 0xad, 0xaf}; //some data to send
uint16_t length = 5;
int status;
//initialize socket and bind
if (destination_addr == NULL) {
struct addrinfo hints;
struct addrinfo *servinfo, *p;
srand(time(NULL));
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((status = getaddrinfo(SERVER_HOST, UDP_PORT, &hints, &servinfo)) != 0) {
printf("Unable to send UDP. Reason: %s", gai_strerror(status));
return 0;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
if (p->ai_addr != NULL)
destination_addr = (struct sockaddr_in *) p->ai_addr;
}
client_port = 1027 + rand()%50000;
freeaddrinfo(servinfo);
printf("Created destination_addr with IP %s\n", inet_ntoa(destination_addr->sin_addr));
}
int send_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (send_socket_fd == -1) {
printf("Unable to create UDP socket. Reason: %s", strerror(errno));
return 0;
}
printf("IP after socket creation is %s\n", inet_ntoa(destination_addr->sin_addr));
int yes = 1;
if (setsockopt(send_socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1) {
perror("setsockopt");
return 0;
}
printf("IP after sockopt is %s\n", inet_ntoa(destination_addr->sin_addr));
// bind to local address
char str_client_port[6];
snprintf(str_client_port, 5, "%d", client_port);
struct addrinfo *source_addr_info;
struct addrinfo hints;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
// ***** destination_addr changes after this call *****
getaddrinfo (NULL, str_client_port, &hints, &source_addr_info);
printf("IP after getaddrinfo is %s\n", inet_ntoa(destination_addr->sin_addr));
bind(send_socket_fd, source_addr_info->ai_addr, source_addr_info->ai_addrlen);
printf("IP after binding is %s\n", inet_ntoa(destination_addr->sin_addr));
// send
int bytes_sent = sendto(send_socket_fd, bytes, length, 0, (struct sockaddr *)destination_addr, sizeof *destination_addr);
printf("Sent to IP %s\n", inet_ntoa(destination_addr->sin_addr));
if (bytes_sent != length){
if (bytes_sent == -1){
printf("UDP send failed. Reason: %s", strerror(errno));
}
else {
printf("UDP: not all bytes could be sent.");
}
}
close(send_socket_fd);
return 1;
}
The output generated by the execution of this program in my machine is:
Created destination_addr with IP 64.233.167.105
IP after socket creation is 64.233.167.105
IP after sockopt is 64.233.167.105
IP after getaddrinfo is 0.0.0.0
IP after binding is 0.0.0.0
Sent to IP 0.0.0.0
I am rather new to socket programming in C, and pretty sure I am doing some silly mistake, but after googling a lot and trying many things, I am still stuck with this. Any idea?
Solved. As #molbdnilo pointed out, the error was caused by the call to freeaddrinfo. To fix it I now copy the value pointed by p->ai_addr, so that it is not lost when freeing. I substituted:
if (p->ai_addr != NULL)
destination_addr = (struct sockaddr_in *) p->ai_addr;
with
if (p->ai_addr != NULL){
destination_addr = malloc(sizeof *destination_addr);
memcpy(destination_addr, (struct sockaddr_in *)p->ai_addr, sizeof *p->ai_addr);
}
and it worked.

Why set the whole `hints` variable to 0?

I am reading Beej's guide to network programming and in chapter 5.1, in the showip.c program I see the following line of code:
memset(&hints, 0, sizeof hints);
After a discussion on the ##c channel on freenode I deducted that the reasoning of that memset call could be to set the value of hints.ai_flags to 0(note that the program works fine I remove that line and I explicitly initialize hints.ai_flags to 0). If this is true, why does he need to set the whole struct to 0?
This is the full source:
/*
** showip.c -- show IP addresses for a host given on the command line
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: showip hostname\n");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 2;
}
printf("IP addresses for %s:\n\n", argv[1]);
for(p = res;p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
freeaddrinfo(res); // free the linked list
return 0;
}
It's required by getaddrinfo() function documentation (where you pass your hints variable as parameter). From man getaddrinfo:
All the other fields in the structure pointed to by hints must contain either 0 or a NULL pointer, as appropriate.
It's because you are only going to fill/use/initialize some of the fields of the struct, giving 0 to the other fields prevents reading an uninitialzied variable, and sometimes 0 is the default value for those variables.
note that the program works fine I remove that line and I explicitly initialize hints.ai_flags to 0
Not necessarily, if you are on linux, I suggest using valgrind to detect reads to uninitialized variables, since that causes undefined behavior, the behavior could be that nothing wierd happens.

Sockets sendto() returning EINVAL

I'm trying to send a UDP packet in C. I have the following sendto():
char* msg = "Hello";
//ret is the return value of getaddrinfo, the address is AF_INET (IPv4)
//and the sock_type is SOCK_DGRAM (UDP)
struct sockaddr_in *ip = (struct sockaddr_in *)ret->ai_addr;
if ((sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)ip,
sizeof(struct sockaddr *))) != -1) {
printf("msg sent successfully");
} else {
printf("Error sending msg: %s\n", strerror(errno));
}
However, it's returning an error saying there's an invalid argument. Looking at the manpage I can't really tell which one is the invalid argument. Any ideas?
EDIT: Here's all my code
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
/*
* Help the technically challenged among us who have no idea
* what on God's green Earth they are doing with this thing.
*/
if (argc != 2) {
printf("usage: routetracer <ip address or hostname>\n");
return -1;
}
/*
* hints- parameters for return value of getaddrinfo
* ret- return value of getaddrinfo
*/
struct addrinfo hints, *ret;
int status;
char ipv4[INET_ADDRSTRLEN];
int ttl = 0;
char* msg = "Hello";
int last_hop = 0;
//define what we want from getaddrinfo
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; //IPv4
hints.ai_socktype = SOCK_DGRAM; //UDP packets
//call getaddrinfo to fill ret, w/ error chk
if ((status = getaddrinfo(argv[1], NULL, &hints, &ret)) != 0) {
printf("getaddrinfo: %s\n", gai_strerror(status));
return -1;
}
//extract IPv4 address from ret
struct sockaddr_in* ip = (struct sockaddr_in *)ret->ai_addr;
//convert address from pure numbers to something easier to read
inet_ntop(ret->ai_family, &(ip->sin_addr), ipv4, INET_ADDRSTRLEN);
//kindly inform the user of which hostname they are connecting to
printf("Route for: %s\n", ipv4);
//create a socket
int sock = socket(ret->ai_family, ret->ai_socktype, ret->ai_protocol);
ttl = 1;
if ((setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))) != -1) {
printf("TTL set successfully\n");
} else {
printf("Error setting TTL: %s\n", strerror(errno));
}
if ((sendto(sock, msg, strlen(msg), 0, ret->ai_addr,
ret->ai_addrlen)) != -1) {
printf("msg sent successfully");
} else {
printf("Error sending msg: %s\n", strerror(errno));
}
return 0;
}
Running the program gives the following output:
$ ./routetracer www.google.com
Route for: 173.194.46.82
TTL set successfully
Error sending msg: Invalid argument
Try:
if ((sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)ip,
sizeof(struct sockaddr_in))) != -1) {
You're giving it the size of a pointer, not the size of the structure. And it needs to be the specific structure type, not the generic type.
As Barmar points out, one reason for the EINVAL is the incorrect:
sizeof(struct sockaddr *)
which gives the size of a pointer. See Socket programming: sendto always fails with errno 22 (EINVAL).
The second reason seems to be sin_port, which getaddrinfo returns as 0. Changing it to 80 say clears up the EINVAL, as in:
((struct sockaddr_in *)ret->ai_addr)->sin_port = htons(80); // test
Here port 80 here is not HTTP, but instead (for UDP) is Google's experimental QUIC Chromium.
Wikipedia http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers states that Port 0 is for UDP reserved, and for TCP is unofficially reserved as a "programming technique for specifying system-allocated (dynamic) ports".
And as an aside (and referring to the original question), you may not need bother with the variable ip. You are casting ret->ai_addr to struct sockaddr_in *, and then back again to its original type.
And, as Remy Lebeau points out, it is better to use the service parameter of getaddrinfo. So putting this all together, your code could look more like:
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; //IPv4
hints.ai_socktype = SOCK_DGRAM; //UDP packets
if ((status = getaddrinfo(argv[1], "80", &hints, &ret)) != 0) {
printf("getaddrinfo: %s\n", gai_strerror(status));
return -1;
}
assert(ret->ai_family == AF_INET); // guaranteed
assert(ret->ai_socktype == SOCK_DGRAM); // guaranteed
assert(((struct sockaddr_in *)ret->ai_addr)->sin_port == htons(80)); // guaranteed
// ...
if ((sendto(sock, msg, strlen(msg), 0, ret->ai_addr, ret->ai_addrlen)) != -1) {
// ...

Resources