initial commit

master
tiptorrent development team 2021-08-17 00:05:31 +02:00
commit 2610239d62
11 changed files with 1152 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

7
Makefile.am 100644
View File

@ -0,0 +1,7 @@
sbin_PROGRAMS = tiptorrent
AM_CFLAGS = ${LIBEVENT_CFLAGS} -g -Wall
tiptorrent_SOURCES = src/handler.c \
src/core.c \
src/main.c

21
README 100644
View File

@ -0,0 +1,21 @@
1) Compile:
make
2) Run (serving files in the local folder):
./tiptorrent
3) Generate test file
dd if=/dev/random of=TEST
... ctrl-c whenever you like ...
md5sum TEST
4) Test (on a different terminal)
wget http://localhost:9999/TEST
Now check that downloaded file is the same:
md5sum TEST

23
configure.ac 100644
View File

@ -0,0 +1,23 @@
AC_INIT(tiptorrent, 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/core.c 100644
View File

@ -0,0 +1,440 @@
/*
* Copyright (C) 2020-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 "core.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
struct ev_loop *tip_main_loop;
int num_clients;
static LIST_HEAD(client_list);
static LIST_HEAD(client_redirect_list);
static void tip_client_activate_pending(void);
static void tip_client_release(struct ev_loop *loop, struct tip_client *cli)
{
syslog(LOG_INFO, "closing connection with %s:%hu",
inet_ntoa(cli->addr.sin_addr), htons(cli->addr.sin_port));
list_del(&cli->list);
ev_io_stop(loop, &cli->io);
close(cli->io.fd);
if (cli->fd > 0)
close(cli->fd);
free((void *)cli->uri);
free((void *)cli->path);
free(cli);
num_clients--;
tip_client_activate_pending();
}
static int tip_client_payload_too_large(struct tip_client *cli)
{
char buf[] = "HTTP/1.1 413 Payload Too Large\r\n"
"Content-Length: 0\r\n\r\n";
send(tip_client_socket(cli), buf, strlen(buf), 0);
return -1;
}
static int tip_client_state_recv_hdr(struct tip_client *cli)
{
char *ptr;
ptr = strstr(cli->buf, "\r\n\r\n");
if (!ptr)
return 0;
cli->msg_len = ptr - cli->buf + 4;
ptr = strstr(cli->buf, "Content-Length: ");
if (ptr) {
sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length);
if (cli->content_length < 0)
return -1;
cli->msg_len += cli->content_length;
}
ptr = strstr(cli->buf, "Authorization: ");
if (ptr)
sscanf(ptr, "Authorization: %63[^\r\n]", cli->auth_token);
return 1;
}
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) {
if (ret < 0) {
syslog(LOG_ERR, "error reading from client %s:%hu (%s)\n",
inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port),
strerror(errno));
}
return ret;
}
return ret;
}
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 close;
ev_timer_again(loop, &cli->timer);
cli->buf_len += ret;
if (cli->buf_len >= sizeof(cli->buf)) {
syslog(LOG_ERR, "client request from %s:%hu is too long\n",
inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
tip_client_payload_too_large(cli);
goto close;
}
switch (cli->state) {
case TIP_CLIENT_RECEIVING_HEADER:
ret = tip_client_state_recv_hdr(cli);
if (ret < 0)
goto close;
if (!ret)
return;
cli->state = TIP_CLIENT_RECEIVING_PAYLOAD;
/* Fall through. */
case TIP_CLIENT_RECEIVING_PAYLOAD:
/* Still not enough data to process request. */
if (cli->buf_len < cli->msg_len)
return;
cli->state = TIP_CLIENT_PROCESSING_REQUEST;
/* fall through. */
case TIP_CLIENT_PROCESSING_REQUEST:
ret = tip_client_state_process_payload(cli);
if (ret > 0) {
/* client is pending. */
return;
} else if (ret < 0) {
syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
inet_ntoa(cli->addr.sin_addr),
ntohs(cli->addr.sin_port));
goto close;
}
ev_io_stop(loop, &cli->io);
ev_io_set(&cli->io, tip_client_socket(cli), EV_READ | EV_WRITE);
ev_io_start(loop, &cli->io);
break;
default:
syslog(LOG_ERR, "unknown read state, critical internal error\n");
goto close;
}
return;
close:
ev_timer_stop(loop, &cli->timer);
tip_client_release(loop, cli);
}
static void tip_client_redirect_timer_cb(struct ev_loop *loop, ev_timer *timer,
int events)
{
struct tip_client_redirect *redir;
redir = container_of(timer, struct tip_client_redirect, timer);
syslog(LOG_ERR, "timeout for client redirection to %s:%hu for %s\n",
inet_ntoa(redir->addr.sin_addr), ntohs(redir->addr.sin_port),
redir->uri);
list_del(&redir->list);
free((void *)redir->uri);
free(redir);
}
static int tip_client_redirect_create(const struct tip_client *cli)
{
struct tip_client_redirect *redir;
bool found = false;
if (!redirect || !cli->allow_redirect)
return 0;
list_for_each_entry(redir, &client_redirect_list, list) {
if (!strcmp(redir->uri, cli->uri) &&
redir->addr.sin_addr.s_addr == cli->addr.sin_addr.s_addr) {
found = true;
break;
}
}
if (found) {
syslog(LOG_INFO, "client redirection to %s:%hu for %s already exists, skipping",
inet_ntoa(cli->addr.sin_addr), htons(cli->addr.sin_port),
cli->uri);
return 0;
}
redir = calloc(1, sizeof(struct tip_client_redirect));
if (!redir)
return -1;
redir->addr = cli->addr;
redir->addr.sin_port = htons(9999);
redir->uri = strdup(cli->uri);
list_add_tail(&redir->list, &client_redirect_list);
ev_timer_init(&redir->timer, tip_client_redirect_timer_cb, 60, 0.);
ev_timer_start(tip_main_loop, &redir->timer);
syslog(LOG_INFO, "adding client redirection to %s:%hu for %s",
inet_ntoa(redir->addr.sin_addr), htons(redir->addr.sin_port),
redir->uri);
return 0;
}
static void tip_client_write_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);
ev_timer_again(loop, &cli->timer);
switch (cli->state) {
case TIP_CLIENT_PROCESSING_REQUEST_2:
ret = tip_client_state_process_payload_reply(cli);
if (ret > 0) {
goto close;
} else if (ret < 0) {
syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
inet_ntoa(cli->addr.sin_addr),
ntohs(cli->addr.sin_port));
goto close;
}
break;
case TIP_CLIENT_PROCESSING_REQUEST_3:
ret = tip_client_state_process_payload_bulk(cli);
if (ret > 0)
goto shutdown;
else if (ret < 0) {
syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n",
inet_ntoa(cli->addr.sin_addr),
ntohs(cli->addr.sin_port));
goto close;
}
break;
default:
syslog(LOG_ERR, "unknown write state, critical internal error\n");
goto close;
}
return;
shutdown:
if (cli->size > FILE_SIZE_THRESHOLD)
tip_client_redirect_create(cli);
close:
ev_timer_stop(loop, &cli->timer);
tip_client_release(loop, cli);
}
static void tip_client_cb(struct ev_loop *loop, struct ev_io *io, int events)
{
if (events & EV_READ)
return tip_client_read_cb(loop, io, events);
if (events & EV_WRITE)
return tip_client_write_cb(loop, io, events);
}
static void tip_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
{
struct tip_client *cli;
cli = container_of(timer, struct tip_client, timer);
syslog(LOG_ERR, "timeout request for client %s:%hu\n",
inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port));
tip_client_release(loop, cli);
}
/* Shut down connection if there is no data after 15 seconds. */
#define TIP_CLIENT_TIMEOUT 15
static void tip_client_start(struct tip_client *cli)
{
cli->state = TIP_CLIENT_RECEIVING_HEADER;
ev_io_start(tip_main_loop, &cli->io);
ev_timer_init(&cli->timer, tip_client_timer_cb, TIP_CLIENT_TIMEOUT, 0.);
ev_timer_start(tip_main_loop, &cli->timer);
}
void tip_client_pending(struct tip_client *cli)
{
ev_io_stop(tip_main_loop, &cli->io);
ev_timer_stop(tip_main_loop, &cli->timer);
cli->state = TIP_CLIENT_PENDING;
}
static void tip_client_activate_pending(void)
{
struct tip_client *cli, *next;
list_for_each_entry_safe(cli, next, &client_list, list) {
if (cli->state != TIP_CLIENT_PENDING)
continue;
tip_client_redirect(cli);
ev_io_set(&cli->io, tip_client_socket(cli), EV_READ | EV_WRITE);
ev_io_start(tip_main_loop, &cli->io);
ev_timer_start(tip_main_loop, &cli->timer);
cli->state = TIP_CLIENT_PROCESSING_REQUEST_2;
break;
}
}
bool tip_client_redirect(struct tip_client *cli)
{
struct tip_client_redirect *redir, *next;
char addr[INET_ADDRSTRLEN + 1];
if (!redirect)
return false;
inet_ntop(AF_INET, &cli->addr.sin_addr, addr, INET_ADDRSTRLEN);
list_for_each_entry_safe(redir, next, &client_redirect_list, list) {
if (strcmp(redir->uri, cli->uri) ||
redir->addr.sin_addr.s_addr == cli->addr.sin_addr.s_addr)
continue;
cli->redirect = true;
cli->redirect_addr = redir->addr;
syslog(LOG_INFO, "redirecting client %s:%hu to %s:%hu",
addr, htons(cli->addr.sin_port),
inet_ntoa(redir->addr.sin_addr), htons(redir->addr.sin_port));
free((void *)redir->uri);
ev_timer_stop(tip_main_loop, &redir->timer);
list_del(&redir->list);
free(redir);
return true;
}
syslog(LOG_INFO, "no client redirections are available for %s:%hu",
addr, htons(cli->addr.sin_port));
return false;
}
#define TIP_TCP_KEEPALIVE_IDLE 60
#define TIP_TCP_KEEPALIVE_INTL 30
#define TIP_TCP_KEEPALIVE_CNT 4
void tip_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events)
{
int intl = TIP_TCP_KEEPALIVE_INTL, cnt = TIP_TCP_KEEPALIVE_CNT;
int on = 1, idle = TIP_TCP_KEEPALIVE_IDLE;
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(client_addr);
struct tip_client *cli;
int client_sd, flags;
if (events & EV_ERROR)
return;
client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen);
if (client_sd < 0) {
syslog(LOG_ERR, "cannot accept client connection\n");
return;
}
flags = fcntl(client_sd, F_GETFL);
fcntl(client_sd, F_SETFL, flags | O_NONBLOCK);
setsockopt(client_sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(int));
setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int));
setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPINTVL, &intl, sizeof(int));
setsockopt(client_sd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(int));
cli = (struct tip_client *)calloc(1, sizeof(struct tip_client));
if (!cli) {
close(client_sd);
return;
}
memcpy(&cli->addr, &client_addr, sizeof(client_addr));
cli->fd = -1;
syslog(LOG_ERR, "accepting client connection from %s:%hu",
inet_ntoa(cli->addr.sin_addr), htons(cli->addr.sin_port));
list_add_tail(&cli->list, &client_list);
ev_io_init(&cli->io, tip_client_cb, client_sd, EV_READ);
tip_client_start(cli);
}
int tip_socket_server_init(const char *port)
{
struct sockaddr_in local;
int sd, on = 1;
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sd < 0) {
syslog(LOG_ERR, "cannot create main socket\n");
return -1;
}
setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int));
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(atoi(port));
if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) {
close(sd);
syslog(LOG_ERR, "cannot bind socket\n");
return -1;
}
listen(sd, 250);
return sd;
}

