I'm trying to learn network programming in C, I'm currently working on an echo server, a server which sends back any string it receives, I have the following code:
You can skip the entire code section to get to the actual problem
server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#define PORT "8080"
#define BUFFLEN 1024
char *strin(FILE *stream);
void printaddrs(struct addrinfo *res);
int main() {
struct addrinfo hints, *res;
// sockfd -> server file descriptor (serverfd)
int sockfd, clientfd;
char buffer[BUFFLEN];
memset(&hints, 0, sizeof(hints)); // Make sure 'hints' is empty
// Fill in the 'hints' addrinfo structure
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 (IPv4 or IPv6)
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_PASSIVE; // Use my address
printf("'hints' filled successfully!\n");
if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
printf("getaddrinfo(): Error!\n");
}
printf("getaddrinfo(): Success!\n");
// print all addresses returned by getaddrinfo()
printaddrs(res);
printf("Select an address to bind to: ");
// strin -> takes input from FILE *stream no matter how long it is!!!
char *id = strin(stdin);
if (id == NULL) {
printf("No input!\n");
freeaddrinfo(res);
free(id);
return 1;
}
struct sockaddr_in *ipv4;
struct sockaddr_in6 *ipv6;
char addrstr[INET6_ADDRSTRLEN];
struct addrinfo *p;
int count = 0, type, family;
char ipstr[INET6_ADDRSTRLEN], ipver[5];
for (; res != NULL; res = res->ai_next) {
if (atoi(id) == count) {
if (res->ai_family == AF_INET) { // IPv4
ipv4 = (struct sockaddr_in *) res->ai_addr;
inet_ntop(res->ai_family, ipv4, addrstr, sizeof(addrstr));
} else if (res->ai_family == AF_INET6) { // IPv6
ipv6 = (struct sockaddr_in6 *) res->ai_addr;
inet_ntop(res->ai_family, ipv6, addrstr, sizeof(addrstr));
}
printf("Chosen address: %s\n", addrstr);
break;
}
strcpy(addrstr, "");
count++;
}
free(id);
if (strcmp(addrstr, "") == 0) {
printf("Couldn't find entered id.\n");
freeaddrinfo(res);
return 1;
}
// Create the socket
sockfd = socket(res->ai_family, res->ai_socktype, 0);
if (sockfd == -1) {
printf("socket(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("socket(): Success!\n");
// Bind the socket to an IP address and a port
if (bind(sockfd, res->ai_addr, res->ai_addrlen) == -1) {
printf("bind(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("bind(): Success!\n");
if (listen(sockfd, 5) == -1) {
printf("listen(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("Socket listening on port %s...\n", PORT);
struct sockaddr *clientaddr;
char clientaddrstr[INET6_ADDRSTRLEN];
while (1) {
clientfd = accept(sockfd, clientaddr, (int *)sizeof(clientaddr));
if (clientfd == -1) {
printf("accept(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("accept(): Success!\n");
inet_ntop(clientaddr->sa_family, clientaddr, clientaddrstr, sizeof(clientaddrstr));
printf("Accepted a conenction to %s\n", clientaddrstr);
}
freeaddrinfo(res);
return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#define PORT "8080"
#define BUFFLEN 1024
/** TODO
* getaddrinfo() to get my address
*/
void printaddrs(struct addrinfo *res);
char *strin(FILE *stream);
int main() {
printf("Choose an address to connect to: ");
char *servaddr = strin(stdin);
// Initialize variables to be used later
struct addrinfo hints, *res;
int clientfd;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(servaddr, PORT, &hints, &res) != 0) {
printf("getaddrinfo(): Failure!\n");
free(servaddr);
freeaddrinfo(res);
return 1;
}
printf("getaddrinfo(): Success!\n");
clientfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (clientfd == -1) {
printf("socket(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("socket(): Success!\n");
if (connect(clientfd, res->ai_addr, res->ai_addrlen) == -1) {
printf("connect(): Failure!\n");
freeaddrinfo(res);
return 1;
}
printf("connect(): Success!\n");
printf("Connecting to %s...\n", servaddr);
free(servaddr);
freeaddrinfo(res);
return 0;
}
Here's the printaddrs() function
void printaddrs(struct addrinfo *res) {
struct addrinfo *p;
int count = 0;
char ipstr[INET6_ADDRSTRLEN], ipver[5];
for (p = res; p != NULL; p = p->ai_next) {
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *) p->ai_addr;
inet_ntop(p->ai_family, ipv4, ipstr, sizeof(ipstr));
strcpy(ipver, "ipv4");
} else if (p->ai_family == AF_INET6) { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *) p->ai_addr;
inet_ntop(p->ai_family, ipv6, ipstr, sizeof(ipstr));
strcpy(ipver, "ipv6");
} else { // Other IP format
continue;
}
printf("%d: %s\t:\t%s\n", count, ipstr, ipver);
count++;
}
}
And here's strin()
char *strin(FILE *stream) {
char *str = NULL;
size_t size = 0;
int c;
int i = 0;
while (1) {
c = getc(stream);
if (stream != stdin) {
if(c == EOF)
break;
} else {
if(c == EOF || c =='\n')
break;
}
size++;
str = realloc(str, size * sizeof(char));
*(str + i) = c;
i++;
}
if (size == 0) {
return NULL;
}
str[size] = '\0';
return str;
}
Here's the problem (Finally...)
When I run server.c, getaddrinfo() returnes the *res structure, which is a pointer to a linked list of addresses, the output looks like this:
0: 2.0.31.144 : ipv4 1: a00:1f90:: : ipv6
I would have expected to get the localhost address, 127.0.0.1 for IPv4 and ::1 for IPv6 but apparently not, when I enter 0 to choose the ipv4 address:
'hints' filled successfully! getaddrinfo(): Success! 0: 2.0.31.144 : ipv4 1: a00:1f90:: : ipv6 Select an address to bind to: 0 Chosen address: 2.0.31.144 socket(): Success! bind(): Success! Socket listening on port 8080...
And then connect to that address when running client.c
Choose an address to connect to: 2.0.31.144 getaddrinfo(): Success! socket(): Success! connect(): Failure!
The connect function doesn't work...
Does anybody have any suggestions?
Thank you all in advance for your answers, I really appreciate it!
I tried asking artificial intelligence for a solution... Not the smartest move, I know... I'm getting pretty desperate
Instead of this:
This:
Similar treatment for the ipv6 struct:
This will print
0.0.0.0and::for your two addresses. Which I believe is to be expected for when using AI_PASSIVE. These are the "any" address which tells server code to bind to all adapters - which is usually what you want.If you remove the AI_PASSIVE flag from your getaddrsinfo call, you'll likely get
127.0.0.1and::1printed. These are the localhost addresses.