initial commit

master
tiptorrent development team 2021-09-17 14:13:40 +02:00
commit 5c8ec91711
5 changed files with 536 additions and 0 deletions

64
.gitignore vendored 100644
View File

@ -0,0 +1,64 @@
# http://www.gnu.org/software/automake
Makefile.in
.deps/
.dirstamp
# http://www.gnu.org/software/autoconf
autom4te.cache
/autoscan.log
/autoscan-*.log
/aclocal.m4
/compile
/config.cache
/config.guess
/config.h.in
/config.log
/config.status
/config.sub
/configure
/configure~
/configure.scan
/depcomp
/install-sh
/missing
/stamp-h1
/build-aux/
# https://www.gnu.org/software/libtool/
/ltmain.sh
# http://www.gnu.org/software/texinfo
/texinfo.tex
# http://www.gnu.org/software/m4/
m4/libtool.m4
m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/lt~obsolete.m4
# Generated Makefile
# (meta build system like autotools,
# can automatically generate from config.status script
# (which is called by configure script))
Makefile
# Object files
*.o
*.ko
*.obj
*.elf
# Executables
tiptorrent
# Debug files
*.dSYM/
*.su
*.idb
*.pdb

5
Makefile.am 100644
View File

@ -0,0 +1,5 @@
sbin_PROGRAMS = tiptorrent-client
AM_CFLAGS = ${LIBEVENT_CFLAGS} -g -Wall
tiptorrent_client_SOURCES = src/main.c

23
configure.ac 100644
View File

@ -0,0 +1,23 @@
AC_INIT(tiptorrent-client, 1.0, info@soleta.eu)
AC_CONFIG_AUX_DIR([build-aux])
AC_PREFIX_DEFAULT(/usr)
AC_CANONICAL_HOST
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([-Wall foreign subdir-objects tar-pax no-dist-gzip dist-bzip2 1.6])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S
case "$host" in
*-*-linux*) ;;
*) AC_MSG_ERROR([Linux only, sorry!]);;
esac
AC_CHECK_LIB([ev], [ev_loop_new], , AC_MSG_ERROR([libev not found]))
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

440
src/main.c 100644
View File