83
src/core.h 100644
View File

@ -0,0 +1,83 @@
#ifndef _TIP_CORE_H
#define _TIP_CORE_H
#include <ev.h>
#include "list.h"
#include <stdbool.h>
#include <netinet/in.h>
#define TIP_MSG_REQUEST_MAXLEN 131072
extern const char *root;
#define DEFAULT_MAX_CLIENTS 3
extern int max_clients;
extern int num_clients;
extern bool redirect;
/* max_client logic only applies for files larger than 1024 bytes. */
#define FILE_SIZE_THRESHOLD 1024
enum tip_client_state {
TIP_CLIENT_PENDING = 0,
TIP_CLIENT_RECEIVING_HEADER,
TIP_CLIENT_RECEIVING_PAYLOAD,
TIP_CLIENT_PROCESSING_REQUEST,
TIP_CLIENT_PROCESSING_REQUEST_2,
TIP_CLIENT_PROCESSING_REQUEST_3,
};
struct tip_client {
struct list_head list;
struct ev_io io;
struct ev_timer timer;
struct sockaddr_in addr;
enum tip_client_state state;
char buf[TIP_MSG_REQUEST_MAXLEN];
unsigned int buf_len;
unsigned int msg_len;
int content_length;
char auth_token[64];
/* for file serving. */
const char *uri;
const char *path;
size_t size;
int fd;
off_t offset;
/* for redirection. */
bool redirect;
struct sockaddr_in redirect_addr;
bool allow_redirect;
};
static inline int tip_client_socket(const struct tip_client *cli)
{
return cli->io.fd;
}
void tip_client_pending(struct tip_client *cli);
bool tip_client_redirect(struct tip_client *cli);
extern struct ev_loop *tip_main_loop;
int tip_socket_server_init(const char *port);
void tip_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events);
int tip_client_state_process_payload(struct tip_client *cli);
int tip_client_state_process_payload_reply(struct tip_client *cli);
int tip_client_state_process_payload_bulk(struct tip_client *cli);
enum tip_http_method {
TIP_METHOD_GET = 0,
TIP_METHOD_POST,
TIP_METHOD_NO_HTTP
};
struct tip_client_redirect {
struct list_head list;
struct sockaddr_in addr;
const char *uri;
struct ev_timer timer;
};
#endif

