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.