commit a1c89ffb83abc71996516f94fdfd3616be9e7fe4 Author: Alessandro Mauri Date: Thu Nov 11 17:22:17 2021 +0100 first commit diff --git a/get.c b/get.c new file mode 100644 index 0000000..ced5465 --- /dev/null +++ b/get.c @@ -0,0 +1,212 @@ +#define _POSIX_C_SOURCE 200809l +#define _XOPEN_SOURCE 500 + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "xalloc.h" +#include "url.h" + +#define CHUNK_SIZE 1024 + +int send_chunk(int, char *, size_t); +int recv_chunk(int, char **, size_t *); + +ssize_t recv_timeout(int, char **, struct timeval *); + +int main(int argc, char **argv) +{ + if (argc < 2) + err(EXIT_FAILURE, "needs a url as argument"); + + url_t *url = url_parse(argv[1]); + if (!url) + err(EXIT_FAILURE, "error parsing url"); + + printf("scheme: %s\n" + "net_path: %s\n" + "abs_path: %s\n" + "net_node: %s\n" + "net_service: %s\n\n", + url->scheme, url->net_path, url->abs_path, url->net_node, url->net_service); + + // got the url parsed, now try to connect + if (strcmp(url->scheme, "http")) { + url_free(url); + free(url); + err(EXIT_FAILURE, "only supports http"); + } + + // get address information, this gives us a list of addinfos where + // we can connect to + struct addrinfo hints = {0}; + struct addrinfo *servinfo = {0}; + int status; + + hints.ai_family = AF_UNSPEC; + // TODO: adjust hints based on cache and url info (TCP or UDP) + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + status = getaddrinfo(url->net_node, url->net_service, &hints, &servinfo); + if (status) + err(status, "getaddrinfo: %s", gai_strerror(status)); + + + // This will print all available addresses for the given url + printf("IP addresses for %s:\n", argv[1]); + char ipstr[INET6_ADDRSTRLEN]; + struct addrinfo *p; + for(p = servinfo;p != NULL; p = p->ai_next) { + void *addr; + char *ipver; + // get the pointer to the address itself, + // different fields in IPv4 and IPv6: + if (p->ai_family == AF_INET) { // IPv4 + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + addr = &(ipv4->sin_addr); + ipver = "IPv4"; + } else { // IPv6 + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; + addr = &(ipv6->sin6_addr); + ipver = "IPv6"; + } + inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); + printf("\t%s: %s\n", ipver, ipstr); + } + puts("\n"); + + // Connect to the server + int servsocket; + struct addrinfo *sai; + for (sai = servinfo; sai; sai = sai->ai_next) { + servsocket = socket(sai->ai_family, sai->ai_socktype, sai->ai_protocol); + if (servsocket != -1) + break; + } + if (servsocket == -1 || !sai) + err(errno, NULL); + + + char port[32]; + void *addr; + long portn; + if (sai->ai_family == AF_INET) { // IPv4 + struct sockaddr_in *ipv4 = (struct sockaddr_in *)sai->ai_addr; + addr = &(ipv4->sin_addr); + portn = ntohs(ipv4->sin_port); + } else { // IPv6 + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)sai->ai_addr; + addr = &(ipv6->sin6_addr); + portn = ntohl(ipv6->sin6_port); + } + snprintf(port, 31, "%ld", portn); + inet_ntop(sai->ai_family, addr, ipstr, sizeof ipstr); + printf("Connected to %s:%s\n", ipstr, port); + + + connect(servsocket, sai->ai_addr, sai->ai_addrlen); + + //make socket non blocking + //fcntl(servsocket, F_SETFL, O_NONBLOCK); + + char *header; + xasprintf(&header, "GET /%s HTTP/1.0\r\n" + "Host: %s\r\n" + "\r\n", + url->abs_path, url->net_node); + + printf("HTTP request header:\n"); + printf("%s\n", header); + + if (send_chunk(servsocket, header, strlen(header)) == -1) + err(errno, NULL); + + + char *buf; + ssize_t bufsize; + struct timeval timeout = {10, 0}; + if ((bufsize = recv_timeout(servsocket, &buf, &timeout)) < 0) + err(errno, NULL); + + printf("GOT RESPONSE OF SIZE %ld\n\n", bufsize); + puts(buf ? buf : "NOTHING RECIVED"); + + free(buf); + + free(header); + + close(servsocket); + freeaddrinfo(servinfo); + url_free(url); + free(url); + return 0; +} + +// TODO: also use select here +int send_chunk(int s, char *msg, size_t len) +{ + // send a whole chunk of data + size_t sent = 0; + ssize_t st; + while (sent < len) { + st = send(s, msg+sent, len-sent, 0); + if (st == -1) + return -1; + sent += st; + } + return 0; +} + +ssize_t recv_timeout(int s, char **buf, struct timeval *t) +{ + if (!t || !buf) { + errno = EINVAL; + return -1; + } + + fd_set fds; + int n; + ssize_t size = 0, sr; + char chunk[CHUNK_SIZE]; + + FD_ZERO(&fds); + FD_SET(s, &fds); + *buf = NULL; + + for (;;) { + n = select(s+1, &fds, NULL, NULL, t); + if (n == -1) + goto err; + if (n == 0) + break; + sr = recv(s, chunk, CHUNK_SIZE, 0); + if (sr == -1) + goto err; + if (sr == 0) + break; + *buf = xrealloc(*buf, size+sr+1); + memcpy(&((*buf)[size]), chunk, sr); + size += sr; + (*buf)[size] = '\0'; + } + + return size; + err: + if (*buf) + free(*buf); + return -1; +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..c5e0003 --- /dev/null +++ b/makefile @@ -0,0 +1,15 @@ +CC ?= gcc +CFLAGS = -Wall -pedantic --std=c11 -O2 +DBG_CFLAGS = -Wall -Werror -pedantic --std=c11 -O0 -g +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +FILES = get.c xalloc.c url.c + +get: ${FILES} + +dbg: + ${CC} ${LDFLAGS} ${DBG_CFLAGS} ${FILES} -o get + +clean: + rm -f get *.o diff --git a/url.c b/url.c new file mode 100644 index 0000000..7dc3349 --- /dev/null +++ b/url.c @@ -0,0 +1,74 @@ +#define _POSIX_C_SOURCE 200809l + +#include +#include + +#include "xalloc.h" +#include "url.h" + +void url_free(url_t *u) +{ + if (u->scheme) + free(u->scheme); + if (u->net_path) + free(u->net_path); + if (u->abs_path) + free(u->abs_path); + if (u->net_node) + free(u->net_node); + if (u->net_service) + free(u->net_service); +} + +url_t *url_parse(const char *s) +{ + // TODO: verify each token (or whole url with regex) + + char *u = xstrdup(s); + url_t *url = xmalloc(sizeof(url_t)); + memset(url, 0, sizeof(url_t)); + char *state = NULL; + + char *t = u; + char *m = strstr(u, "://"); + char *scheme; + if (!m) { + scheme = "http"; + } else { + m[0] = '\0'; + m[2] = '\0'; + u = &(m[3]); + } + + char *net_path = strtok_r(u, "/", &state); + if (!net_path) + goto url_err; + char *abs_path = strtok_r(NULL, "", &state); + // default to index.html + if (!abs_path) + abs_path = ""; + + url->scheme = xstrdup(scheme); + url->net_path = xstrdup(net_path); + url->abs_path = xstrdup(abs_path); + + state = NULL; + char *net_service = url->scheme; + char *net_node = strtok_r(net_path, ":", &state); + if (strcmp(net_node, url->net_path)) { + net_service = strtok_r(NULL, "", &state); + if (!net_service) + net_service = url->scheme; + } + url->net_node = xstrdup(net_node); + url->net_service = xstrdup(net_service); + + free(t); + return url; + + url_err: + url_free(url); + free(url); + free(u); + return NULL; +} diff --git a/url.h b/url.h new file mode 100644 index 0000000..e262523 --- /dev/null +++ b/url.h @@ -0,0 +1,19 @@ +#ifndef _URL_H +#define _URL_H + +typedef struct _url { + char *fullname; + + char *scheme; + char *net_path; + char *abs_path; + + char *net_node; // just the domain + char *net_service; // http, port number +} url_t; + +void url_free(url_t *); +url_t *url_parse(const char *); + + +#endif diff --git a/xalloc.c b/xalloc.c new file mode 100644 index 0000000..5fa0270 --- /dev/null +++ b/xalloc.c @@ -0,0 +1,78 @@ +#define _POSIX_C_SOURCE 200809l + +#include +#include +#include +#include +#include +#include + +#include "xalloc.h" + +#define GUESS 240U + +void *xmalloc(size_t n) +{ + void *p = malloc(n); + if (!p) + err(errno, NULL); + return p; +} + +void *xcalloc(size_t n, size_t s) +{ + if (!n || !s) + err(EXIT_FAILURE, "calloc: invalid argument"); + void *p = calloc(n, s); + if (!p) + err(errno, NULL); + return p; +} + +void *xrealloc(void *p, size_t n) +{ + void *r = realloc(p, n); + if (!r && n) + err(errno, NULL); + return r; +} + +char *xstrdup(const char *s) +{ + char *r = strdup(s); + if (!r) + err(errno, NULL); + return r; +} + +int xasprintf(char **s, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = xvasprintf(s, fmt, ap); + va_end(ap); + return ret; +} + +int xvasprintf(char **s, const char *fmt, va_list ap) +{ + va_list ap2; + char *a; + unsigned int l=GUESS; + + if (!(a=malloc(GUESS))) return -1; + + va_copy(ap2, ap); + l=vsnprintf(a, GUESS, fmt, ap2); + va_end(ap2); + + if (l +#include + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void *xrealloc(void *, size_t); +int xasprintf(char **, const char *, ...); +int xvasprintf(char **, const char *, va_list); + +char *xstrdup(const char *); + +#endif