Handle http:// torrent urls

This commit is contained in:
x3 2022-07-14 15:33:32 +02:00
parent b9465c916e
commit 4de06eae0c
Signed by: x3
GPG Key ID: 7E9961E8AD0E240E
3 changed files with 172 additions and 6 deletions

View File

@ -1,4 +1,5 @@
MultiThread = Yes MultiThread = Yes
HttpTorrent = Yes
InstallPrefix = /usr/local/bin InstallPrefix = /usr/local/bin
PROGNAME = torrent-verify PROGNAME = torrent-verify
@ -8,10 +9,15 @@ CPPFLAGS = -DPROGRAM_NAME='"$(PROGNAME)"' -DBUILD_INFO \
-DBUILD_HASH="\"`git rev-parse --abbrev-ref HEAD` -> `git rev-parse --short HEAD`\"" -DBUILD_DATE="\"`date -I`\"" -DBUILD_HASH="\"`git rev-parse --abbrev-ref HEAD` -> `git rev-parse --short HEAD`\"" -DBUILD_DATE="\"`date -I`\""
ifeq ($(MultiThread), Yes) ifeq ($(MultiThread), Yes)
CFLAGS += -lpthread LDLIBS += -lpthread
CPPFLAGS += -DMT CPPFLAGS += -DMT
endif endif
ifeq ($(HttpTorrent), Yes)
LDLIBS += -lcurl
CPPFLAGS += -DHTTP_TORRENT=1
endif
SOURCE = $(wildcard subm/heapless-bencode/*.c) $(wildcard src/*.c) SOURCE = $(wildcard subm/heapless-bencode/*.c) $(wildcard src/*.c)
#OBJ = $(addsuffix .o,$(basename $(SOURCE))) #OBJ = $(addsuffix .o,$(basename $(SOURCE)))
OBJS = $(SOURCE:.c=.o) OBJS = $(SOURCE:.c=.o)
@ -25,7 +31,8 @@ uninstall:
rm -f -- $(InstallPrefix)/$(PROGNAME) rm -f -- $(InstallPrefix)/$(PROGNAME)
$(PROGNAME): $(OBJS) $(PROGNAME): $(OBJS)
$(CC) -o $@ $+ $(CFLAGS) $(CPPFLAGS) $(CC) -o $@ $+ $(CFLAGS) $(CPPFLAGS) $(LDLIBS)
clean: clean:
-rm -- $(OBJS) $(PROGNAME) -rm -- $(OBJS) $(PROGNAME)

View File

@ -42,6 +42,9 @@ BUILD_HASH " (" BUILD_DATE ")\n"
#ifdef MT #ifdef MT
"MultiThread support\n" "MultiThread support\n"
#endif #endif
#ifdef HTTP_TORRENT
"HTTP Torrent support\n"
#endif
#endif #endif
); );
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
@ -55,7 +58,11 @@ int main(int argc, char** argv) {
help(); help();
if (optind >= argc) { if (optind >= argc) {
fprintf(stderr, "Provide at least one torrent file\n"); fprintf(stderr, "Provide at least one torrent file"
#ifdef HTTP_TORRENT
" or an http link to a torrent file"
#endif
"\n");
usage(); usage();
} }

View File

@ -3,18 +3,30 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdint.h>
#include "sha1.h" #include "sha1.h"
#ifdef HTTP_TORRENT
#include <curl/curl.h>
#endif
/* 128 MiB */ /* 128 MiB */
#define MAX_TORRENT_SIZE 128*1024*1024 #define MAX_TORRENT_SIZE 128*1024*1024
#ifdef HTTP_TORRENT
struct http_metainfo {
int64_t c_size, max_size;
char *data;
};
#endif
/* /*
* Read the file in memory, and return the pointer to it (which needs to be * Read the file in memory, and return the pointer to it (which needs to be
* freed) in out_contents and the size in out_size. If the file is too big, * freed) in out_contents and the size in out_size. If the file is too big,
* fail. Returns 0 on success and an errno on fail. * fail. Returns 0 on success and an errno on fail.
*/ */
static int metainfo_read(const char* path, char** out_contents, int* out_size) { static int metainfo_read_file(const char* path, char** out_contents, int* out_size) {
int ret = 0; int ret = 0;
FILE* f = NULL; FILE* f = NULL;
long size = 0; long size = 0;
@ -67,6 +79,146 @@ end:
return ret; return ret;
} }
#ifdef HTTP_TORRENT
static size_t metainfo_read_http_headercb(char *buf, size_t size, size_t n,
void *data) {
struct http_metainfo *h_meta = (struct http_metainfo*)data;
const char *cl_header = "content-length";
char *sep = memchr(buf, ':', size * n);
if (!sep || (sep - buf) != strlen(cl_header))
goto end;
if (strncmp(cl_header, buf, strlen(cl_header)) == 0) {
char *endp;
int64_t len;
sep += 2;
errno = 0;
len = strtoll(sep, &endp, 10);
if (sep != endp && errno == 0)
h_meta->max_size = len;
}
end:
return size * n;
}
static size_t metainfo_read_http_writecb(char *ptr, size_t size, size_t n,
void *data) {
struct http_metainfo *h_meta = (struct http_metainfo*)data;
size_t bytes = size * n;
if (h_meta->max_size >= MAX_TORRENT_SIZE)
goto fail; /* Stop processing if too large */
if (h_meta->max_size == -1) {
/* If no content-length, make a dinamic array */
h_meta->max_size = 0;
} else if (!h_meta->data) {
/* We have content-size, and we haven't alloced yet */
h_meta->data = malloc(h_meta->max_size);
}
size_t free_space = h_meta->max_size - h_meta->c_size;
if (bytes > free_space) {
while (bytes > free_space) {
if (h_meta->max_size == 0)
h_meta->max_size = 2048;
h_meta->max_size *= 2;
free_space = h_meta->max_size - h_meta->c_size;
}
void *n_data = realloc(h_meta->data, h_meta->max_size);
if (!n_data)
goto fail;
h_meta->data = n_data;
}
if (h_meta->c_size + bytes > MAX_TORRENT_SIZE)
goto fail;
memcpy(&h_meta->data[h_meta->c_size], ptr, bytes);
h_meta->c_size += bytes;
return bytes;
fail:
if (h_meta->data)
free(h_meta->data);
return 0;
}
#endif
/*
* Download the file in memory, and return the pointer to it (which needs to be
* freed) in out_contents and the size in out_size. If the file is too big,
* fail. Returns 0 on success and an errno on fail.
*/
static int metainfo_read_http(const char* url, char** out_contents, int* out_size) {
#ifdef HTTP_TORRENT
char errbuf[CURL_ERROR_SIZE];
CURL *curl = curl_easy_init();
CURLcode res;
struct http_metainfo h_meta = {
.c_size = 0,
.max_size = -1,
.data = NULL,
};
if (!curl) {
return -1; /* Not errno but eh */
}
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &h_meta);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &h_meta);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, metainfo_read_http_headercb);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metainfo_read_http_writecb);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
curl_easy_setopt(curl, CURLOPT_URL, url);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res) {
fprintf(stderr, "libCurl error: %s\n", errbuf);
return res;
}
*out_contents = h_meta.data;
*out_size = h_meta.c_size; /* caller will free */
return 0;
#else
__builtin_unreachable(); /* Could be better tbh */
#endif
}
/*
* Read/download the file in memory, and return the pointer to it (which needs to be
* freed) in out_contents and the size in out_size. If the file is too big,
* fail. Returns 0 on success and an errno on fail.
*/
static int metainfo_read(const char* path, char** out_contents, int* out_size) {
#ifdef HTTP_TORRENT
const int has_http = 1;
#else
const int has_http = 0;
#endif
if (strncmp(path, "http", 4) == 0) {
if (!has_http)
return ENOPROTOOPT;
return metainfo_read_http(path, out_contents, out_size);
}
return metainfo_read_file(path, out_contents, out_size);
}
static int len_strcmp(const char* s1, int s1_len, const char* s2, int s2_len) { static int len_strcmp(const char* s1, int s1_len, const char* s2, int s2_len) {
return (s1_len == s2_len) && (strncmp(s1, s2, s1_len) == 0); return (s1_len == s2_len) && (strncmp(s1, s2, s1_len) == 0);
} }
@ -173,8 +325,8 @@ static int metainfo_parse(metainfo_t* metai, bencode_t* benc) {
} }
int metainfo_create(metainfo_t* metai, const char* path) { int metainfo_create(metainfo_t* metai, const char* path) {
char* bytes; char* bytes = NULL;
int size; int size = 0;
int ret = metainfo_read(path, &bytes, &size); int ret = metainfo_read(path, &bytes, &size);
if (ret) { if (ret) {
fprintf(stderr, "Metafile reading failed: %s\n", \ fprintf(stderr, "Metafile reading failed: %s\n", \