155
src/handler.c 100644
View File

@ -0,0 +1,155 @@
#include "core.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <errno.h>
static int tip_client_method_not_found(struct tip_client *cli)
{
/* To meet RFC 7231, this function MUST generate an Allow header field
* containing the correct methods. For example: "Allow: POST\r\n"
*/
char buf[] = "HTTP/1.1 405 Method Not Allowed\r\n"
"Content-Length: 0\r\n\r\n";
send(tip_client_socket(cli), buf, strlen(buf), 0);
return -1;
}
static int tip_client_file_not_found(struct tip_client *cli)
{
char buf[] = "HTTP/1.1 404 Not Found\r\n"
"Content-Length: 0\r\n\r\n";
send(tip_client_socket(cli), buf, strlen(buf), 0);
return -1;
}
/* TODO: sanitize uri, don't escape directory serving files. */
static bool sanitize(const char *uri)
{
return true;
}
#define BLOCK 1024000
int tip_client_state_process_payload(struct tip_client *cli)
{
const char *trailer, *x_redirect;
enum tip_http_method method;
bool allow_redirect = true;
char _uri[32], *uri = _uri;
char path[PATH_MAX + 1];
char redirect[5];
struct stat st;
int err;
/* syslog(LOG_DEBUG, "%s:rhu %.32s ...\n",
inet_ntoa(cli->addr.sin_addr),
ntohs(cli->addr.sin_port), cli->buf); */
if (!strncmp(cli->buf, "GET", strlen("GET"))) {
method = TIP_METHOD_GET;
if (sscanf(cli->buf, "GET %31s HTTP/1.1", uri) != 1)
return tip_client_method_not_found(cli);
} else {
return tip_client_method_not_found(cli);
}
x_redirect = strstr(cli->buf, "X-Accept-Redirect: ");
if (x_redirect &&
sscanf(x_redirect, "X-Accept-Redirect: %4s", redirect) == 1 &&
!strncmp(redirect, "off", strlen("off")))
allow_redirect = false;
trailer = strstr(cli->buf, "\r\n\r\n");
if (!sanitize(uri))
return tip_client_method_not_found(cli);
snprintf(path, PATH_MAX, "%s/%s", root, uri);
err = stat(path, &st);
if (err < 0)
return tip_client_file_not_found(cli);
/* skip initial / */
uri++;
cli->uri = strdup(uri);
cli->path = strdup(path);
cli->size = st.st_size;
cli->allow_redirect = allow_redirect;
num_clients++;
if (cli->size > FILE_SIZE_THRESHOLD && num_clients > max_clients) {
if (!tip_client_redirect(cli)) {
tip_client_pending(cli);
return 1;
}
}
cli->state = TIP_CLIENT_PROCESSING_REQUEST_2;
return 0;
}
int tip_client_state_process_payload_reply(struct tip_client *cli)
{
char buf[1024];
int fd;
if (cli->redirect) {
snprintf(buf, sizeof(buf),
"HTTP/1.1 301 Moves Permanently\r\nLocation: http://%s:%hu/%s\r\n\r\n",
inet_ntoa(cli->redirect_addr.sin_addr),
htons(cli->redirect_addr.sin_port), cli->uri);
send(tip_client_socket(cli), buf, strlen(buf), 0);
return 1;
}
fd = open(cli->path, O_RDONLY);
if (fd < 0)
return tip_client_file_not_found(cli);
snprintf(buf, sizeof(buf),
"HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n",
cli->size);
send(tip_client_socket(cli), buf, strlen(buf), 0);
cli->fd = fd;
cli->state = TIP_CLIENT_PROCESSING_REQUEST_3;
return 0;
}
int tip_client_state_process_payload_bulk(struct tip_client *cli)
{
sendfile(tip_client_socket(cli), cli->fd, &cli->offset, BLOCK);
if (cli->offset >= cli->size)
return 1;
return 0;
}

