Files
2026-05-31 14:34:00 -04:00

109 lines
3.3 KiB
C

#include <arpa/inet.h>
#include <assert.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "client.h"
#define BUFFER_MAX_SIZE 4096
/* Given a struct with server information, return a dynamically allocated
string of the IP address in dotted decimal or colon hexadecimal notation.
Consult Chapter 4, as well as /usr/include/netdb.h and
/usr/include/arpa/inet.h as needed. Use inet_ntop() to get the formatted
string based on the address's ai_addr field.
*/
char *
addr_string (struct addrinfo *server)
{
// Return safely in case server is NULL:
if (server == NULL)
return strdup ("no address information");
size_t addrlen
= server->ai_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN;
char *buffer = calloc (addrlen, sizeof (char));
void *addr;
if (server->ai_family == AF_INET)
addr = &((struct sockaddr_in *)server->ai_addr)->sin_addr;
else
addr = &((struct sockaddr_in6 *)server->ai_addr)->sin6_addr;
inet_ntop (server->ai_family, addr, buffer, addrlen);
return buffer;
}
/* Given the server address info, return a dynamically allocated string with
its transport-layer protocol, IP version, and IP address. For instance,
querying jmu.edu over TCP/IPv4 should return:
"TCP IPv4 134.126.10.50"
Use addr_string() to get the formatted address string, concatenate it to
the string to return, then free the result from addr_string(). If the
passed server parameter is NULL, use strdup() to return a dynamically
allocated copy of "no address information".
NOTE: When distinguishing TCP and UDP, you should be checking the
server->ai_socktype field, not server->ai_protocol. The test cases do not
set the server->ai_protocol, which is sometimes not used in practice.
*/
char *
serv_string (struct addrinfo *server)
{
// Return safely in case server is NULL:
if (server == NULL)
return strdup ("no address information");
size_t addrlen
= server->ai_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN;
char *buffer = calloc (10 + addrlen, sizeof (char));
char *proto = server->ai_socktype == SOCK_STREAM ? "TCP" : "UDP";
char *ipver = server->ai_family == AF_INET ? "IPv4" : "IPv6";
char *addr = addr_string (server);
snprintf (buffer, 10 + addrlen, "%s %s %s", proto, ipver, addr);
free (addr);
return buffer;
}
/* Given a hostname string, use getaddrinfo() to query DNS for the specified
protocol parameters. Boolean values indicate whether or not to use IPv6
(as opposed to IPv4) or TCP (as opposed to UDP). For this lab, the protocol
will only be "http", though it could be others in a more complete
implementation. Return the pointer to the linked list of server results.
*/
struct addrinfo *
get_server_list (const char *hostname, const char *proto, bool tcp, bool ipv6)
{
struct addrinfo hints = { 0 };
struct addrinfo *server_list = NULL;
hints.ai_family = ipv6 ? AF_INET6 : AF_INET;
if (tcp)
{
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
}
else
{
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
}
getaddrinfo (hostname, proto, &hints, &server_list);
return server_list;
}