109 lines
3.3 KiB
C
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;
|
|
}
|