162
src/list.h 100644
View File

@ -0,0 +1,162 @@
#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H
#include <stddef.h>
#define container_of(ptr, type, member) ({ \
typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
#endif

95
src/main.c 100644
View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2020-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 "core.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <getopt.h>
int max_clients = DEFAULT_MAX_CLIENTS;
const char *root = ".";
bool redirect;
static struct option tip_repo_opts[] = {
{ "max-clients", 1, 0, 'n' },
{ "redirect", 0, 0, 'r' },
{ "root", 1, 0, 't' },
{ NULL },
};
struct ev_io ev_io_server_rest;
int main(int argc, char *argv[])
{
int socket_rest, val;
openlog("tiptorrent", LOG_PID, LOG_DAEMON);
tip_main_loop = ev_default_loop(0);
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
exit(EXIT_FAILURE);
while (1) {
val = getopt_long(argc, argv, "n:r", tip_repo_opts, NULL);
if (val < 0)
break;
switch (val) {
case 'n':
max_clients = atoi(optarg);
if (max_clients <= 0) {
syslog(LOG_ERR, "Invalid number for max_clients");
return EXIT_FAILURE;
}
break;
case 'r':
redirect = true;
break;
case 't':
root = strdup(optarg);
break;
case '?':
return EXIT_FAILURE;
default:
break;
}
}
socket_rest = tip_socket_server_init("9999");
if (socket_rest < 0) {
syslog(LOG_ERR, "Cannot open tiptorrent server socket\n");
exit(EXIT_FAILURE);
}
ev_io_init(&ev_io_server_rest, tip_server_accept_cb, socket_rest, EV_READ);
ev_io_start(tip_main_loop, &ev_io_server_rest);
syslog(LOG_INFO, "Waiting for connections\n");
while (1)
ev_loop(tip_main_loop, 0);
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,84 @@
#!/bin/bash
if [ $UID -ne 0 ]
then
echo "You must be root to run this test script"
exit 0
fi
# / c1
# /- c2
# srv ----- br -- c3
# \- c4
start () {
ip netns add srv
ip netns add br
ip netns add c1
ip netns add c2
ip netns add c3
ip netns add c4
ip link add veth0 netns srv type veth peer name veth0 netns br
ip link add veth1 netns br type veth peer name veth0 netns c1
ip link add veth2 netns br type veth peer name veth0 netns c2
ip link add veth3 netns br type veth peer name veth0 netns c3
ip link add veth4 netns br type veth peer name veth0 netns c4
ip -net br link set up dev veth0
ip -net br link set up dev veth1
ip -net br link set up dev veth2
ip -net br link set up dev veth3
ip -net br link set up dev veth4
ip -net br link add name br0 type bridge
ip -net br link set dev veth0 master br0
ip -net br link set dev veth1 master br0
ip -net br link set dev veth2 master br0
ip -net br link set dev veth3 master br0
ip -net br link set dev veth4 master br0
ip -net br link set up dev br0
ip -net srv addr add 10.141.10.1/24 dev veth0
ip -net srv link set up dev veth0
ip netns exec srv .././grepo --max-clients 1 --redirect --root . &
ip -net c1 addr add 10.141.10.2/24 dev veth0
ip -net c1 link set up dev veth0
ip netns exec c1 .././tiptorrent --max-clients 1 &
ip -net c2 addr add 10.141.10.3/24 dev veth0
ip -net c2 link set up dev veth0
ip netns exec c2 .././tiptorrent --max-clients 1 &
ip -net c3 addr add 10.141.10.4/24 dev veth0
ip -net c3 link set up dev veth0
ip netns exec c3 .././tiptorrent --max-clients 1 &
ip -net c4 addr add 10.141.10.5/24 dev veth0
ip -net c4 link set up dev veth0
ip netns exec c4 .././tiptorrent --max-clients 1 &
}
stop () {
ip netns del srv
ip netns del br
ip netns del c1
ip netns del c2
ip netns del c3
ip netns del c4
killall -15 tiptorrent
}
case $1 in
start)
start
;;
stop)
stop
;;
*)
echo "$0 [start|stop]"
;;
esac
exit 0

View File

@ -0,0 +1,18 @@
#!/bin/bash
if [ ! -f TEST ]
then
echo "create the TEST first, e.g. dd if=/dev/urandom of=TEST bs=750M count=1 iflag=fullblock"
exit 0
fi
if [ $UID -ne 0 ]
then
echo "You must be root to run this test script"
exit 0
fi
ip netns exec c1 wget http://10.141.10.1:9999/TEST -O /dev/null &
ip netns exec c2 wget http://10.141.10.1:9999/TEST -O /dev/null &
ip netns exec c3 wget http://10.141.10.1:9999/TEST -O /dev/null &
ip netns exec c4 wget http://10.141.10.1:9999/TEST -O /dev/null &