sockaddr value changes unexpectedly after calling getaddrinfo() - c

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.

Related

getaddrinfo linux manpage server example but using TCP instead of UDP

I'm trying to convert the server program example from getaddrinfo's linux manpage from UDP to TCP.
When running the program without any change first, I can communicate with a netcat client using UDP :
gcc main.c
./a.out 1234
netcat -u 127.0.0.1 1234
// this works here, I can send messages to the server
Then if I try with TCP :
netcat 127.0.0.1 1234
^ this doesn´t connect and exists directly
I tried to change some attributes in the addrinfo struct :
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* Stream socket */
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
And it doesn´t connect either.
Here is the whole code :
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#define BUF_SIZE 500
int
main(int argc, char *argv[])
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int sfd, s;
struct sockaddr_storage peer_addr;
socklen_t peer_addr_len;
ssize_t nread;
char buf[BUF_SIZE];
if (argc != 2) {
fprintf(stderr, "Usage: %s port\n", argv[0]);
exit(EXIT_FAILURE);
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* Stream socket */
hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */
hints.ai_protocol = 0; /* Any protocol */
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
s = getaddrinfo(NULL, argv[1], &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
close(sfd);
}
freeaddrinfo(result); /* No longer needed */
if (rp == NULL) { /* No address succeeded */
fprintf(stderr, "Could not bind\n");
exit(EXIT_FAILURE);
}
/* Read datagrams and echo them back to sender */
for (;;) {
peer_addr_len = sizeof(peer_addr);
nread = recvfrom(sfd, buf, BUF_SIZE, 0,
(struct sockaddr *) &peer_addr, &peer_addr_len);
if (nread == -1)
continue; /* Ignore failed request */
char host[NI_MAXHOST], service[NI_MAXSERV];
s = getnameinfo((struct sockaddr *) &peer_addr,
peer_addr_len, host, NI_MAXHOST,
service, NI_MAXSERV, NI_NUMERICSERV);
if (s == 0)
printf("Received %zd bytes from %s:%s\n",
nread, host, service);
else
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(s));
if (sendto(sfd, buf, nread, 0,
(struct sockaddr *) &peer_addr,
peer_addr_len) != nread)
fprintf(stderr, "Error sending response\n");
}
}
How to make this server program accept TCP connections with netcat ?

The UDP sendto() and recvfrom() functions are not working as expected