@ -0,0 +1,440 @@
/*
* Copyright (C) 2021 Soleta Networks <info@soleta.eu>
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*/
#include <stdlib.h>
#include <stdio.h>
#include <ev.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <stddef.h>
#include <stdbool.h>
#include <sys/time.h>
#include <limits.h>
#include <syslog.h>
#define TIP_TORRENT_PORT 9999
/* number of chunks for files. */
#define MAX_CHUNKS 4
#define container_of(ptr, type, member) ({ \
typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
static const char *filename;
static const char *addr;
struct ev_loop *tip_main_loop;
enum {
TIP_CLIENT_RECEIVING_HEADER,
TIP_CLIENT_RECEIVING_PAYLOAD,
TIP_CLIENT_NOTIFY_REDIRECT,
TIP_CLIENT_DONE,
};
struct tip_client {
ev_io io;
struct sockaddr_in addr;
char buf[10240000];
uint32_t buf_len;
uint64_t data_len;
uint64_t content_len;
int state;
int fd;
bool error;
bool redirected;
const char *payload;
};
static struct tip_client _cli = {
.fd = -1,
};
struct {
uint32_t direct_from_server;
uint32_t redirects;
} tip_client_stats;
static int tip_client_recv(struct tip_client *cli, int events)
{
struct ev_io *io = &cli->io;
int ret;
ret = recv(io->fd, cli->buf + cli->buf_len,
sizeof(cli->buf) - cli->buf_len, 0);
if (ret < 0) {
syslog(LOG_ERR, "error reading from server %s:%hu (%s)\n",
inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port),
strerror(errno));
}
return ret;
}
static void tip_client_close(struct tip_client *cli)
{
ev_io_stop(tip_main_loop, &cli->io);
shutdown(cli->io.fd, SHUT_RDWR);
close(cli->io.fd);
cli->buf_len = 0;
}
static void tip_client_error(struct tip_client *cli)
{
cli->error = true;
tip_client_close(cli);
}
static int tip_client_connect(const char *addr);
static int tip_client_state_recv_hdr(struct tip_client *cli)
{
char *ptr, *trailer, *payload;
char redirect_addr[32];
uint32_t payload_len;
uint32_t header_len;
int ret;
ptr = strstr(cli->buf, "\r\n\r\n");
if (!ptr)
return 0;
if (!strncmp(cli->buf, "HTTP/1.1 404 Not Found", strlen("HTTP/1.1 404 Not Found"))) {
syslog(LOG_ERR, "server says file `%s' not found\n", filename);
return -1;
}
if (!strncmp(cli->buf, "HTTP/1.1 301 Moves Permanently", strlen("HTTP/1.1 301 Moves Permanently"))) {
ptr = strstr(cli->buf, "Location:");
if (!ptr)
return -1;
ret = sscanf(ptr, "Location: http://%31s[^\r\n]",
redirect_addr);
if (ret != 1)
return -1;
ptr = strchr(redirect_addr, ':');
if (!ptr)
return -1;
ptr[0] = '\0';
syslog(LOG_INFO, "Redirected to %s to fetch file %s\n",
redirect_addr, filename);
cli->redirected = true;
tip_client_close(cli);
tip_client_connect(redirect_addr);
cli->state = TIP_CLIENT_RECEIVING_HEADER;
return 0;
}
trailer = ptr + 4;
ptr = strstr(cli->buf, "Content-Length: ");
if (!ptr)
return -1;
if (sscanf(ptr, "Content-Length: %lu[^\r\n]", &cli->content_len) != 1)
return -1;
if (cli->content_len < 0)
return -1;
if (cli->content_len == 0) {
cli->buf_len = 0;
return 1;
}
if (cli->redirected)
tip_client_stats.redirects++;
else
tip_client_stats.direct_from_server++;
cli->fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (cli->fd < 0)
return ret;
header_len = trailer - cli->buf;
payload = cli->buf + header_len;
payload_len = cli->buf_len - header_len;
cli->data_len += cli->buf_len;
cli->buf_len = 0;
if (payload_len > 0) {
ret = write(cli->fd, payload, payload_len);
if (ret < 0)
return ret;
}
if (payload_len >= cli->content_len)
return 0;
return 1;
}
static int tip_client_state_recv_payload(struct tip_client *cli)
{
int ret;
cli->data_len += cli->buf_len;
ret = write(cli->fd, cli->buf, cli->buf_len);
if (ret < 0)
return ret;
cli->buf_len = 0;
if (cli->data_len >= cli->content_len) {
if (cli->redirected) {
tip_client_close(cli);
tip_client_connect(addr);
cli->state = TIP_CLIENT_NOTIFY_REDIRECT;
return 1;
}
cli->state = TIP_CLIENT_DONE;
return 0;
}
return 1;
}
static int tip_client_state_notify_redirect(struct tip_client *cli)
{
char *ptr;
ptr = strstr(cli->buf, "\r\n\r\n");
if (!ptr)
return 0;
if (strncmp(cli->buf, "HTTP/1.1 200 OK", strlen("HTTP/1.1 200 OK")))
return -1;
ptr = strstr(cli->buf, "Content-Length: ");
if (!ptr)
return -1;
if (sscanf(ptr, "Content-Length: %lu[^\r\n]", &cli->content_len) != 1)
return -1;
if (cli->content_len < 0)
return -1;
if (cli->content_len != 0)
return -1;
cli->state = TIP_CLIENT_DONE;
return 0;
}
static void tip_client_read_cb(struct ev_loop *loop, struct ev_io *io, int events)
{
struct tip_client *cli;
int ret;
cli = container_of(io, struct tip_client, io);
ret = tip_client_recv(cli, events);
if (ret < 0)
goto error;
cli->buf_len += ret;
switch (cli->state) {
case TIP_CLIENT_RECEIVING_HEADER:
ret = tip_client_state_recv_hdr(cli);
if (ret < 0)
goto error;
if (!ret)
return;
cli->state = TIP_CLIENT_RECEIVING_PAYLOAD;
/* Fall through. */
case TIP_CLIENT_RECEIVING_PAYLOAD:
ret = tip_client_state_recv_payload(cli);
if (ret < 0)
goto error;
if (ret == 0)
goto close;
break;
case TIP_CLIENT_NOTIFY_REDIRECT:
ret = tip_client_state_notify_redirect(cli);
if (ret < 0)
goto error;
if (ret == 0)
goto close;
break;
}
return;
error:
tip_client_error(cli);
return;
close:
tip_client_close(cli);
return;
}
static void tip_client_connect_cb(struct ev_loop *loop, struct ev_io *io, int events)
{
struct tip_client *cli;
char buf[PATH_MAX + 1];
int ret, len;
cli = container_of(io, struct tip_client, io);
if (events & EV_ERROR)
return;
len = sizeof(cli->addr);
ret = connect(cli->io.fd, (struct sockaddr *)&cli->addr, len);
if (ret < 0) {
if (errno != EINPROGRESS) {
perror("connect");
tip_client_error(cli);
return;
}
}
if (cli->state == TIP_CLIENT_NOTIFY_REDIRECT)
snprintf(buf, sizeof(buf), "POST /%s HTTP/1.1\r\n\r\n", filename);
else
snprintf(buf, sizeof(buf), "GET /%s HTTP/1.1\r\n\r\n", filename);
ret = send(cli->io.fd, buf, strlen(buf), 0);
if (ret < 0) {
tip_client_error(cli);
return;
}
ev_io_stop(tip_main_loop, &cli->io);
ev_io_init(&cli->io, tip_client_read_cb, cli->io.fd, EV_READ);
ev_io_start(tip_main_loop, &cli->io);
}
static int tip_client_connect(const char *addr)
{
struct tip_client *cli = &_cli;
int remote_fd;
int flags;
int len;
int ret;
remote_fd = socket(AF_INET, SOCK_STREAM, 0);
if (remote_fd < 0)
return -1;
flags = fcntl(remote_fd, F_GETFL);
flags |= O_NONBLOCK;
ret = fcntl(remote_fd, F_SETFL, flags);
if (ret < 0)
return ret;
cli->addr.sin_family = AF_INET;
cli->addr.sin_addr.s_addr = inet_addr(addr);
cli->addr.sin_port = htons(TIP_TORRENT_PORT);
len = sizeof(cli->addr);
ret = connect(remote_fd, (struct sockaddr *)&cli->addr, len);
if (ret < 0 && errno != EINPROGRESS) {
perror("connect");
return ret;
}
ev_io_init(&cli->io, tip_client_connect_cb, remote_fd, EV_WRITE);
ev_io_start(tip_main_loop, &cli->io);
syslog(LOG_INFO, "connecting to %s to fetch file %s\n", addr, filename);
return 0;
}
static uint32_t select_file_chunk(bool *file_chunk)
{
struct timeval tv;
uint32_t k;
int i;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
k = rand() % MAX_CHUNKS;
for (i = 0; i < MAX_CHUNKS; i++) {
if (!file_chunk[k])
break;
k++;
if (k == MAX_CHUNKS)
k = 0;
}
return k;
}
static char _filename[PATH_MAX + 1];
int main(int argc, char *argv[])
{
struct timeval tv_start, tv_stop, tv;
bool file_chunk[MAX_CHUNKS] = {};
int i, k;
if (argc != 3) {
printf("%s [ip] [file]\n", argv[0]);
return EXIT_FAILURE;
}
addr = argv[1];
openlog("tiptorrent-client", LOG_PID, LOG_DAEMON);
signal(SIGPIPE, SIG_IGN);
tip_main_loop = ev_default_loop(0);
gettimeofday(&tv_start, NULL);
for (i = 0; i < MAX_CHUNKS; i++) {
memset(&_cli, 0, sizeof(_cli));
k = select_file_chunk(file_chunk);
snprintf(_filename, sizeof(_filename), "%s.%u", argv[2], k);
filename = _filename;
syslog(LOG_INFO, "Requesting file %s to server\n", filename);
tip_client_connect(argv[1]);
_cli.state = TIP_CLIENT_RECEIVING_HEADER;
while (_cli.state != TIP_CLIENT_DONE || _cli.error)
ev_loop(tip_main_loop, 0);
file_chunk[k] = true;
}
if (_cli.state == TIP_CLIENT_DONE) {
gettimeofday(&tv_stop, NULL);
timersub(&tv_stop, &tv_start, &tv);
printf("Done in %lus.%lums. "
"Direct from server: %u Redirected: %u\n",
tv.tv_sec, tv.tv_usec,
tip_client_stats.direct_from_server,
tip_client_stats.redirects);
return EXIT_SUCCESS;
}
printf("Failure, see syslog for details.\n");
return EXIT_FAILURE;
}

View File

@ -0,0 +1,4 @@
for ((i=1;i<=18;i++))
do
ip netns exec c$i .././tiptorrent-client 10.141.10.1 TEST &
done