You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
brwsh/get.c

213 lines
4.6 KiB

2 years ago
#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;
}