commit
a1c89ffb83
@ -0,0 +1,212 @@ |
|||||||
|
#define _POSIX_C_SOURCE 200809l |
||||||
|
#define _XOPEN_SOURCE 500 |
||||||
|
|
||||||
|
#include <sys/socket.h> |
||||||
|
#include <sys/select.h> |
||||||
|
|
||||||
|
#include <arpa/inet.h> |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <err.h> |
||||||
|
#include <netdb.h> |
||||||
|
#include <errno.h> |
||||||
|
|
||||||
|
#include <fcntl.h> |
||||||
|
#include <sys/time.h> |
||||||
|
|
||||||
|
#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; |
||||||
|
} |
@ -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
|
@ -0,0 +1,74 @@ |
|||||||
|
#define _POSIX_C_SOURCE 200809l |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#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; |
||||||
|
} |
@ -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 |
@ -0,0 +1,78 @@ |
|||||||
|
#define _POSIX_C_SOURCE 200809l |
||||||
|
|
||||||
|
#include <stdlib.h> |
||||||
|
#include <stdio.h> |
||||||
|
#include <string.h> |
||||||
|
#include <err.h> |
||||||
|
#include <errno.h> |
||||||
|
#include <stdarg.h> |
||||||
|
|
||||||
|
#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<GUESS) { |
||||||
|
char *b = xrealloc(a, l+1U); |
||||||
|
*s = b ? b : a; |
||||||
|
return l; |
||||||
|
} |
||||||
|
free(a); |
||||||
|
if (!(*s=xmalloc(l+1U))) return -1; |
||||||
|
return vsnprintf(*s, l+1U, fmt, ap); |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
#ifndef _ERROR_H |
||||||
|
#define _ERROR_H |
||||||
|
|
||||||
|
#include <stddef.h> |
||||||
|
#include <stdarg.h> |
||||||
|
|
||||||
|
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 |
Loading…
Reference in new issue