first commit
This commit is contained in:
commit
a1c89ffb83
212
get.c
Normal file
212
get.c
Normal file
@ -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;
|
||||
}
|
15
makefile
Normal file
15
makefile
Normal file
@ -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
|
74
url.c
Normal file
74
url.c
Normal file
@ -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;
|
||||
}
|
19
url.h
Normal file
19
url.h
Normal file
@ -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
|
78
xalloc.c
Normal file
78
xalloc.c
Normal file
@ -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);
|
||||
}
|
15
xalloc.h
Normal file
15
xalloc.h
Normal file
@ -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
|
Reference in New Issue
Block a user