I wanted to implement a simple UDP server and client, so I wrote the code halfway in C language.
The purpose of the code is to send a message from the Client to the Server, and if the message is sent correctly, the Server sends an "ACK" message to the Client.
However, the return values of sendto() and recvfrom() are -1, and the message is not sent.
In udpserver.c, I know that "ACK" is firmly overwritten in buf by strcpy.
In udpserver.c, I can send messages and in udpclient.c, I can receive messages, but I don't know how to write code from here. Can you give me some specific code?
Also, I want the server to say Hello, and the client to say ACK.
#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 MYPORT "4567" // the port that client will be connecting to
#define MAXBUFLEN 100
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
int32_t receivedNumber;
socklen_t addr_len;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM; // UDP
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "listener: failed to bind socket\n");
exit(1);
}
printf("server: waiting for client...\n");
addr_len = sizeof their_addr;
//Receive from client
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
printf("Received from client: %s\n", buf);
strcpy(buf,"ACK");
//Send client an ACK message
if(sendto(sockfd,buf,strlen(buf),0,p->ai_addr,p->ai_addrlen)==-1){
printf("Error\n");
}
/*
unknown code
*/
freeaddrinfo(servinfo);
close(sockfd);
return 0;
}
#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 "4567" // the port that client will be connecting to
#define MAXBUFLEN 100
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;
socklen_t addr_len;
int rv;
int numbytes;
char buf[MAXBUFLEN];
if (argc != 3) {
fprintf(stderr,"usage: talker hostname message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to create socket\n");
return 2;
}
// Send to server
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0, p->ai_addr, p->ai_addrlen)) == -1) {
perror("client: sendto");
exit(1);
}
//Receive from server
if(recvfrom(sockfd,buf,MAXBUFLEN-1,0,(struct sockaddr *)&their_addr,&p->ai_addrlen)==-1){
printf("Error\n");
}
/*
unknown code
*/
printf("Received from server: %s\n", buf);
freeaddrinfo(servinfo);
close(sockfd);
return 0;
}
gcc udpserver.c
./a.out
server: waiting for client...
Received from client: Hello~
gcc udpclient.c
./a.out 127.0.0.1 Hello
Received from server: �*��z ```
If you read from a channel into a buffer and the result is positive, then you have successfully read some bytes, but not a null-terminated string.
e.g.
numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1, ...);
numbytes is the number of bytes read, now it is time to make a string:
buf[numbytes] = '\0';
Both the client and server do not add the null-byte to the end of buf.

TCP server socket not listening by addrinfo method

So here is the code, code successfully compiles...
#include <stdio.h> // for printf
#include <linux/if_tun.h> //for IFF_TUN
#include <sys/socket.h> //socket, struct sockaddr_in
#include <fcntl.h> // for O_RDWR macros
#include <string.h> //for strcpy
#include <unistd.h> //for read();
#include <netdb.h> //for struct sockaddr
#include <net/if.h> //struct ifreq and IFNAMSIZ and other macros
#include <errno.h>
#include <stdlib.h>
// _check: error handler
static int _check(int retval, const char *msg)
{
if(retval == -1)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(EXIT_FAILURE);
}
return retval;
}
int tcp_listen_sock(int listen_connection)
{
/*-------------------------socket-----------------------*/
int sock, tcp_sock;
struct addrinfo hints, *result;
struct sockaddr *addrin;
memset(&hints ,0 , sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
const char *host;
host = "0.0.0.0";
_check(getaddrinfo(host, NULL, &hints, &result), "getaddrinfo");
if (result->ai_family == AF_INET)
((struct sockaddr_in *)result->ai_addr)->sin_port = htons(5678);
else if (result->ai_family == AF_INET6)
((struct sockaddr_in6 *)result->ai_addr)->sin6_port = htons(5678);
else {
fprintf(stderr, "unknown ai_family %d", result->ai_family);
freeaddrinfo(result);
return -1;
}
memcpy(addrin, result->ai_addr, result->ai_addrlen);
// *client_len = result->ai_addrlen;
_check((sock = socket(AF_INET, SOCK_STREAM, 0)), "socket");
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "enp0s3");
_check(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)), "setsockopt");
int flags;
if((flags = fcntl(sock, F_GETFL)) != -1)
{
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
}
else
perror("socket fcntl");
_check(bind(sock,result->ai_addr, result->ai_addrlen), "tcp bind");
int len = sizeof(struct sockaddr);
_check(listen(sock, listen_connection), "listen");
tcp_sock = accept(sock, result->ai_addr, &result->ai_addrlen );
printf("now listening on tcp!\n");
return tcp_sock;
}
int main(int argc, char **argv)
{
printf("Starting program\n");
int tcp = tcp_listen_sock(5);
printf("ending program\n");
return 0;
}
and ,
OUTPUT
Starting program
now listening on tcp!
ending program
but server socket is not actually listening...
expected output:
Starting program
now listening on tcp!
read...
write...
read...
write...
read...
write..
I can't figure our what I am missing, I know I didn't implemented read, write yet but I will do it after when server socket seems to working fine and listening properly.
NOTE: I am doing this in linux (specifically ubuntu)
Any help will be appreciated...
Calling getaddrinfo to initialize a local list socket seems like overkill.
Start with this. This is a simple "create a listen socket and wait for an incoming TCP connection" code sample.
int tcp_socket_listen(int listen_connection)
{
struct sockaddr_in addr = {0};
sockaddr_in addrRemote = {0};
socklen_t sizeRemote = 0;
int tcp_socket = -1;
s = socket(AF_INET, SOCK_STREAM, 0);
_check(s, "socket");
addr.sin_family = AF_INET;
addr.sin_port = htons(5678);
_check(bind(s, (sockaddr*)&addr, sizeof(addr)), "bind");
_check(listen(sock, listen_connection), "listen");
sizeRemote = sizeof(addrRemote);
tcp_sock = accept(s, (sockaddr*)&addrRemote, &sizeRemote);
_check(tcp_sock, "accept");
printf("now listening on TCP\n");
return tcp_sock;
}
Now if you want to bind to a specific adapter (e.g. "enp0s3") instead of the default ("all adapters") or need IPV6 support, you can peruse my sample code on github here for the GetSocketAddressForAdapter and use that address for the bind call instead of the default addr address above. It's C++, but you can probably port it to straight C with a little work.

Why am I getting a bad address error when trying to use sendto() on my UDP server?

I am quite new to socket programming. I am programming a UDP server to follow the Time protocol. I am confused since I have done a UDP server which uses the Daytime protocol and this was successful. I only copied the code of that one and just changed the message.
#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>
#include <time.h>
#define PORT "6000" // the port users will be connecting to
#define MAXBUFLEN 512
// get sockaddr, Ipv4 or Ipv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd;
struct addrinfo info, *udpServerInfo, *connection;
int rv;
int nrBytes;
const struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
socklen_t addr_len;
char s[INET6_ADDRSTRLEN];
time_t mytime;
mytime = time(NULL);
memset(&info, 0, sizeof info);
info.ai_family = AF_INET; // Ipv4 address
info.ai_socktype = SOCK_DGRAM;
info.ai_flags = AI_PASSIVE; // use my Ip
if ((rv = getaddrinfo(NULL, PORT, &info, &udpServerInfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(connection = udpServerInfo; connection != NULL; connection = connection->ai_next) {
if ((sockfd = socket(connection->ai_family, connection->ai_socktype,
connection->ai_protocol)) == -1) {
perror("listener: socket");
continue;
}
if (bind(sockfd, connection->ai_addr, connection->ai_addrlen) == -1) {
close(sockfd);
perror("listener: bind");
continue;
}
break;
}
if (connection == NULL) {
fprintf(stderr, "listener: failed to bind socket\n");
return 2;
}
// printf(asctime(&mytime));
freeaddrinfo(udpServerInfo);
printf("listener: waiting to recvfrom...\n");
addr_len = sizeof their_addr;
int done = 1;
while (done == 1 ) {
nrBytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
(struct sockaddr *)&their_addr, &addr_len);
printf("listener: got packet from %s\n",
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s));
printf("listener: packet is %d bytes long\n", nrBytes);
buf[nrBytes] = '\0';
printf("listener: packet contains \"%s\"\n", buf);
if(sendto(sockfd, mytime, sizeof(mytime), 0, (struct sockaddr *)&their_addr, addr_len) == -1) {
perror("send %s\n");
}
else {
printf ("Seconds %lu\n", mytime);
done = 0;
}
}
close(sockfd);
return 0;
}
The second parameter of sendto() requires a pointer. You should call it like:
sendto(sockfd, &mytime, sizeof(mytime))
sendto() expects a pointer to the data to be sent as 2nd parameter.
So this
if (sendto(sockfd, mytime, ...
ought to be
if (sendto(sockfd, &mytime, ...
As it stands, the value of mytime will be taken as address, which is mostly likely an invalid one, which in turn provokes undefined behaviour inside sendto() when being de-referenced.
The compiler should have warned you about this.

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