├── .gitignore ├── LICENSE.txt ├── Makefile ├── README.md ├── img ├── bitfiend.png └── session.png └── src ├── libbf ├── bencode.c ├── bencode.h ├── bitfiend.c ├── bitfiend.h ├── bitfiend_internal.h ├── byte_str.c ├── byte_str.h ├── dict.c ├── dict.h ├── dl_file.c ├── dl_file.h ├── lbitfield.h ├── list.c ├── list.h ├── log.c ├── log.h ├── peer.h ├── peer_connection.c ├── peer_connection.h ├── peer_id.c ├── peer_id.h ├── peer_listener.c ├── peer_listener.h ├── peer_msg.c ├── peer_msg.h ├── piece_request.c ├── piece_request.h ├── queue.c ├── queue.h ├── sha1.c ├── sha1.h ├── stats.c ├── stats.h ├── thread_reaper.c ├── thread_reaper.h ├── torrent.c ├── torrent.h ├── torrent_file.c ├── torrent_file.h ├── tracker_announce.c ├── tracker_announce.h ├── tracker_connection.c ├── tracker_connection.h ├── tracker_http.c ├── tracker_http.h ├── tracker_resp_parser.c ├── tracker_resp_parser.h ├── tracker_udp.c ├── tracker_udp.h ├── url.c └── url.h └── ui └── cli ├── commands.c ├── commands.h ├── main.c ├── printclr.c └── printclr.h /.gitignore: -------------------------------------------------------------------------------- 1 | /obj/ 2 | /bin/ 3 | /lib/ 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | AR=ar 3 | BIN=./bin/bitfiend 4 | LIB=./lib/libbf.a 5 | 6 | ########## LIBBF ############################################################### 7 | 8 | libbf: CFLAGS=-std=gnu99 -g 9 | libbf: DEFS=-D_FILE_OFFSET_BITS=64 -D__USE_BSD -D_GNU_SOURCE 10 | 11 | LIBBF_SRCS=$(wildcard ./src/libbf/*.c) 12 | LIBBF_OBJS=$(LIBBF_SRCS:./src/%.c=./obj/%.o) 13 | LIBBF_DEPS=$(LIBBF_OBJS:%.o=%.d) 14 | 15 | ./obj/libbf/%.o: ./src/libbf/%.c 16 | @mkdir -p ./obj/libbf 17 | $(CC) -MT $@ -MMD -MP -MF ./obj/libbf/$*.d $(CFLAGS) $(DEFS) -c $< -o $@ 18 | 19 | libbf: $(LIBBF_OBJS) 20 | @mkdir -p ./lib 21 | $(AR) rcs $(LIB) $(LIBBF_OBJS) 22 | 23 | -include $(LIBBF_DEPS) 24 | 25 | ########## CLI ################################################################# 26 | 27 | bfcli: CFLAGS=-std=gnu99 -pthread -g 28 | bfcli: LDFLAGS=-L./lib -lbf -lm -lrt 29 | bfcli: INCLUDE=-I./src/ 30 | 31 | BFCLI_SRCS=$(wildcard ./src/ui/cli/*.c) 32 | BFCLI_OBJS=$(BFCLI_SRCS:./src/%.c=./obj/%.o) 33 | BFCLI_DEPS=$(BFCLI_OBJS:%.o=%.d) 34 | 35 | ./obj/ui/cli/%.o: ./src/ui/cli/%.c 36 | @mkdir -p ./obj/ui/cli 37 | $(CC) -MT $@ -MMD -MP -MF ./obj/ui/cli/$*.d $(INCLUDE) $(CFLAGS) -c $< -o $@ 38 | 39 | bfcli: $(BFCLI_OBJS) libbf 40 | @mkdir -p ./bin 41 | $(CC) $(CFLAGS) $(BFCLI_OBJS) -o $(BIN) $(LDFLAGS) 42 | 43 | -include $(BFCLI_DEPS) 44 | 45 | ################################################################################ 46 | 47 | .PHONY: clean 48 | clean: 49 | @rm -f $(LIBBF_OBJS) $(LIBBF_DEPS) $(BFCLI_OBJS) $(BFCLI_DEPS) $(BIN) $(LIB) 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ## 2 | 3 | BitFiend is a BitTorrent client written in C. It is multithreaded with POSIX threads. 4 | It can be built for Linux and has a command line interface. 5 | 6 | The client has successfully torrented Ubuntu ISOs, academic torrents, and various 7 | multimedia files from popular torrent websites. It should work with most torrents! 8 | 9 | At the moment, the client still lacks some non-essential features. I plan to 10 | continue to extend its' functionality in the future. 11 | 12 | ## Building BitFiend ## 13 | 14 | To build the command-line client, run `make bfcli` in the top-level directory. 15 | 16 | To build the core of BitFiend as a static library, run `make libbf` in the top-level 17 | directory. The library API can be found in `libbf/bitfiend.h` 18 | 19 | BitFiend does not depend on any third party libraries. It requires a C99 compiler with 20 | GNU extensions as well as POSIX compliance. Some non-portable Linux-specific code is 21 | present. 22 | 23 | ## Example BitFiend Session ## 24 | 25 | 26 | -------------------------------------------------------------------------------- /img/bitfiend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eduard-permyakov/BitFiend/76596841533afc26271d8f27deab79a923f99c79/img/bitfiend.png -------------------------------------------------------------------------------- /img/session.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eduard-permyakov/BitFiend/76596841533afc26271d8f27deab79a923f99c79/img/session.png -------------------------------------------------------------------------------- /src/libbf/bencode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "bencode.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | static bencode_obj_t *bencode_parse_string(const char *benc, const char **endptr); 30 | static bencode_obj_t *bencode_parse_int(const char *benc, const char **endptr); 31 | static bencode_obj_t *bencode_parse_dict(const char *benc, const char **endptr); 32 | static bencode_obj_t *bencode_parse_list(const char *benc, const char **endptr); 33 | void print_obj(bencode_obj_t *obj); //TEMP 34 | 35 | 36 | static bencode_obj_t *bencode_obj_create(void) 37 | { 38 | bencode_obj_t *ret = malloc(sizeof(bencode_obj_t)); 39 | memset(ret->sha1, 0, DIGEST_LEN); 40 | return ret; 41 | } 42 | 43 | static void *bencode_obj_free(bencode_obj_t *obj) 44 | { 45 | free(obj); 46 | } 47 | 48 | void bencode_free_obj_and_data_recursive(bencode_obj_t *obj) 49 | { 50 | switch(obj->type) { 51 | case BENCODE_TYPE_STRING: 52 | byte_str_free(obj->data.string); 53 | free(obj); 54 | break; 55 | case BENCODE_TYPE_INT: 56 | free(obj); 57 | break; 58 | case BENCODE_TYPE_LIST: { 59 | const unsigned char *entry; 60 | 61 | FOREACH_ENTRY(entry, obj->data.list) { 62 | bencode_free_obj_and_data_recursive(*((bencode_obj_t**)entry)); 63 | } 64 | 65 | list_free(obj->data.list); 66 | free(obj); 67 | break; 68 | } 69 | case BENCODE_TYPE_DICT: { 70 | const char *key; 71 | const unsigned char *val; 72 | 73 | FOREACH_KEY_AND_VAL(key, val, obj->data.dictionary) { 74 | bencode_free_obj_and_data_recursive(*((bencode_obj_t**)val)); 75 | } 76 | 77 | dict_free(obj->data.dictionary); 78 | free(obj); 79 | break; 80 | } 81 | } 82 | } 83 | 84 | bencode_obj_t *bencode_parse_object(const char *benc, const char **endptr) 85 | { 86 | if(isdigit(benc[0])) 87 | return bencode_parse_string(benc, endptr); 88 | else if (benc[0] == 'i') 89 | return bencode_parse_int(benc, endptr); 90 | else if(benc[0] == 'l') 91 | return bencode_parse_list(benc, endptr); 92 | else if(benc[0] == 'd') 93 | return bencode_parse_dict(benc, endptr); 94 | 95 | return NULL; 96 | } 97 | 98 | static bencode_obj_t *bencode_parse_string(const char *benc, const char **endptr) 99 | { 100 | long strl; 101 | bencode_obj_t *ret; 102 | *endptr = benc; 103 | 104 | strl = strtol(benc, (char**)endptr, 10); 105 | assert(**endptr == ':'); 106 | (*endptr)++; 107 | 108 | ret = bencode_obj_create(); 109 | assert(ret); 110 | 111 | ret->type = BENCODE_TYPE_STRING; 112 | ret->data.string = byte_str_new(strl, *endptr); 113 | assert(ret->data.string); 114 | 115 | *endptr += strl; 116 | return ret; 117 | } 118 | 119 | static bencode_obj_t *bencode_parse_int(const char *benc, const char **endptr) 120 | { 121 | long i; 122 | bencode_obj_t *ret; 123 | *endptr = benc; 124 | 125 | assert(*benc == 'i'); 126 | benc++; 127 | 128 | i = strtol(benc, (char**)endptr, 10); 129 | assert(**endptr == 'e'); 130 | (*endptr)++; 131 | 132 | ret = bencode_obj_create(); 133 | assert(ret); 134 | 135 | ret->type = BENCODE_TYPE_INT; 136 | ret->data.integer = i; 137 | return ret; 138 | } 139 | 140 | static bencode_obj_t *bencode_parse_dict(const char *benc, const char **endptr) 141 | { 142 | bencode_obj_t *ret; 143 | 144 | assert(*benc == 'd'); 145 | *endptr = benc + 1; 146 | 147 | ret = bencode_obj_create(); 148 | ret->type = BENCODE_TYPE_DICT; 149 | /* Dict of size 1 (bin) will preserve the alphabetical ordering of the keys for iteration */ 150 | ret->data.dictionary = dict_init(1); 151 | assert(ret->data.dictionary); 152 | 153 | while(**endptr != 'e') { 154 | benc = *endptr; 155 | bencode_obj_t *key = bencode_parse_string(benc, endptr); 156 | assert(key); 157 | assert(*endptr > benc); 158 | 159 | benc = *endptr; 160 | bencode_obj_t *value = bencode_parse_object(benc, endptr); 161 | assert(value); 162 | assert(*endptr > benc); 163 | 164 | if(!strcmp((char*)key->data.string->str, "info")) { 165 | assert(benc[0] == 'd'); 166 | assert((*endptr)[-1] == 'e'); 167 | sha1_compute(benc, *endptr - benc, value->sha1); 168 | } 169 | 170 | dict_add(ret->data.dictionary, (char*)key->data.string->str, 171 | (unsigned char*)&value, sizeof(value)); 172 | 173 | bencode_free_obj_and_data_recursive(key); 174 | } 175 | 176 | assert(**endptr == 'e'); 177 | (*endptr)++; 178 | 179 | return ret; 180 | } 181 | 182 | static bencode_obj_t *bencode_parse_list(const char *benc, const char **endptr) 183 | { 184 | bencode_obj_t *ret; 185 | 186 | assert(*benc == 'l'); 187 | *endptr = benc + 1; 188 | 189 | ret = bencode_obj_create(); 190 | assert(ret); 191 | ret->type = BENCODE_TYPE_LIST; 192 | ret->data.list = list_init(); 193 | assert(ret->data.list); 194 | 195 | while(**endptr != 'e') { 196 | benc = *endptr; 197 | bencode_obj_t *elem= bencode_parse_object(benc, endptr); 198 | assert(elem); 199 | 200 | list_add(ret->data.list, (unsigned char*)&elem, sizeof(elem)); 201 | } 202 | 203 | assert(**endptr == 'e'); 204 | (*endptr)++; 205 | 206 | return ret; 207 | } 208 | 209 | // TEST BEGIN 210 | 211 | void print_obj(bencode_obj_t *obj) 212 | { 213 | switch(obj->type) { 214 | case BENCODE_TYPE_INT: 215 | printf("Int: %ld\n", obj->data.integer); 216 | break; 217 | case BENCODE_TYPE_DICT: { 218 | 219 | const char *key; 220 | const unsigned char *val; 221 | 222 | printf("Dict: \n"); 223 | FOREACH_KEY_AND_VAL(key, val, obj->data.dictionary) { 224 | printf("Key: %s\n", key); 225 | print_obj(*((bencode_obj_t**)val)); 226 | } 227 | break; 228 | } 229 | case BENCODE_TYPE_LIST: { 230 | const unsigned char *entry; 231 | printf("List: \n"); 232 | FOREACH_ENTRY(entry, obj->data.list) { 233 | printf(" "); 234 | print_obj(*((bencode_obj_t**)entry)); 235 | } 236 | break; 237 | } 238 | case BENCODE_TYPE_STRING: 239 | printf("String: %p\n", obj->data.string); 240 | break; 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/libbf/bencode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef BENCODE_H 21 | #define BENCODE_H 22 | 23 | #include "byte_str.h" 24 | #include "list.h" 25 | #include "dict.h" 26 | #include "sha1.h" 27 | 28 | #include 29 | 30 | typedef enum { 31 | BENCODE_TYPE_STRING, 32 | BENCODE_TYPE_INT, 33 | BENCODE_TYPE_LIST, 34 | BENCODE_TYPE_DICT 35 | }bencode_type_t; 36 | 37 | typedef struct bencode_obj { 38 | bencode_type_t type; 39 | union { 40 | byte_str_t *string; 41 | int64_t integer; 42 | list_t *list; 43 | dict_t *dictionary; 44 | }data; 45 | unsigned char sha1[DIGEST_LEN]; 46 | }bencode_obj_t; 47 | 48 | bencode_obj_t *bencode_parse_object(const char *benc, const char **endptr); 49 | void bencode_free_obj_and_data_recursive(bencode_obj_t *obj); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/libbf/bitfiend.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "bitfiend.h" 21 | #include "list.h" 22 | #include "peer_id.h" 23 | #include "peer_listener.h" 24 | #include "bencode.h" 25 | #include "torrent.h" 26 | #include "torrent_file.h" 27 | #include "log.h" 28 | #include "peer_connection.h" 29 | #include "thread_reaper.h" 30 | #include "stats.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | static pthread_t s_peer_listener; 43 | static pthread_t s_reaper; 44 | static const uint16_t s_port = 6889; 45 | static pthread_mutex_t s_torrents_lock = PTHREAD_MUTEX_INITIALIZER; 46 | static list_t *s_torrents; 47 | /* Threads for incoming peer connections which have been created but not yet associated 48 | * with a particular torrent, which can't be done until after handshaking. Store their handles 49 | * here for now, until a torrent_t associates with it and removes the handle from this list */ 50 | static pthread_mutex_t s_unassoc_peerthreads_lock = PTHREAD_MUTEX_INITIALIZER; 51 | static list_t *s_unassoc_peerthreads; 52 | 53 | static FILE *s_logfile; 54 | 55 | int bitfiend_init(const char *logfile) 56 | { 57 | s_logfile = fopen(logfile, "a"); 58 | if(!s_logfile) 59 | return BITFIEND_FAILURE; 60 | 61 | log_set_lvl(LOG_LEVEL_DEBUG); 62 | log_set_logfile(s_logfile); 63 | 64 | peer_id_create(g_local_peer_id); 65 | 66 | s_torrents = list_init(); 67 | s_unassoc_peerthreads = list_init(); 68 | 69 | if(stats_init()) 70 | goto fail_init; 71 | 72 | if(peer_listener_create(&s_peer_listener, &s_port)) 73 | goto fail_init; 74 | 75 | reaper_arg_t *arg = malloc(sizeof(reaper_arg_t)); 76 | if(!arg) 77 | goto fail_init; 78 | arg->reap_interval = 5; 79 | arg->torrents = s_torrents; 80 | arg->torrents_lock = &s_torrents_lock; 81 | arg->unassoc_peers = s_unassoc_peerthreads; 82 | arg->unassoc_peer_lock = &s_unassoc_peerthreads_lock; 83 | if(thread_reaper_create(&s_reaper, arg)){ 84 | free(arg); 85 | goto fail_init; 86 | } 87 | 88 | log_printf(LOG_LEVEL_INFO, "BitFiend init successful\n"); 89 | return BITFIEND_SUCCESS; 90 | 91 | fail_init: 92 | log_printf(LOG_LEVEL_ERROR, "BitFiend init error\n"); 93 | return BITFIEND_FAILURE; 94 | } 95 | 96 | static int shutdown_torrent(torrent_t *torrent) 97 | { 98 | pthread_cancel(torrent->tracker_thread); 99 | 100 | const unsigned char *entry; 101 | void *tret; 102 | pthread_join(torrent->tracker_thread, &tret); 103 | assert(tret == PTHREAD_CANCELED); 104 | 105 | pthread_mutex_lock(&torrent->sh_lock); 106 | const list_iter_t *iter = list_iter_first(torrent->sh.peer_connections); 107 | pthread_mutex_unlock(&torrent->sh_lock); 108 | 109 | while(iter){ 110 | peer_conn_t *conn = *(peer_conn_t**)(list_iter_get_value(iter)); 111 | void *ret; 112 | 113 | pthread_cancel(conn->thread); 114 | /* Interrupt any blocking call the peer thread may be waiting on 115 | * While they should time out, this results in consistently fast shutdown of the client */ 116 | pthread_kill(conn->thread, SIGINT); 117 | pthread_join(conn->thread, &ret); 118 | 119 | pthread_mutex_lock(&torrent->sh_lock); 120 | iter = list_iter_next(iter); 121 | pthread_mutex_unlock(&torrent->sh_lock); 122 | } 123 | 124 | torrent_free(torrent); 125 | stats_remove_entry(torrent); 126 | return BITFIEND_SUCCESS; 127 | 128 | fail_stop_peer: 129 | fail_stop_tracker: 130 | return BITFIEND_FAILURE; 131 | } 132 | 133 | int bitfiend_shutdown(void) 134 | { 135 | int ret = BITFIEND_SUCCESS; 136 | const unsigned char *entry; 137 | void *tret; 138 | 139 | /* Thread join order matters here. 140 | * First, join the peer_listener so no new unassociated peers can be added. 141 | * Next, join unassociated peers, after which a torrent's peer_connections 142 | * list can only grow if its' tracker_thread gives it peers. 143 | * Now, iterate over all torrents. Join the tracker thread first. Now no new peer 144 | * threads can be spawned which can touch the torrent. Join the torrent's peers last. 145 | */ 146 | 147 | if(pthread_cancel(s_reaper)) 148 | ret = BITFIEND_FAILURE; 149 | pthread_join(s_reaper, &tret); 150 | 151 | if(pthread_cancel(s_peer_listener)) 152 | ret = BITFIEND_FAILURE; 153 | pthread_join(s_peer_listener, &tret); 154 | 155 | size_t listsize; 156 | pthread_mutex_lock(&s_unassoc_peerthreads_lock); 157 | listsize = list_get_size(s_unassoc_peerthreads); 158 | pthread_mutex_unlock(&s_unassoc_peerthreads_lock); 159 | log_printf(LOG_LEVEL_DEBUG, "Cancelling and joining unassociated peer threads. There are %zu\n", 160 | listsize); 161 | 162 | const list_iter_t *iter = NULL; 163 | pthread_t curr; 164 | do { 165 | /* Remove one entry at a time from the list head. This is so we are not holding the 166 | * list lock while we are joining the thread in the list, since the thread being 167 | * joined can also hold the lock and remove an entry from the list */ 168 | pthread_mutex_lock(&s_unassoc_peerthreads_lock); 169 | iter = list_iter_first(s_unassoc_peerthreads); 170 | if(iter){ 171 | curr = *(pthread_t*)list_iter_get_value(iter); 172 | list_remove(s_unassoc_peerthreads, (unsigned char*)&curr); 173 | } 174 | pthread_mutex_unlock(&s_unassoc_peerthreads_lock); 175 | 176 | if(iter){ 177 | pthread_cancel(curr); 178 | pthread_join(curr, NULL); 179 | } 180 | 181 | }while(iter); 182 | 183 | pthread_mutex_lock(&s_unassoc_peerthreads_lock); 184 | list_free(s_unassoc_peerthreads); 185 | pthread_mutex_unlock(&s_unassoc_peerthreads_lock); 186 | 187 | pthread_mutex_lock(&s_torrents_lock); 188 | FOREACH_ENTRY(entry, s_torrents) { 189 | shutdown_torrent(*(torrent_t**)entry); 190 | } 191 | list_free(s_torrents); 192 | pthread_mutex_unlock(&s_torrents_lock); 193 | 194 | stats_shutdown(); 195 | 196 | if(ret == BITFIEND_SUCCESS) 197 | log_printf(LOG_LEVEL_INFO, "BitFiend shutdown successful\n"); 198 | else 199 | log_printf(LOG_LEVEL_INFO, "BitFiend shutdown error\n"); 200 | 201 | fclose(s_logfile); 202 | 203 | return ret; 204 | } 205 | 206 | bf_htorrent_t *bitfiend_add_torrent(const char *metafile, const char *destdir) 207 | { 208 | char metacopy[strlen(metafile) + 1]; 209 | strcpy(metacopy, metafile); 210 | 211 | char *saveptr, *token, *next; 212 | token = strtok_r(metacopy, "/", &saveptr); 213 | while(next = strtok_r(NULL, "/", &saveptr)) { 214 | token = next; 215 | } 216 | /* Now token points to the filename */ 217 | char *trim = strstr(token, ".torrent"); 218 | if(trim) 219 | *trim = '\0'; 220 | 221 | bencode_obj_t *obj = torrent_file_parse(metafile); 222 | if(!obj) 223 | goto fail_parse; 224 | 225 | torrent_t *torrent = torrent_init(obj, token, destdir); 226 | 227 | //extern void print_torrent(torrent_t *torrent); 228 | //print_torrent(torrent); 229 | 230 | bencode_free_obj_and_data_recursive(obj); 231 | if(!torrent) 232 | goto fail_create; 233 | 234 | stats_add_entry(torrent); 235 | 236 | tracker_arg_t *arg = malloc(sizeof(tracker_arg_t)); 237 | arg->torrent = torrent; 238 | arg->port = s_port; 239 | if(tracker_connection_create(&torrent->tracker_thread, arg)) 240 | goto fail_create; 241 | 242 | pthread_mutex_lock(&s_torrents_lock); 243 | list_add(s_torrents, (unsigned char*)&torrent, sizeof(torrent_t*)); 244 | pthread_mutex_unlock(&s_torrents_lock); 245 | 246 | log_printf(LOG_LEVEL_INFO, "Torrent added successfully: %s\n", metafile); 247 | return torrent; 248 | 249 | fail_create: 250 | fail_parse: 251 | log_printf(LOG_LEVEL_ERROR, "Error adding torrent: %s\n", metafile); 252 | return NULL; 253 | } 254 | 255 | int bitfiend_remove_torrent(bf_htorrent_t *torrent) 256 | { 257 | pthread_mutex_lock(&s_torrents_lock); 258 | list_remove(s_torrents, (unsigned char*)&torrent); 259 | pthread_mutex_unlock(&s_torrents_lock); 260 | 261 | shutdown_torrent((torrent_t*)torrent); 262 | } 263 | 264 | int bitfiend_stat_torrent(bf_htorrent_t *torrent, bf_stat_t *out) 265 | { 266 | torrent_t *ptr = (torrent_t*)torrent; 267 | 268 | out->name = ptr->name; 269 | out->tot_pieces = dict_get_size(ptr->pieces); 270 | pthread_mutex_lock(&ptr->sh_lock); 271 | out->pieces_left = ptr->sh.pieces_left; 272 | pthread_mutex_unlock(&ptr->sh_lock); 273 | 274 | out->tot_uploaded = stats_up_total(ptr); 275 | out->tot_downloaded = stats_down_total(ptr); 276 | out->avg_uprate = stats_up_avgrate(ptr); 277 | out->avg_downrate = stats_down_avgrate(ptr); 278 | out->inst_uprate = stats_up_instrate(ptr); 279 | out->inst_downrate = stats_down_instrate(ptr); 280 | } 281 | 282 | void bitfiend_foreach_torrent(void (*func)(bf_htorrent_t *torrent, void *arg), void *arg) 283 | { 284 | const unsigned char *entry; 285 | 286 | pthread_mutex_lock(&s_torrents_lock); 287 | const list_iter_t *iter = list_iter_first(s_torrents); 288 | pthread_mutex_unlock(&s_torrents_lock); 289 | 290 | while(iter) { 291 | const list_iter_t *next; 292 | 293 | pthread_mutex_lock(&s_torrents_lock); 294 | next = list_iter_next(iter); 295 | pthread_mutex_unlock(&s_torrents_lock); 296 | 297 | torrent_t *torrent = *((torrent_t**)list_iter_get_value(iter)); 298 | func(torrent, arg); 299 | 300 | iter = next; 301 | } 302 | } 303 | 304 | torrent_t *bitfiend_assoc_peer(peer_conn_t *peer, char infohash[20]) 305 | { 306 | const unsigned char *entry; 307 | torrent_t *ret = NULL; 308 | 309 | pthread_mutex_lock(&s_torrents_lock); 310 | FOREACH_ENTRY(entry, s_torrents) { 311 | torrent_t *torrent = *(torrent_t**)entry; 312 | 313 | if(!memcmp(torrent->info_hash, infohash, sizeof(torrent->info_hash))) { 314 | 315 | pthread_mutex_lock(&torrent->sh_lock); 316 | unsigned num_conns = list_get_size(torrent->sh.peer_connections); 317 | if(num_conns == torrent->max_peers){ 318 | pthread_mutex_unlock(&torrent->sh_lock); 319 | ret = NULL; 320 | break; 321 | } 322 | pthread_mutex_unlock(&torrent->sh_lock); 323 | 324 | pthread_mutex_lock(&s_unassoc_peerthreads_lock); 325 | /* Handle the case if we've already been "chosen" to be joined by the main thread */ 326 | if(!list_contains(s_unassoc_peerthreads, (unsigned char*)&peer->thread)){ 327 | pthread_mutex_unlock(&s_unassoc_peerthreads_lock); 328 | break; 329 | } 330 | list_remove(s_unassoc_peerthreads, (unsigned char*)&peer->thread); 331 | pthread_mutex_unlock(&s_unassoc_peerthreads_lock); 332 | 333 | pthread_mutex_lock(&torrent->sh_lock); 334 | list_add(torrent->sh.peer_connections, (unsigned char*)&peer, sizeof(peer_conn_t*)); 335 | ret = torrent; 336 | pthread_mutex_unlock(&torrent->sh_lock); 337 | 338 | log_printf(LOG_LEVEL_INFO, "Associated incoming peer connection with torrent\n"); 339 | 340 | break; 341 | } 342 | 343 | } 344 | pthread_mutex_unlock(&s_torrents_lock); 345 | 346 | return ret; 347 | } 348 | 349 | void bitfiend_add_unassoc_peer(pthread_t thread) 350 | { 351 | pthread_mutex_lock(&s_unassoc_peerthreads_lock); 352 | list_add(s_unassoc_peerthreads, (unsigned char*)&thread, sizeof(pthread_t)); 353 | pthread_mutex_unlock(&s_unassoc_peerthreads_lock); 354 | } 355 | 356 | int bitfiend_notify_peers_have(torrent_t *torrent, unsigned have_index) 357 | { 358 | int ret = 0; 359 | const unsigned char *entry; 360 | pthread_mutex_lock(&torrent->sh_lock); 361 | 362 | FOREACH_ENTRY(entry, torrent->sh.peer_connections) { 363 | peer_conn_t *conn = *(peer_conn_t**)entry; 364 | 365 | if(pthread_equal(conn->thread, pthread_self()) == 0) 366 | continue; 367 | 368 | char queue_name[64]; 369 | peer_connection_queue_name(conn->thread, queue_name, sizeof(queue_name)); 370 | mqd_t queue = mq_open(queue_name, O_WRONLY | O_NONBLOCK); 371 | if(queue != (mqd_t)-1) { 372 | if(mq_send(queue, (char*)&have_index, sizeof(unsigned), 0) && errno != EAGAIN) { 373 | log_printf(LOG_LEVEL_ERROR, "Failed to send have event to peer threads\n"); 374 | } 375 | mq_close(queue); 376 | }else{ 377 | ret = -1; 378 | log_printf(LOG_LEVEL_ERROR, "Could not open queue for sending: %s\n", queue_name); 379 | } 380 | } 381 | 382 | pthread_mutex_unlock(&torrent->sh_lock); 383 | 384 | return ret; 385 | } 386 | 387 | -------------------------------------------------------------------------------- /src/libbf/bitfiend.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef BITFIEND_H 21 | #define BITFIEND_H 22 | 23 | #define LIBBF_VER_MAJOR 0 24 | #define LIBBF_VER_MINOR 1 25 | #define LIBBF_VER_PATCH 0 26 | 27 | enum{ 28 | BITFIEND_FAILURE = -1, 29 | BITFIEND_SUCCESS 30 | }; 31 | 32 | typedef struct bf_stat { 33 | const char *name; 34 | unsigned tot_pieces; 35 | unsigned pieces_left; 36 | unsigned long tot_uploaded; /* bytes */ 37 | unsigned long tot_downloaded; /* bytes */ 38 | double avg_uprate; /* bits/sec */ 39 | double avg_downrate; /* bits/sec */ 40 | double inst_uprate; /* bits/sec */ 41 | double inst_downrate; /* bits/sec */ 42 | }bf_stat_t; 43 | 44 | typedef void bf_htorrent_t; 45 | 46 | int bitfiend_init(const char *logfile); 47 | int bitfiend_shutdown(void); 48 | 49 | bf_htorrent_t *bitfiend_add_torrent(const char *metafile, const char *destdir); 50 | int bitfiend_remove_torrent(bf_htorrent_t *torrent); 51 | int bitfiend_stat_torrent(bf_htorrent_t *torrent, bf_stat_t *out); 52 | 53 | void bitfiend_foreach_torrent(void (*func)(bf_htorrent_t *torrent, void *arg), void *arg); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/libbf/bitfiend_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef BITFIEND_INTERNAL_H 21 | #define BITFIEND_INTERNAL_H 22 | 23 | #include "torrent.h" 24 | #include 25 | 26 | torrent_t *bitfiend_assoc_peer(peer_conn_t *peer, char infohash[20]); 27 | void bitfiend_add_unassoc_peer(pthread_t thead); 28 | int bitfiend_notify_peers_have(torrent_t *torrent, unsigned have_index); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/libbf/byte_str.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "byte_str.h" 21 | 22 | #include 23 | #include 24 | 25 | byte_str_t *byte_str_new(size_t size, const unsigned char *str) 26 | { 27 | byte_str_t *ret; 28 | ret = malloc(sizeof(byte_str_t) + size + 1); 29 | if(ret) { 30 | memcpy(ret->str, str, size); 31 | /* NULL-terminate all data so this type is suitable for 32 | * storing ASCII data also 33 | */ 34 | ret->str[size] = '\0'; 35 | ret->size = size; 36 | } 37 | return ret; 38 | } 39 | 40 | void byte_str_free(byte_str_t *str) 41 | { 42 | free(str); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/libbf/byte_str.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef BYTE_STR_H 21 | #define BYTE_STR_H 22 | 23 | #include 24 | 25 | typedef struct byte_str{ 26 | size_t size; 27 | unsigned char str[]; 28 | }byte_str_t; 29 | 30 | byte_str_t *byte_str_new(size_t size, const unsigned char *str); 31 | void byte_str_free(byte_str_t *str); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/libbf/dict.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "dict.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define FOREACH_ENTRY_IN_BIN(_entry, _dict_ptr, _bin) \ 30 | for(dict_entry_t *_entry = _dict_ptr->bins[_bin]; _entry; _entry = _entry->next) 31 | 32 | #define FOREACH_ENTRY_AND_PREV_IN_BIN(_entry, _prev, _dict_ptr, _bin) \ 33 | for(dict_entry_t *_entry = _dict_ptr->bins[_bin], *_prev = NULL; _entry; \ 34 | _prev = _entry, _entry = _entry->next) 35 | 36 | #define SET_TO_LAST_ENTRY(entry_ptr, dict_ptr, bin) \ 37 | do { \ 38 | entry_ptr = dict_ptr->bins[bin]; \ 39 | while(entry_ptr->next) \ 40 | entry_ptr = entry_ptr->next; \ 41 | }while(0) 42 | 43 | 44 | typedef struct dict_entry{ 45 | struct dict_entry *next; 46 | char *key; 47 | size_t size; 48 | unsigned char value[]; 49 | }dict_entry_t; 50 | 51 | struct dict{ 52 | unsigned size; 53 | unsigned binsize; 54 | dict_entry_t **bins; 55 | }; 56 | 57 | // DICT ENTRY START 58 | 59 | static dict_entry_t *dict_entry_init(const char *key, void *value, size_t size) 60 | { 61 | dict_entry_t *ret = malloc(sizeof(dict_entry_t) + size); 62 | if(ret) { 63 | ret->key = malloc(strlen(key) + 1); 64 | if(!ret->key) { 65 | free(ret); 66 | return NULL; 67 | } 68 | memcpy(ret->key, key, strlen(key) + 1); 69 | ret->size = size; 70 | ret->next = NULL; 71 | memcpy(ret->value, value, size); 72 | } 73 | return ret; 74 | } 75 | 76 | static void dict_entry_free(dict_entry_t *entry) 77 | { 78 | free(entry->key); 79 | free(entry); 80 | } 81 | 82 | // DICT ENTRY END 83 | 84 | static unsigned hashf(size_t binsize, const char *key) 85 | { 86 | long sum = 0; 87 | const char *cursor = key; 88 | size_t keylen = strlen(key); 89 | 90 | /* Sum string as sequence of integers */ 91 | while(cursor < key + keylen) { 92 | int tmp = 0; 93 | int len = (cursor + sizeof(int) > key + keylen) ? (key + keylen - cursor) : sizeof(int); 94 | memcpy(&tmp, cursor, len); 95 | sum += tmp; 96 | cursor += len; 97 | } 98 | 99 | return abs(sum) % binsize; 100 | } 101 | 102 | dict_t *dict_init(size_t binsize) 103 | { 104 | dict_t *ret = malloc(sizeof(dict_t)); 105 | if(ret) { 106 | ret->size = 0; 107 | ret->binsize = binsize; 108 | ret->bins = calloc(binsize, sizeof(dict_entry_t*)); 109 | if(!ret->bins) { 110 | free(ret); 111 | ret = NULL; 112 | } 113 | } 114 | return ret; 115 | } 116 | 117 | void dict_free(dict_t *dict) 118 | { 119 | for(unsigned i = 0; i < dict->binsize; i++){ 120 | FOREACH_ENTRY_AND_PREV_IN_BIN(entry, prev, dict, i) { 121 | if(prev){ 122 | dict_entry_free(prev); 123 | } 124 | if(!entry->next){ 125 | dict_entry_free(entry); 126 | break; 127 | } 128 | } 129 | } 130 | free(dict->bins); 131 | free(dict); 132 | } 133 | 134 | int dict_add(dict_t *dict, const char *key, unsigned char *data, size_t size) 135 | { 136 | unsigned hash = hashf(dict->binsize, key); 137 | dict_entry_t *entry = dict_entry_init(key, data, size); 138 | 139 | if(!entry) 140 | return -1; 141 | 142 | if(dict_get(dict, key)){ 143 | dict_remove(dict, key); 144 | } 145 | 146 | if(!dict->bins[hash]) { 147 | dict->bins[hash] = entry; 148 | }else{ 149 | dict_entry_t *curr; 150 | SET_TO_LAST_ENTRY(curr, dict, hash); 151 | curr->next = entry; 152 | } 153 | dict->size++; 154 | return 0; 155 | } 156 | 157 | unsigned char *dict_get(dict_t *dict, const char *key) 158 | { 159 | unsigned hash = hashf(dict->binsize, key); 160 | FOREACH_ENTRY_IN_BIN(entry, dict, hash) { 161 | if(!strcmp(entry->key, key)) 162 | return entry->value; 163 | } 164 | return NULL; 165 | } 166 | 167 | void *dict_remove(dict_t *dict, const char *key) 168 | { 169 | unsigned hash = hashf(dict->binsize, key); 170 | FOREACH_ENTRY_AND_PREV_IN_BIN(entry, prev, dict, hash) { 171 | 172 | if(!strcmp(entry->key, key)) { 173 | if(prev) 174 | prev->next = entry->next; 175 | else if(entry->next) 176 | dict->bins[hash] = entry->next; 177 | else 178 | dict->bins[hash] = NULL; 179 | 180 | dict_entry_free(entry); 181 | dict->size--; 182 | break; 183 | } 184 | } 185 | } 186 | 187 | void dict_rehash(dict_t *dict, size_t newsize) 188 | { 189 | dict_entry_t **newbins; 190 | newbins = calloc(newsize, sizeof(dict_entry_t*)); 191 | 192 | for(unsigned i = 0; i < dict->binsize; i++) { 193 | FOREACH_ENTRY_AND_PREV_IN_BIN(entry, prev, dict, i) { 194 | if(prev) 195 | prev->next = NULL; /*This node at the end of a new bin*/ 196 | 197 | unsigned hash = hashf(newsize, entry->key); 198 | if(!newbins[hash]){ 199 | newbins[hash] = entry; 200 | }else { 201 | dict_entry_t *last = newbins[hash]; 202 | while(last->next) 203 | last = last->next; 204 | 205 | last->next = entry; 206 | } 207 | } 208 | } 209 | 210 | free(dict->bins); 211 | dict->binsize = newsize; 212 | dict->bins = newbins; 213 | } 214 | 215 | unsigned dict_get_size(dict_t *dict) 216 | { 217 | return dict->size; 218 | } 219 | 220 | const dict_iter_t *dict_iter_first(const dict_t *dict) 221 | { 222 | for(unsigned i = 0; i < dict->binsize; i++) { 223 | FOREACH_ENTRY_IN_BIN(entry, dict, i) { 224 | return entry; 225 | } 226 | } 227 | return NULL; 228 | } 229 | 230 | const dict_iter_t *dict_iter_next(dict_t *dict, const dict_iter_t *iter) 231 | { 232 | if(((dict_entry_t*)iter)->next) 233 | return ((dict_entry_t*)iter)->next; 234 | 235 | unsigned hash = hashf(dict->binsize, dict_iter_get_key(iter)); 236 | 237 | for(unsigned i = hash + 1; i < dict->binsize; i++) { 238 | FOREACH_ENTRY_IN_BIN(entry, dict, i) { 239 | return entry; 240 | } 241 | } 242 | return NULL; 243 | } 244 | 245 | const unsigned char *dict_iter_get_value(const dict_iter_t *iter) 246 | { 247 | return ((dict_entry_t*)iter)->value; 248 | } 249 | 250 | const char *dict_iter_get_key(const dict_iter_t *iter) 251 | { 252 | return ((dict_entry_t*)iter)->key; 253 | } 254 | 255 | void dict_key_for_uint32(uint32_t key, char *out, size_t len) 256 | { 257 | char base = 'a'; 258 | size_t num_nybbles = sizeof(uint32_t) * CHAR_BIT / 4; 259 | assert(len >= num_nybbles + 1); 260 | int i; 261 | for(i = 0; i < num_nybbles; i++) { 262 | out[i] = base + ((key >> (i*4)) & 0xF); 263 | assert(out[i] >= 'a' && out[i] < ('a' + 16)); 264 | } 265 | out[i] = '\0'; 266 | } 267 | 268 | void *dict_dump(dict_t *dict) 269 | { 270 | for(unsigned i = 0; i < dict->binsize; i++) { 271 | printf("%4d: ", i); 272 | FOREACH_ENTRY_IN_BIN(entry, dict, i) { 273 | printf("[%s]", entry->key); 274 | if(entry->next) 275 | printf("-->"); 276 | else 277 | printf("-->[0]"); 278 | } 279 | printf("\n"); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /src/libbf/dict.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef DICT_H 21 | #define DICT_H 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | typedef struct dict dict_t; 28 | 29 | dict_t *dict_init(size_t size); 30 | void dict_free(dict_t *dict); 31 | int dict_add(dict_t *dict, const char *key, unsigned char *data, size_t size); 32 | unsigned char *dict_get(dict_t *dict, const char *key); 33 | void *dict_remove(dict_t *dict, const char *key); 34 | void dict_rehash(dict_t *dict, size_t newsize); 35 | unsigned dict_get_size(dict_t *dict); 36 | 37 | /* Gives a unique 8-char ascii string for each uint32_t, allowing to use uint32_t as keys, 38 | * should not be mixed with arbitrary string keys in a single dict */ 39 | void dict_key_for_uint32(uint32_t key, char *out, size_t len); 40 | 41 | typedef void dict_iter_t; 42 | 43 | const dict_iter_t *dict_iter_first(const dict_t *dict); 44 | const dict_iter_t *dict_iter_next(dict_t *dict, const dict_iter_t *iter); 45 | const char *dict_iter_get_key(const dict_iter_t *iter); 46 | const unsigned char *dict_iter_get_value(const dict_iter_t *iter); 47 | 48 | #define FOREACH_KEY_AND_VAL(_key, _val, _dict_ptr) \ 49 | for(const dict_iter_t *_iter = dict_iter_first(_dict_ptr); \ 50 | _iter \ 51 | && (_key = dict_iter_get_key(_iter)) \ 52 | && (_val = dict_iter_get_value(_iter)); \ 53 | _iter = dict_iter_next(_dict_ptr, _iter)) 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/libbf/dl_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "dl_file.h" 21 | #include "log.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | struct dl_file { 36 | pthread_mutex_t file_lock; 37 | size_t size; 38 | unsigned char *data; 39 | char path[]; 40 | }; 41 | 42 | dl_file_t *dl_file_create_and_open(size_t size, const char *path) 43 | { 44 | unsigned char *mem; 45 | int fd; 46 | struct stat stats; 47 | char errbuff[64]; 48 | 49 | char newpath[512]; 50 | strcpy(newpath, path); 51 | strcat(newpath, ".incomplete"); 52 | 53 | fd = open(path, O_CREAT | O_RDWR, 0777); 54 | if(fd < 0) 55 | goto fail_open; 56 | 57 | if(ftruncate(fd, size)) 58 | goto fail_truncate; 59 | 60 | fstat(fd, &stats); 61 | assert(stats.st_size == size); //temp 62 | 63 | mem = mmap(NULL, stats.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 64 | if(!mem) 65 | goto fail_map; 66 | 67 | dl_file_t *file = malloc(sizeof(dl_file_t) + strlen(newpath) + 1); 68 | if(!file) 69 | goto fail_alloc; 70 | 71 | pthread_mutex_init(&file->file_lock, NULL); 72 | file->size = size; 73 | file->data = mem; 74 | memcpy(file->path, newpath, strlen(newpath)); 75 | file->path[strlen(newpath)] = '\0'; 76 | 77 | rename(path, newpath); 78 | 79 | close(fd); 80 | log_printf(LOG_LEVEL_INFO, "Successfully (created and) opened file at: %s\n", path); 81 | return file; 82 | 83 | fail_alloc: 84 | munmap(mem, stats.st_size); 85 | fail_map: 86 | fail_truncate: 87 | close(fd); 88 | fail_open: 89 | log_printf(LOG_LEVEL_ERROR, "Unable to (create and) open file at:%s\n", path); 90 | return NULL; 91 | } 92 | 93 | int dl_file_close_and_free(dl_file_t *file) 94 | { 95 | int ret = 0; 96 | if(munmap(file->data, file->size)) 97 | ret = -1; 98 | 99 | pthread_mutex_destroy(&file->file_lock); 100 | free(file); 101 | 102 | return ret; 103 | } 104 | 105 | void dl_file_getfilemem(const dl_file_t *file, filemem_t *out) 106 | { 107 | out->mem = file->data; 108 | out->size = file->size; 109 | } 110 | 111 | int dl_file_complete(dl_file_t* file) 112 | { 113 | char *trim; 114 | char oldpath[512]; 115 | strncpy(oldpath, file->path, sizeof(oldpath)); 116 | trim = strstr(file->path, ".incomplete"); 117 | assert(trim && trim > file->path); 118 | 119 | *trim = '\0'; 120 | rename(oldpath, file->path); 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/libbf/dl_file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef DL_FILE_H 21 | #define DL_FILE_H 22 | 23 | #include 24 | 25 | typedef struct dl_file dl_file_t; 26 | 27 | typedef struct filemem { 28 | void *mem; 29 | size_t size; 30 | }filemem_t; 31 | 32 | dl_file_t *dl_file_create_and_open(size_t size, const char *path); 33 | int dl_file_close_and_free(dl_file_t *file); 34 | void dl_file_getfilemem(const dl_file_t *file, filemem_t *out); 35 | int dl_file_complete(dl_file_t *file); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/libbf/lbitfield.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef LBITFIELD_H 21 | #define LBITFIELD_H 22 | 23 | #include 24 | 25 | /* Most significant bit of the first byte in the buffer is at index 0 26 | * 27 | * *-+-+-+-+-+-+-+-* *-+-+-- ... 28 | * |0|1|2|3|4|5|6|7| |8|9|10 ... 29 | * *-+-+-+-+-+-+-+-* *-+-+-- ... 30 | * byte 0 byte 1 31 | * 32 | */ 33 | 34 | #define LBITFIELD_NUM_BYTES(_len) (((_len)/CHAR_BIT) + ((_len) % CHAR_BIT ? 1 : 0)) 35 | #define LBITFIELD_ISSET(_index, _buff) !!((_buff)[(_index)/CHAR_BIT] & (1 << (CHAR_BIT-((_index) % CHAR_BIT)-1))) 36 | 37 | #define LBITFIELD_SET(_index, _buff) \ 38 | do { \ 39 | ((_buff)[(_index)/CHAR_BIT] |= (1 << (CHAR_BIT-((_index) % CHAR_BIT)-1))); \ 40 | }while(0) 41 | 42 | #define LBITFIELD_CLR(_index, _buff) \ 43 | do { \ 44 | ((_buff)[(_index)/CHAR_BIT] &= ~(1 << (CHAR_BIT-((_index) % CHAR_BIT)-1))); \ 45 | }while(0) 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/libbf/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "list.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | 27 | #define FOREACH_NODE(_node, _list) \ 28 | for(node_t *_node = _list->head; _node; _node = _node->next) 29 | 30 | #define FOREACH_NODE_AND_PREV(_node, _prev, _list) \ 31 | for(node_t *_node = _list->head, *_prev = NULL; _node; _prev = _node, _node = _node->next) 32 | 33 | typedef struct node { 34 | struct node *next; 35 | size_t size; 36 | unsigned char data[]; 37 | }node_t; 38 | 39 | struct list { 40 | node_t *head; 41 | unsigned size; 42 | }; 43 | 44 | 45 | static node_t *node_init(void *data, size_t size) 46 | { 47 | node_t *node = malloc(sizeof(node_t) + size); 48 | if(node) { 49 | node->next = NULL; 50 | node->size = size; 51 | memcpy(node->data, data, size); 52 | } 53 | return node; 54 | } 55 | 56 | static void node_free(node_t *node) 57 | { 58 | free(node); 59 | } 60 | 61 | list_t *list_init(void) 62 | { 63 | list_t *ret = malloc(sizeof(list_t)); 64 | if(ret) { 65 | ret->head = NULL; 66 | ret->size = 0; 67 | } 68 | return ret; 69 | } 70 | 71 | void list_free(list_t *list) 72 | { 73 | node_t *curr = list->head; 74 | while(curr) { 75 | node_t *tmp = curr->next; 76 | node_free(curr); 77 | curr = tmp; 78 | } 79 | 80 | free(list); 81 | } 82 | 83 | int list_add(list_t *list, unsigned char *data, size_t size) 84 | { 85 | node_t *new_node; 86 | 87 | new_node = node_init(data, size); 88 | if(!new_node) 89 | return -1; 90 | 91 | if(!list->head) { 92 | list->head = new_node; 93 | list->size = 1; 94 | return 0; 95 | } 96 | 97 | FOREACH_NODE(curr, list){ 98 | if(curr->next) 99 | continue; 100 | 101 | curr->next = new_node; 102 | list->size++; 103 | return 0; 104 | } 105 | } 106 | 107 | int list_remove(list_t *list, unsigned char *data) 108 | { 109 | if(!list->head) 110 | return -1; 111 | 112 | FOREACH_NODE_AND_PREV(curr, prev, list) { 113 | if(memcmp(curr->data, data, curr->size)) 114 | continue; 115 | 116 | if(prev) 117 | prev->next = curr->next; 118 | else 119 | list->head = curr->next; 120 | node_free(curr); 121 | list->size--; 122 | return 0; 123 | } 124 | } 125 | 126 | unsigned list_get_size(list_t *list) 127 | { 128 | return list->size; 129 | } 130 | 131 | bool list_contains(list_t *list, unsigned char *data) 132 | { 133 | FOREACH_NODE(curr, list) { 134 | if(!memcmp(curr->data, data, curr->size)) 135 | return true; 136 | } 137 | return false; 138 | } 139 | 140 | const list_iter_t *list_iter_first(const list_t *list) 141 | { 142 | if(list->head) 143 | return list->head; 144 | else 145 | return NULL; 146 | } 147 | 148 | const list_iter_t *list_iter_next(const list_iter_t *iter) 149 | { 150 | return ((node_t*)iter)->next; 151 | } 152 | 153 | const unsigned char *list_iter_get_value(const list_iter_t *iter) 154 | { 155 | return ((node_t*)iter)->data; 156 | } 157 | -------------------------------------------------------------------------------- /src/libbf/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef LIST_H 21 | #define LIST_H 22 | 23 | #include 24 | #include 25 | 26 | typedef struct list list_t; 27 | 28 | list_t *list_init(void); 29 | void list_free(list_t *list); 30 | int list_add(list_t *list, unsigned char *data, size_t size); 31 | int list_remove(list_t *list, unsigned char *data); 32 | bool list_contains(list_t *list, unsigned char *data); 33 | unsigned list_get_size(list_t *list); 34 | 35 | typedef void list_iter_t; 36 | 37 | const list_iter_t *list_iter_first(const list_t *list); 38 | const list_iter_t *list_iter_next(const list_iter_t *iter); 39 | const unsigned char *list_iter_get_value(const list_iter_t *iter); 40 | 41 | #define FOREACH_ENTRY(_entry, _list_ptr) \ 42 | for(const list_iter_t *_iter = list_iter_first(_list_ptr); \ 43 | _iter && (_entry = list_iter_get_value(_iter)); _iter = list_iter_next(_iter)) 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/libbf/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "log.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | static pthread_mutex_t s_log_lock = PTHREAD_MUTEX_INITIALIZER; 30 | static int s_loglevel = DEFAULT_LOG_LVL; 31 | static FILE *s_logfile = NULL; 32 | 33 | void log_set_lvl(log_level_t lvl) 34 | { 35 | pthread_mutex_lock(&s_log_lock); 36 | s_loglevel = lvl; 37 | pthread_mutex_unlock(&s_log_lock); 38 | } 39 | 40 | void log_set_logfile(FILE *f) 41 | { 42 | pthread_mutex_lock(&s_log_lock); 43 | s_logfile = f; 44 | pthread_mutex_unlock(&s_log_lock); 45 | } 46 | 47 | void log_printf(log_level_t lvl, const char *fmt, ...) 48 | { 49 | va_list args; 50 | long tid = (long)syscall(SYS_gettid); 51 | time_t now = time(0); 52 | char timestr[9]; 53 | 54 | strftime(timestr, sizeof(timestr), "%H:%M:%S", localtime(&now)); 55 | 56 | pthread_mutex_lock(&s_log_lock); 57 | 58 | if(lvl < s_loglevel){ 59 | pthread_mutex_unlock(&s_log_lock); 60 | return; 61 | } 62 | 63 | fprintf(s_logfile, "[%.*s] [%05ld] ", 8, timestr, tid); 64 | switch(lvl){ 65 | case LOG_LEVEL_WARNING: 66 | fprintf(s_logfile, "WARNING: "); 67 | break; 68 | case LOG_LEVEL_ERROR: 69 | fprintf(s_logfile, "ERROR: "); 70 | break; 71 | } 72 | 73 | va_start(args, fmt); 74 | vfprintf(s_logfile, fmt, args); 75 | va_end(args); 76 | 77 | pthread_mutex_unlock(&s_log_lock); 78 | 79 | fflush(s_logfile); 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/libbf/log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef LOG_H 21 | #define LOG_H 22 | 23 | #include 24 | 25 | typedef enum{ 26 | LOG_LEVEL_DEBUG, 27 | LOG_LEVEL_INFO, 28 | LOG_LEVEL_WARNING, 29 | LOG_LEVEL_ERROR, 30 | LOG_LEVEL_NONE 31 | }log_level_t; 32 | 33 | #define DEFAULT_LOG_LVL LOG_LEVEL_INFO 34 | 35 | void log_set_lvl(log_level_t lvl); 36 | void log_set_logfile(FILE *f); 37 | void log_printf(log_level_t lvl, const char *fmt, ...); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/libbf/peer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef PEER_H 21 | #define PEER_H 22 | 23 | #include 24 | 25 | typedef struct peer{ 26 | char peer_id[20]; 27 | union { 28 | struct sockaddr_storage sas; 29 | struct sockaddr sa; 30 | struct sockaddr_in sa_in; 31 | struct sockaddr_in6 sa_in6; 32 | }addr; 33 | }peer_t; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/libbf/peer_connection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef PEER_CONNECTION_H 21 | #define PEER_CONNECTION_H 22 | 23 | #include 24 | #include 25 | #include "torrent.h" 26 | #include "peer.h" 27 | 28 | #define KB (1 << 10) 29 | #define PEER_REQUEST_SIZE (16 * KB) 30 | 31 | typedef struct peer_arg { 32 | bool has_torrent; 33 | torrent_t *torrent; 34 | bool has_sockfd; 35 | int sockfd; 36 | peer_t peer; 37 | }peer_arg_t; 38 | 39 | 40 | typedef struct peer_conn { 41 | pthread_t thread; 42 | peer_t peer; 43 | }peer_conn_t; 44 | 45 | int peer_connection_create(pthread_t *thread, peer_arg_t *arg); 46 | void peer_connection_queue_name(pthread_t thread, char *out, size_t len); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/libbf/peer_id.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "peer_id.h" 21 | #include "bitfiend.h" 22 | #include "log.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define LIBBF_CLIENT_ID "bf" 31 | 32 | char g_local_peer_id[20]; 33 | 34 | void peer_id_create(char outbuff[20]) 35 | { 36 | int offset = 0; 37 | unsigned int seed = time(NULL); 38 | 39 | memset(outbuff, 0, 20); 40 | offset += snprintf(outbuff, 20, "-%s%01X%01X%02X-", LIBBF_CLIENT_ID, LIBBF_VER_MAJOR, 41 | LIBBF_VER_MINOR, LIBBF_VER_PATCH); 42 | 43 | for(int i = 0; i < 12/(sizeof(int32_t)); i++){ 44 | int32_t r = rand_r(&seed); 45 | memcpy(outbuff + offset, &r, sizeof(r)); 46 | offset += sizeof(r); 47 | } 48 | log_printf(LOG_LEVEL_INFO, "Generated local client id: %.*s\n", 20, outbuff); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/libbf/peer_id.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef PEER_ID_H 21 | #define PEER_ID_H 22 | 23 | extern char g_local_peer_id[20]; 24 | 25 | void peer_id_create(char outbuff[20]); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/libbf/peer_listener.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "peer_listener.h" 21 | #include "log.h" 22 | #include "peer.h" 23 | #include "peer_connection.h" 24 | #include "bitfiend_internal.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #define LISTEN_QUEUE_SIZE 50 38 | 39 | static int bind_listener(const uint16_t port) 40 | { 41 | int sockfd; 42 | struct addrinfo hints, *listener, *head; 43 | 44 | memset(&hints, 0, sizeof(hints)); 45 | hints.ai_family = AF_UNSPEC; 46 | hints.ai_socktype = SOCK_STREAM; 47 | hints.ai_flags = AI_PASSIVE; 48 | 49 | char port_str[5]; 50 | snprintf(port_str, sizeof(port_str), "%04u", port); 51 | port_str[4] = '\0'; 52 | 53 | if(getaddrinfo(NULL, port_str, &hints, &head) < 0) 54 | goto fail_getaddrinfo; 55 | 56 | for(listener = head; listener; listener = listener->ai_next) { 57 | if((sockfd = socket(listener->ai_family, listener->ai_socktype, 58 | listener->ai_protocol)) < 0) { 59 | continue; 60 | } 61 | 62 | int itrue = 1; 63 | if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &itrue, sizeof(int)) < 0) { 64 | close(sockfd); 65 | continue; 66 | } 67 | 68 | if(bind(sockfd, listener->ai_addr, listener->ai_addrlen) < 0) { 69 | close(sockfd); 70 | continue; 71 | } 72 | 73 | break; 74 | } 75 | 76 | if(!listener) 77 | goto fail_bind; 78 | 79 | freeaddrinfo(head); 80 | log_printf(LOG_LEVEL_INFO, "Successfully bound peer listener socket (fd: %d) on port %hd\n", 81 | sockfd, port); 82 | return sockfd; 83 | 84 | fail_bind: 85 | freeaddrinfo(head); 86 | fail_getaddrinfo: 87 | return -1; 88 | } 89 | 90 | static void peer_listen_cleanup(void *arg) 91 | { 92 | int sockfd = *(int*)arg; 93 | log_printf(LOG_LEVEL_INFO, "Closing peer listener socket (fd: %d)\n", sockfd); 94 | close(sockfd); 95 | } 96 | 97 | static int create_peer_connection(peer_t *peer, int sockfd) 98 | { 99 | peer_arg_t *arg = malloc(sizeof(peer_arg_t)); 100 | if(!arg) 101 | goto fail_alloc; 102 | 103 | arg->has_torrent = false; 104 | arg->has_sockfd = true; 105 | arg->sockfd = sockfd; 106 | arg->peer = *peer; 107 | 108 | pthread_t newthread; 109 | if(peer_connection_create(&newthread, arg)) 110 | goto fail_create; 111 | 112 | bitfiend_add_unassoc_peer(newthread); 113 | 114 | free(peer); 115 | return 0; 116 | 117 | fail_create: 118 | free(arg); 119 | fail_alloc: 120 | free(peer); 121 | log_printf(LOG_LEVEL_ERROR, "Failed to create peer thread\n"); 122 | return -1; 123 | } 124 | 125 | static void *peer_listen(void *arg) 126 | { 127 | int sockfd; 128 | char errbuff[64]; 129 | 130 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 131 | if((sockfd = bind_listener(*(const uint16_t*)arg)) < 0) 132 | goto fail_bind; 133 | 134 | if(listen(sockfd, LISTEN_QUEUE_SIZE) < 0) 135 | goto fail_listen; 136 | 137 | pthread_cleanup_push(peer_listen_cleanup, (void*)&sockfd); 138 | 139 | while(true) { 140 | log_printf(LOG_LEVEL_INFO, "Listening for incoming peer connections...\n"); 141 | 142 | struct sockaddr peersock; 143 | socklen_t len = sizeof(peersock); 144 | int peer_sockfd; 145 | 146 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 147 | /* Cancellation point */ 148 | peer_sockfd = accept(sockfd, &peersock, &len); 149 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 150 | 151 | if(peer_sockfd < 0) 152 | continue; 153 | 154 | log_printf(LOG_LEVEL_INFO, "Peer connection accepted (sockfd: %d)\n", peer_sockfd); 155 | 156 | peer_t *peer = malloc(sizeof(peer_t)); 157 | memset(peer->peer_id, 0, sizeof(peer->peer_id)); 158 | peer->addr.sa = peersock; 159 | 160 | create_peer_connection(peer, peer_sockfd); 161 | } 162 | 163 | pthread_cleanup_pop(0); 164 | 165 | fail_listen: 166 | fail_bind: 167 | if(errno){ 168 | strerror_r(errno, errbuff, sizeof(errbuff)); 169 | log_printf(LOG_LEVEL_ERROR, "%s\n", errbuff); 170 | } 171 | pthread_exit(NULL); 172 | } 173 | 174 | int peer_listener_create(pthread_t *thread, const uint16_t *port) 175 | { 176 | if(pthread_create(thread, NULL, peer_listen, (void*)port)) 177 | goto fail_create_thread; 178 | 179 | return 0; 180 | 181 | fail_create_thread: 182 | log_printf(LOG_LEVEL_ERROR, "Failed to create peer listener thread\n"); 183 | return -1; 184 | } 185 | 186 | -------------------------------------------------------------------------------- /src/libbf/peer_listener.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef PEER_LISTENER_H 21 | #define PEER_LISTENER_H 22 | 23 | #include 24 | #include 25 | 26 | int peer_listener_create(pthread_t *thread, const uint16_t *port); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/libbf/peer_msg.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "peer_msg.h" 21 | #include "log.h" 22 | #include "peer_id.h" 23 | #include "peer_connection.h" 24 | #include "lbitfield.h" 25 | #include "piece_request.h" 26 | #include "dl_file.h" 27 | #include "stats.h" 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 40 | 41 | static uint32_t msgbuff_len(msg_type_t type, const torrent_t *torrent); 42 | static inline bool valid_len(msg_type_t type, const torrent_t *torrent, uint32_t len); 43 | static int peer_msg_recv_piece(int sockfd, peer_msg_t *out, const torrent_t *torrent, uint32_t len); 44 | static int peer_msg_send_piece(int sockfd, piece_msg_t *pmsg, const torrent_t *torrent); 45 | 46 | 47 | int peer_send_buff(int sockfd, const char *buff, size_t len) 48 | { 49 | ssize_t tot_sent = 0; 50 | while(tot_sent < len) { 51 | ssize_t sent = stats_send(sockfd, buff, len - tot_sent, 0); 52 | if(sent < 0) 53 | return -1; 54 | 55 | tot_sent += sent; 56 | buff += sent; 57 | } 58 | 59 | if(tot_sent == len) 60 | return 0; 61 | else 62 | return -1; 63 | } 64 | 65 | int peer_recv_buff(int sockfd, char *buff, size_t len) 66 | { 67 | unsigned tot_recv = 0; 68 | ssize_t nb; 69 | 70 | if(len == 0) 71 | return 0; 72 | 73 | do { 74 | assert(len - tot_recv > 0); 75 | nb = stats_recv(sockfd, buff + tot_recv, len - tot_recv, 0); 76 | if(nb < 0){ 77 | return -1; 78 | } 79 | 80 | tot_recv += nb; 81 | 82 | }while(nb > 0 && tot_recv < len); 83 | 84 | if(tot_recv == len) 85 | return 0; 86 | else 87 | return -1; 88 | } 89 | 90 | int peer_recv_handshake(int sockfd, char outhash[20], char outpeerid[20], bool peer_id) 91 | { 92 | const char *pstr = "BitTorrent protocol"; 93 | unsigned char pstrlen = strlen(pstr); 94 | const char reserved[8] = {0}; 95 | 96 | size_t bufflen = 1 + pstrlen + sizeof(reserved) + 20 97 | + (peer_id ? sizeof(g_local_peer_id) : 0); 98 | 99 | char buff[bufflen]; 100 | if(peer_recv_buff(sockfd, buff, bufflen)) 101 | return -1; 102 | 103 | off_t off = 0; 104 | if(buff[off] != pstrlen) 105 | return -1; 106 | off++; 107 | if(strncmp(buff + off, pstr, pstrlen)) 108 | return -1; 109 | off += pstrlen; 110 | 111 | /*Skip checking the reserved bits for now*/ 112 | off += 8; 113 | 114 | memcpy(outhash, buff + off, 20); 115 | if(peer_id) { 116 | off += 20; 117 | memcpy(outpeerid, buff + off, sizeof(g_local_peer_id)); 118 | } 119 | 120 | return 0; 121 | } 122 | 123 | int peer_send_handshake(int sockfd, char infohash[20]) 124 | { 125 | const char *pstr = "BitTorrent protocol"; 126 | unsigned char pstrlen = strlen(pstr); 127 | const char reserved[8] = {0}; 128 | 129 | size_t bufflen = 1 + pstrlen + sizeof(reserved) + 20 + sizeof(g_local_peer_id); 130 | 131 | off_t off = 0; 132 | char buff[bufflen]; 133 | 134 | buff[0] = pstrlen; 135 | off++; 136 | 137 | memcpy(buff + off, pstr, pstrlen); 138 | off += pstrlen; 139 | assert(off == 20); 140 | 141 | memcpy(buff + off, reserved, sizeof(reserved)); 142 | off += sizeof(reserved); 143 | assert(off == 28); 144 | 145 | memcpy(buff + off, infohash, 20); 146 | 147 | off += 20; 148 | memcpy(buff + off, g_local_peer_id, sizeof(g_local_peer_id)); 149 | 150 | return peer_send_buff(sockfd, buff, bufflen); 151 | } 152 | 153 | static uint32_t msgbuff_len(msg_type_t type, const torrent_t *torrent) 154 | { 155 | uint32_t ret; 156 | switch(type){ 157 | case MSG_KEEPALIVE: 158 | ret = 0; 159 | break; 160 | case MSG_PIECE: 161 | ret = 1 + 2 * sizeof(uint32_t) + PEER_REQUEST_SIZE; 162 | break; 163 | case MSG_BITFIELD: 164 | ret = 1 + LBITFIELD_NUM_BYTES(dict_get_size(torrent->pieces)); 165 | break; 166 | case MSG_REQUEST: 167 | ret = 1 + 3 * sizeof(uint32_t); 168 | break; 169 | case MSG_HAVE: 170 | case MSG_PORT: 171 | ret = 1 + sizeof(uint32_t); 172 | break; 173 | default: 174 | ret = 1; 175 | } 176 | return ret; 177 | } 178 | 179 | static inline bool valid_len(msg_type_t type, const torrent_t *torrent, uint32_t len) 180 | { 181 | if(type == MSG_PIECE) 182 | return (len >= (1 + 2 * sizeof(uint32_t) + 1)) && 183 | (len <= (1 + 2 * sizeof(uint32_t) + PEER_REQUEST_SIZE)); 184 | 185 | return (len == msgbuff_len(type, torrent)); 186 | } 187 | 188 | static int peer_msg_recv_piece(int sockfd, peer_msg_t *out, const torrent_t *torrent, uint32_t len) 189 | { 190 | log_printf(LOG_LEVEL_DEBUG, "*** peer_msg_recv_piece ***\n"); 191 | uint32_t u32, left = len; 192 | 193 | if(peer_recv_buff(sockfd, (char*)&u32, sizeof(u32))) 194 | return -1; 195 | out->payload.piece.index = ntohl(u32); 196 | left -= sizeof(uint32_t); 197 | 198 | if(peer_recv_buff(sockfd, (char*)&u32, sizeof(u32))) 199 | return -1; 200 | out->payload.piece.begin = ntohl(u32); 201 | left -= sizeof(uint32_t); 202 | 203 | out->payload.piece.blocklen = left; 204 | piece_request_t *pr = piece_request_create(torrent, out->payload.piece.index); 205 | if(!pr) 206 | return -1; 207 | 208 | block_request_t *br = piece_request_block_at(pr, out->payload.piece.begin); 209 | assert(br); 210 | const unsigned char *entry; 211 | FOREACH_ENTRY(entry, br->filemems) { 212 | filemem_t mem = *(filemem_t*)entry; 213 | log_printf(LOG_LEVEL_DEBUG, "Writing %zu bytes to %p\n", mem.size, mem.mem); 214 | if(peer_recv_buff(sockfd, mem.mem, mem.size)) 215 | goto fail_recv_piece; 216 | 217 | } 218 | piece_request_free(pr); 219 | return 0; 220 | 221 | fail_recv_piece: 222 | piece_request_free(pr); 223 | return -1; 224 | } 225 | 226 | static int peer_msg_send_piece(int sockfd, piece_msg_t *pmsg, const torrent_t *torrent) 227 | { 228 | log_printf(LOG_LEVEL_DEBUG, "*** peer_msg_send_piece [index: %u] ***\n", pmsg->index); 229 | piece_request_t *pr = piece_request_create(torrent, pmsg->index); 230 | if(!pr) 231 | return -1; 232 | 233 | uint32_t send_index = htonl(pmsg->index); 234 | if(peer_send_buff(sockfd, (char*)&send_index, sizeof(uint32_t))) 235 | goto fail_send_piece; 236 | 237 | uint32_t send_offset = htonl(pmsg->begin); 238 | if(peer_send_buff(sockfd, (char*)&send_offset, sizeof(uint32_t))) 239 | goto fail_send_piece; 240 | 241 | size_t written = 0; 242 | off_t offset = 0; 243 | 244 | const unsigned char *entry; 245 | FOREACH_ENTRY(entry, pr->block_requests) { 246 | block_request_t *br = *(block_request_t**)entry; 247 | 248 | const unsigned char *fmem; 249 | FOREACH_ENTRY(fmem, br->filemems) { 250 | filemem_t mem = *(filemem_t*)fmem; 251 | 252 | if(offset + mem.size > pmsg->begin) { 253 | size_t membegin = (offset > pmsg->begin) ? 0 : offset - pmsg->begin; 254 | size_t memlen = MIN(mem.size - membegin, pmsg->blocklen - written); 255 | 256 | log_printf(LOG_LEVEL_DEBUG, "Sending %zu bytes from %p [offset: %u, begin: %u]\n", 257 | memlen, ((char*)mem.mem) + membegin, offset, pmsg->begin); 258 | if(peer_send_buff(sockfd, ((char*)mem.mem) + membegin, memlen)) 259 | goto fail_send_piece; 260 | 261 | written += memlen; 262 | } 263 | 264 | if(written == pmsg->blocklen) 265 | goto done; 266 | 267 | offset += mem.size; 268 | } 269 | } 270 | 271 | done: 272 | piece_request_free(pr); 273 | return 0; 274 | 275 | fail_send_piece: 276 | piece_request_free(pr); 277 | return -1; 278 | } 279 | 280 | int peer_msg_send(int sockfd, peer_msg_t *msg, const torrent_t *torrent) 281 | { 282 | uint32_t len = msgbuff_len(msg->type, torrent); 283 | log_printf(LOG_LEVEL_INFO, "Sending message of type: %d, len: %u\n", msg->type, len); 284 | len = htonl(len); 285 | 286 | if(peer_send_buff(sockfd, (char*)&len, sizeof(uint32_t))) 287 | return -1; 288 | 289 | if(msg->type == MSG_KEEPALIVE) 290 | return 0; 291 | 292 | char out = msg->type; 293 | assert(out >= 0 && out < MSG_MAX); 294 | if(peer_send_buff(sockfd, &out, 1)) 295 | return -1; 296 | 297 | switch(msg->type){ 298 | case MSG_CHOKE: 299 | case MSG_UNCHOKE: 300 | case MSG_INTERESTED: 301 | case MSG_NOT_INTERESTED: 302 | { 303 | assert(ntohl(len) == 1); 304 | return 0; 305 | } 306 | case MSG_PIECE: 307 | { 308 | piece_msg_t *pmsg = &msg->payload.piece; 309 | return peer_msg_send_piece(sockfd, pmsg, torrent); 310 | } 311 | case MSG_BITFIELD: 312 | { 313 | assert(msg->payload.bitfield); 314 | if(peer_send_buff(sockfd, msg->payload.bitfield->str, msg->payload.bitfield->size)) 315 | return -1; 316 | 317 | return 0; 318 | } 319 | case MSG_REQUEST: 320 | { 321 | uint32_t u32; 322 | u32 = htonl(msg->payload.request.index); 323 | if(peer_send_buff(sockfd, (char*)&u32, sizeof(uint32_t))) 324 | return -1; 325 | u32 = htonl(msg->payload.request.begin); 326 | if(peer_send_buff(sockfd, (char*)&u32, sizeof(uint32_t))) 327 | return -1; 328 | u32 = htonl(msg->payload.request.length); 329 | if(peer_send_buff(sockfd, (char*)&u32, sizeof(uint32_t))) 330 | return -1; 331 | 332 | return 0; 333 | } 334 | case MSG_HAVE: 335 | { 336 | uint32_t u32; 337 | u32 = htonl(msg->payload.have); 338 | if(peer_send_buff(sockfd, (char*)&u32, sizeof(uint32_t))) 339 | return -1; 340 | 341 | return 0; 342 | } 343 | case MSG_PORT: 344 | { 345 | uint32_t u32; 346 | u32 = htonl(msg->payload.listen_port); 347 | if(peer_send_buff(sockfd, (char*)&u32, sizeof(uint32_t))) 348 | return -1; 349 | 350 | return 0; 351 | } 352 | case MSG_CANCEL: 353 | { 354 | //TODO: 355 | assert(0); 356 | break; 357 | } 358 | default: 359 | return -1; 360 | } 361 | } 362 | 363 | int peer_msg_recv(int sockfd, peer_msg_t *out, const torrent_t *torrent) 364 | { 365 | uint32_t len; 366 | if(peer_recv_buff(sockfd, (char*)&len, sizeof(uint32_t))) 367 | return -1; 368 | len = ntohl(len); 369 | 370 | log_printf(LOG_LEVEL_INFO, "Receiving message of length: %u\n", len); 371 | if(len == 0){ 372 | out->type = MSG_KEEPALIVE; 373 | return 0; 374 | } 375 | 376 | unsigned char type; 377 | if(peer_recv_buff(sockfd, &type, 1)) 378 | return -1; 379 | 380 | if(type >= MSG_MAX) 381 | return -1; 382 | 383 | if(!valid_len(type, torrent, len)) 384 | return -1; 385 | 386 | out->type = type; 387 | unsigned left = len - 1; 388 | 389 | switch(type){ 390 | case MSG_CHOKE: 391 | case MSG_UNCHOKE: 392 | case MSG_INTERESTED: 393 | case MSG_NOT_INTERESTED: 394 | { 395 | assert(left == 0); 396 | break; 397 | } 398 | /* When we get a piece, write it to the mmap'd file directly */ 399 | case MSG_PIECE: 400 | { 401 | assert(left > 0); 402 | if(peer_msg_recv_piece(sockfd, out, torrent, left)) 403 | return -1; 404 | break; 405 | 406 | } 407 | case MSG_BITFIELD: 408 | { 409 | char buff[left]; 410 | if(peer_recv_buff(sockfd, buff, left)) 411 | return -1; 412 | 413 | out->payload.bitfield = byte_str_new(left, ""); 414 | if(!out->payload.bitfield) 415 | return -1; 416 | memcpy(out->payload.bitfield->str, buff, left); 417 | break; 418 | } 419 | case MSG_REQUEST: 420 | { 421 | char buff[left]; 422 | if(peer_recv_buff(sockfd, buff, left)) 423 | return -1; 424 | 425 | assert(left == 3 * sizeof(uint32_t)); 426 | uint32_t u32; 427 | memcpy(&u32, buff, sizeof(uint32_t)); 428 | out->payload.request.index= ntohl(u32); 429 | 430 | memcpy(&u32, buff + sizeof(uint32_t), sizeof(uint32_t)); 431 | out->payload.request.begin= ntohl(u32); 432 | 433 | memcpy(&u32, buff + 2 * sizeof(uint32_t), sizeof(uint32_t)); 434 | out->payload.request.length = ntohl(u32); 435 | break; 436 | } 437 | case MSG_HAVE: 438 | { 439 | uint32_t u32; 440 | assert(left == sizeof(uint32_t)); 441 | if(peer_recv_buff(sockfd, (char*)&u32, left)) 442 | return -1; 443 | out->payload.have = ntohl(u32); 444 | break; 445 | } 446 | case MSG_PORT: 447 | { 448 | uint32_t u32; 449 | assert(left == sizeof(uint32_t)); 450 | if(peer_recv_buff(sockfd, (char*)&u32, left)) 451 | return -1; 452 | out->payload.listen_port = ntohl(u32); 453 | break; 454 | } 455 | case MSG_CANCEL: 456 | { 457 | //TODO: 458 | assert(0); 459 | break; 460 | } 461 | default: 462 | return -1; 463 | } 464 | 465 | log_printf(LOG_LEVEL_DEBUG, "Successfully received message from peer, Type: %hhu\n", type); 466 | return 0; 467 | } 468 | 469 | bool peer_msg_buff_nonempty(int sockfd) 470 | { 471 | uint32_t len; 472 | int n = recv(sockfd, (char*)&len, sizeof(uint32_t), MSG_PEEK | MSG_DONTWAIT); 473 | if(n < sizeof(uint32_t)) 474 | return false; 475 | len = ntohl(len); 476 | 477 | int bytes_avail; 478 | if(ioctl(sockfd, FIONREAD, &bytes_avail)) 479 | return false; 480 | 481 | if((unsigned)bytes_avail >= len + sizeof(uint16_t)) 482 | return true; 483 | 484 | return false; 485 | } 486 | -------------------------------------------------------------------------------- /src/libbf/peer_msg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef PEER_MSG_H 21 | #define PEER_MSG_H 22 | 23 | #include "byte_str.h" 24 | #include "torrent.h" 25 | #include 26 | 27 | typedef enum { 28 | MSG_CHOKE = 0, 29 | MSG_UNCHOKE = 1, 30 | MSG_INTERESTED = 2, 31 | MSG_NOT_INTERESTED = 3, 32 | MSG_HAVE = 4, 33 | MSG_BITFIELD = 5, 34 | MSG_REQUEST = 6, 35 | MSG_PIECE = 7, 36 | MSG_CANCEL = 8, 37 | MSG_PORT = 9, 38 | MSG_KEEPALIVE, 39 | MSG_MAX 40 | }msg_type_t; 41 | 42 | typedef struct request_msg{ 43 | uint32_t index; 44 | uint32_t begin; 45 | uint32_t length; 46 | }request_msg_t; 47 | 48 | typedef struct piece_msg{ 49 | uint32_t index; 50 | uint32_t begin; 51 | size_t blocklen; 52 | }piece_msg_t; 53 | 54 | typedef struct peer_msg { 55 | msg_type_t type; 56 | union{ 57 | uint32_t have; 58 | byte_str_t *bitfield; 59 | request_msg_t request; 60 | piece_msg_t piece; 61 | unsigned listen_port; 62 | }payload; 63 | }peer_msg_t; 64 | 65 | int peer_send_buff(int sockfd, const char *buff, size_t len); 66 | int peer_recv_buff(int sockfd, char *buff, size_t len); 67 | int peer_send_handshake(int sockfd, char infohash[20]); 68 | int peer_recv_handshake(int sockfd, char outhash[20], char outpeerid[20], bool peer_id); 69 | int peer_msg_send(int sockfd, peer_msg_t *msg, const torrent_t *torrent); 70 | int peer_msg_recv(int sockfd, peer_msg_t *out, const torrent_t *torrent); 71 | bool peer_msg_buff_nonempty(int sockfd); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/libbf/piece_request.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "piece_request.h" 21 | #include "dl_file.h" 22 | #include "peer_connection.h" 23 | 24 | #include 25 | #include 26 | 27 | static void skip_until_index(const list_iter_t **iter, off_t *offset, unsigned index, const torrent_t *torrent) 28 | { 29 | size_t skip = torrent->piece_len * index; 30 | while(skip > 0) { 31 | dl_file_t *file = *((dl_file_t**)list_iter_get_value(*iter)); 32 | assert(file); 33 | filemem_t mem; 34 | dl_file_getfilemem(file, &mem); 35 | 36 | /* This is the last file to skip*/ 37 | if(mem.size > skip) { 38 | *offset = skip; 39 | return; 40 | }else{ 41 | skip -= mem.size; 42 | *iter = list_iter_next(*iter); 43 | } 44 | } 45 | } 46 | 47 | static block_request_t *next_block_request(const list_iter_t **iter, off_t *offset, size_t *left, 48 | size_t piecelen) 49 | { 50 | if(!*iter || *left == 0) 51 | return NULL; 52 | 53 | block_request_t *ret = malloc(sizeof(block_request_t)); 54 | ret->begin = piecelen - *left; 55 | ret->completed = false; 56 | ret->len = 0; 57 | if(!ret) 58 | return NULL; 59 | ret->filemems = list_init(); 60 | if(!ret->filemems){ 61 | free(ret); 62 | return NULL; 63 | } 64 | 65 | unsigned curr_size = 0; 66 | 67 | do { 68 | dl_file_t *file = *((dl_file_t**)list_iter_get_value(*iter)); 69 | assert(file); 70 | filemem_t mem; 71 | dl_file_getfilemem(file, &mem); 72 | 73 | mem.mem = ((char*)mem.mem + *offset); 74 | mem.size -= *offset; 75 | 76 | if(mem.size > PEER_REQUEST_SIZE - curr_size){ 77 | mem.size = PEER_REQUEST_SIZE - curr_size; 78 | *offset += mem.size; 79 | }else{ 80 | *iter = list_iter_next(*iter); 81 | *offset = 0; 82 | } 83 | 84 | *left -= mem.size; 85 | list_add(ret->filemems, (unsigned char*)&mem, sizeof(filemem_t)); 86 | curr_size += mem.size; 87 | 88 | 89 | }while(curr_size < PEER_REQUEST_SIZE && *iter != NULL); 90 | 91 | ret->len = curr_size; 92 | return ret; 93 | } 94 | 95 | piece_request_t *piece_request_create(const torrent_t *torrent, unsigned index) 96 | { 97 | piece_request_t *ret = malloc(sizeof(piece_request_t)); 98 | if(!ret) 99 | return NULL; 100 | 101 | ret->block_requests = list_init(); 102 | if(!ret->block_requests) 103 | goto fail_alloc_list; 104 | 105 | ret->piece_index = index; 106 | 107 | const list_iter_t *iter = list_iter_first(torrent->files); 108 | assert(iter); 109 | block_request_t *block; 110 | size_t left = torrent->piece_len; 111 | off_t offset = 0; /* How many bytes at the start of the file 112 | * at the iterator have already been 'consumed' */ 113 | skip_until_index(&iter, &offset, index, torrent); 114 | 115 | while(block = next_block_request(&iter, &offset, &left, torrent->piece_len)) { 116 | list_add(ret->block_requests, (unsigned char*)&block, sizeof(block_request_t*)); 117 | } 118 | 119 | ret->blocks_left = list_get_size(ret->block_requests); 120 | return ret; 121 | 122 | fail_alloc_entry: ; 123 | const unsigned char *entry; 124 | FOREACH_ENTRY(entry, ret->block_requests) { 125 | free(*(filemem_t**)entry); 126 | } 127 | fail_alloc_list: 128 | free(ret); 129 | return NULL; 130 | } 131 | 132 | void piece_request_free(piece_request_t *request) 133 | { 134 | const unsigned char *entry; 135 | FOREACH_ENTRY(entry, request->block_requests) { 136 | block_request_t *br = *(block_request_t**)entry; 137 | list_free(br->filemems); /* The filemems are copied into the list, 138 | * no additional heap freeing necessary */ 139 | free(br); 140 | } 141 | free(request); 142 | } 143 | 144 | block_request_t *piece_request_block_at(piece_request_t *request, off_t offset) 145 | { 146 | const unsigned char *entry; 147 | FOREACH_ENTRY(entry, request->block_requests) { 148 | block_request_t *req = *(block_request_t**)entry; 149 | 150 | if(req->begin == offset) 151 | return req; 152 | } 153 | return NULL; 154 | } 155 | 156 | -------------------------------------------------------------------------------- /src/libbf/piece_request.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef PIECE_REQUEST_H 21 | #define PIECE_REQUEST_H 22 | 23 | #include 24 | #include 25 | #include "list.h" 26 | #include "torrent.h" 27 | 28 | #include 29 | 30 | typedef struct block_request { 31 | list_t *filemems; 32 | off_t begin; 33 | size_t len; 34 | bool completed; 35 | }block_request_t; 36 | 37 | typedef struct piece_request { 38 | unsigned piece_index; 39 | list_t *block_requests; 40 | unsigned blocks_left; 41 | }piece_request_t; 42 | 43 | piece_request_t *piece_request_create(const torrent_t *torrent, unsigned index); 44 | void piece_request_free(piece_request_t *request); 45 | block_request_t *piece_request_block_at(piece_request_t *request, off_t offset); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /src/libbf/queue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "queue.h" 21 | #include "log.h" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #define QUEUE_BYTES(q) ((q)->entry_size * (q)->capacity) 28 | 29 | struct queue { 30 | size_t entry_size; 31 | int capacity; 32 | size_t size; 33 | char *head; 34 | char *tail; 35 | char *mem; 36 | }; 37 | 38 | static int queue_resize(queue_t *queue, unsigned new_cap) 39 | { 40 | void *ret; 41 | ptrdiff_t offhead, offtail; 42 | offhead = queue->head - queue->mem; 43 | offtail = queue->tail - queue->mem; 44 | 45 | if(ret = realloc(queue->mem, queue->entry_size * new_cap)){ 46 | queue->mem = ret; 47 | queue->head = queue->mem + offhead; 48 | queue->tail = queue->mem + offtail; 49 | }else{ 50 | return -1; 51 | } 52 | queue->capacity = new_cap; 53 | 54 | if(queue->head > queue->tail){ 55 | /* */ 56 | /* +-----+ <--mem ^ */ 57 | /* | | top */ 58 | /* | | | */ 59 | /* +-----+ <--tail | */ 60 | /* +-----+ v */ 61 | /* | | */ 62 | /* | | */ 63 | /* +-----+ <--head ^ */ 64 | /* +-----+ | */ 65 | /* | | bot */ 66 | /* +-----+ v */ 67 | /* | new | */ 68 | /* */ 69 | 70 | assert(queue->tail >= queue->mem); 71 | assert(queue->head >= queue->mem); 72 | ptrdiff_t top = queue->tail + queue->entry_size - queue->mem; 73 | ptrdiff_t bot = queue->mem + QUEUE_BYTES(queue) - queue->head; 74 | 75 | char tmp[top]; 76 | memcpy(tmp, queue->mem, top); 77 | memmove(queue->mem, queue->head, bot); 78 | memcpy(queue->mem + bot, tmp, top); 79 | 80 | queue->head = queue->mem; 81 | queue->tail = queue->mem + bot; 82 | } 83 | 84 | return 0; 85 | } 86 | 87 | queue_t *queue_init(size_t entry_size, int init_capacity) 88 | { 89 | queue_t *ret = malloc(sizeof(queue_t)); 90 | if(ret){ 91 | ret->mem = malloc(entry_size * init_capacity); 92 | if(!ret->mem){ 93 | free(ret); 94 | return NULL; 95 | } 96 | ret->entry_size = entry_size; 97 | ret->capacity = init_capacity; 98 | ret->head = ret->mem; 99 | ret->tail = ret->mem - entry_size; 100 | ret->size = 0; 101 | } 102 | return ret; 103 | } 104 | 105 | queue_t *queue_copy(const queue_t *queue) 106 | { 107 | queue_t *ret = malloc(sizeof(queue_t)); 108 | if(!ret) 109 | return NULL; 110 | 111 | ret->mem = malloc(queue->entry_size * queue->capacity); 112 | if(!ret->mem){ 113 | free(ret); 114 | return NULL; 115 | } 116 | memcpy(ret->mem, queue->mem, queue->entry_size * queue->capacity); 117 | 118 | ret->size = queue->size; 119 | ret->capacity = queue->capacity; 120 | ret->entry_size = queue->entry_size; 121 | ret->head = ret->mem + (queue->head - queue->mem); 122 | ret->tail = ret->mem + (queue->tail- queue->mem); 123 | 124 | return ret; 125 | } 126 | 127 | void queue_free(queue_t *queue) 128 | { 129 | free(queue->mem); 130 | free(queue); 131 | } 132 | 133 | int queue_push(queue_t *queue, void *entry) 134 | { 135 | if(queue->size == queue->capacity) { 136 | if(queue_resize(queue, queue->capacity * 2)) 137 | return -1; 138 | } 139 | 140 | queue->tail += queue->entry_size; 141 | /* Wrap around back to top */ 142 | if(queue->tail >= queue->mem + QUEUE_BYTES(queue)) { 143 | queue->tail = queue->mem; 144 | } 145 | 146 | assert(queue->tail >= queue->mem && queue->tail < queue->mem + QUEUE_BYTES(queue)); 147 | memcpy(queue->tail, entry, queue->entry_size); 148 | queue->size++; 149 | return 0; 150 | } 151 | 152 | int queue_pop(queue_t *queue, void *out) 153 | { 154 | if(queue->size == 0) 155 | return -1; 156 | 157 | assert(queue->head>= queue->mem && queue->head < queue->mem + QUEUE_BYTES(queue)); 158 | memcpy(out, queue->head, queue->entry_size); 159 | queue->head += queue->entry_size; 160 | /*Wrap around back to top */ 161 | if(queue->head >= queue->mem + QUEUE_BYTES(queue)) { 162 | queue->head = queue->mem; 163 | } 164 | queue->size--; 165 | return 0; 166 | } 167 | 168 | size_t queue_get_size(queue_t *queue) 169 | { 170 | return queue->size; 171 | } 172 | 173 | -------------------------------------------------------------------------------- /src/libbf/queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef QUEUE_H 21 | #define QUEUE_H 22 | 23 | #include 24 | 25 | typedef struct queue queue_t; 26 | 27 | queue_t *queue_init(size_t entry_size, int init_capacity); 28 | queue_t *queue_copy(const queue_t *queue); 29 | void queue_free(queue_t *queue); 30 | int queue_push(queue_t *queue, void *entry); 31 | int queue_pop(queue_t *queue, void *out); 32 | size_t queue_get_size(queue_t *queue); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/libbf/sha1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "sha1.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define CIRCULAR_SHIFT_32(X, n) (htobe32((be32toh(X) << (n)) | (be32toh(X) >> (32-(n))))) 32 | 33 | #define ADD_32(a, b) (htobe32(be32toh(a) + be32toh(b))) 34 | 35 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 36 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 37 | 38 | struct sha1_context{ 39 | union { 40 | unsigned char bytes[DIGEST_LEN]; 41 | uint32_t H[5]; 42 | }digest; 43 | uint32_t last_block[16]; 44 | size_t incomp_block_sz; 45 | size_t tot_len; 46 | }; 47 | 48 | static uint32_t f(int t, uint32_t B, uint32_t C, uint32_t D); 49 | static uint32_t K(int t); 50 | static void pad_msg_block(char *msgbuff, size_t topad, size_t len, bool stopbit); 51 | static unsigned num_msg_blocks(size_t len); 52 | static int next_block(sha1_context_t *ctx, const char *msg, size_t len); 53 | 54 | 55 | static uint32_t f(int t, uint32_t B, uint32_t C, uint32_t D) 56 | { 57 | if (0 <= t && t <= 19) 58 | return ((B & C) | ((~B) & D)); 59 | else if(20 <= t && t <= 39) 60 | return (B ^ C ^ D); 61 | else if(40 <= t && t <= 59) 62 | return ((B & C) | (B & D) | (C & D)); 63 | else if(60 <= t && t <= 79) 64 | return (B ^ C ^ D); 65 | } 66 | 67 | static uint32_t K(int t) 68 | { 69 | if (0 <= t && t <= 19) 70 | return htobe32(0x5A827999); 71 | else if(20 <= t && t <= 39) 72 | return htobe32(0x6ED9EBA1); 73 | else if(40 <= t && t <= 59) 74 | return htobe32(0x8F1BBCDC); 75 | else if(60 <= t && t <= 79) 76 | return htobe32(0xCA62C1D6); 77 | } 78 | 79 | static void pad_msg_block(char *msgbuff, size_t topad, size_t len, bool stopbit) 80 | { 81 | size_t footer_len = 1 + sizeof(uint64_t); 82 | assert(topad <= 64 && topad >= footer_len); 83 | 84 | unsigned tozero = topad - footer_len; 85 | memset(msgbuff + (64 - topad), (stopbit ? 0x80 : 0x00), 1); 86 | memset(msgbuff + (64 - topad) + 1, 0, tozero); 87 | 88 | assert(tozero + 1 + sizeof(uint64_t) == topad); 89 | 90 | uint64_t len64 = htobe64(len * 8); 91 | assert(msgbuff + (64 - topad) + tozero + 1 == msgbuff + 64 - sizeof(uint64_t)); 92 | memcpy(msgbuff + (64 - topad) + tozero + 1, &len64, sizeof(uint64_t)); 93 | } 94 | 95 | static unsigned num_msg_blocks(size_t len) 96 | { 97 | return (len / 64) + ((len % 64) ? 1 : 0); 98 | } 99 | 100 | static int next_block(sha1_context_t *ctx, const char *msg, size_t len) 101 | { 102 | size_t consume = MIN(len, 64 - ctx->incomp_block_sz); 103 | memcpy(((char*)ctx->last_block) + ctx->incomp_block_sz, msg, consume); 104 | 105 | size_t bsize = ctx->incomp_block_sz + consume; 106 | ctx->incomp_block_sz = bsize % 64; 107 | ctx->tot_len += consume; 108 | 109 | return consume; 110 | } 111 | 112 | int sha1_compute(const char *msg, size_t len, char out_digest[DIGEST_LEN]) 113 | { 114 | sha1_context_t *ctx = sha1_context_init(); 115 | if(!ctx) 116 | return -1; 117 | 118 | sha1_update(ctx, msg, len); 119 | sha1_finish(ctx, out_digest); 120 | 121 | sha1_context_free(ctx); 122 | return 0; 123 | } 124 | 125 | sha1_context_t *sha1_context_init(void) 126 | { 127 | sha1_context_t *ret = malloc(sizeof(sha1_context_t)); 128 | if(ret) { 129 | ret->digest.H[0] = htobe32(0x67452301); 130 | ret->digest.H[1] = htobe32(0xEFCDAB89); 131 | ret->digest.H[2] = htobe32(0x98BADCFE); 132 | ret->digest.H[3] = htobe32(0x10325476); 133 | ret->digest.H[4] = htobe32(0xC3D2E1F0); 134 | 135 | ret->incomp_block_sz = 0; 136 | ret->tot_len = 0; 137 | } 138 | return ret; 139 | } 140 | 141 | void sha1_update(sha1_context_t *ctx, const char *msg, size_t len) 142 | { 143 | unsigned tot_blocks = num_msg_blocks(len); 144 | for(unsigned i = 0; i < tot_blocks; i++) { 145 | 146 | uint32_t A, B, C, D, E; 147 | uint32_t W[80]; 148 | 149 | size_t consume = next_block(ctx, msg, len); 150 | assert(consume >= 0 && consume <= 64); 151 | assert(len - consume >= 0); 152 | msg += consume; 153 | len -= consume; 154 | 155 | if(ctx->incomp_block_sz > 0) 156 | break; 157 | 158 | for(int t = 0; t <= 15; t++) { 159 | W[t] = ctx->last_block[t]; 160 | } 161 | 162 | for(int t = 16; t <= 79; t++) { 163 | W[t] = CIRCULAR_SHIFT_32(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); 164 | } 165 | 166 | A = ctx->digest.H[0]; 167 | B = ctx->digest.H[1]; 168 | C = ctx->digest.H[2]; 169 | D = ctx->digest.H[3]; 170 | E = ctx->digest.H[4]; 171 | 172 | for(int t = 0; t <= 79; t++) { 173 | uint32_t temp = 174 | ADD_32(ADD_32(ADD_32(ADD_32(CIRCULAR_SHIFT_32(A, 5), f(t, B, C, D)), E), W[t]), K(t)); 175 | 176 | E = D; D = C; C = CIRCULAR_SHIFT_32(B, 30); B = A; A = temp; 177 | } 178 | 179 | ctx->digest.H[0] = ADD_32(ctx->digest.H[0], A); 180 | ctx->digest.H[1] = ADD_32(ctx->digest.H[1], B); 181 | ctx->digest.H[2] = ADD_32(ctx->digest.H[2], C); 182 | ctx->digest.H[3] = ADD_32(ctx->digest.H[3], D); 183 | ctx->digest.H[4] = ADD_32(ctx->digest.H[4], E); 184 | } 185 | } 186 | 187 | void sha1_finish(sha1_context_t *ctx, char digest[DIGEST_LEN]) 188 | { 189 | char msg_block[64]; 190 | bool stopbit = true; 191 | 192 | if(64 - ctx->incomp_block_sz < 1 + sizeof(uint64_t)){ 193 | memcpy(msg_block, ctx->last_block, ctx->incomp_block_sz); 194 | 195 | size_t topad = 64 - ctx->incomp_block_sz; 196 | assert(topad >= 1); 197 | memset(msg_block + ctx->incomp_block_sz, 0x80, 1); 198 | memset(msg_block + ctx->incomp_block_sz + 1, 0x00, topad - 1); 199 | stopbit = false; 200 | 201 | ctx->incomp_block_sz = 0; 202 | 203 | sha1_update(ctx, msg_block, 64); 204 | assert(ctx->incomp_block_sz == 0); 205 | } 206 | 207 | memcpy(msg_block, ctx->last_block, ctx->incomp_block_sz); 208 | pad_msg_block(msg_block, 64 - ctx->incomp_block_sz, ctx->tot_len, stopbit); 209 | ctx->incomp_block_sz = 0; 210 | 211 | sha1_update(ctx, msg_block, 64); 212 | 213 | memcpy(digest, ctx->digest.bytes, DIGEST_LEN); 214 | } 215 | 216 | void sha1_context_free(sha1_context_t *ctx) 217 | { 218 | free(ctx); 219 | } 220 | 221 | -------------------------------------------------------------------------------- /src/libbf/sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef SHA1_H 21 | #define SHA1_H 22 | 23 | #include 24 | 25 | #define DIGEST_LEN 20 26 | 27 | typedef struct sha1_context sha1_context_t; 28 | 29 | sha1_context_t *sha1_context_init(void); 30 | void sha1_context_free(sha1_context_t *ctx); 31 | void sha1_update(sha1_context_t *ctx, const char *msg, size_t len); 32 | void sha1_finish(sha1_context_t *ctx, char digest[DIGEST_LEN]); 33 | 34 | int sha1_compute(const char *msg, size_t len, char digest[DIGEST_LEN]); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/libbf/stats.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "stats.h" 21 | #include "dict.h" 22 | #include "peer_connection.h" 23 | #include "queue.h" 24 | #include "log.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #define SAMPLE_PERIOD_USEC 1000000 34 | 35 | 36 | typedef enum { 37 | BW_UPLOAD, 38 | BW_DOWNLOAD 39 | }bw_type_t; 40 | 41 | typedef struct torr_stat{ 42 | unsigned long total_up; 43 | unsigned long total_down; 44 | unsigned long start_time_us; 45 | /* Keep the number of uploaded/downloaded bytes in the last 46 | * 'sample interval' for calculating instantaneous rates */ 47 | unsigned long last_sample_start_us; 48 | unsigned long last_sample_up; 49 | unsigned long last_sample_down; 50 | /* These are for the rates of the previous 'sample interval' */ 51 | unsigned long prev_sample_interval_us; 52 | unsigned long prev_sample_up; 53 | unsigned long prev_sample_down; 54 | }torr_stat_t; 55 | 56 | 57 | static dict_t *s_thread_torr_table; 58 | static pthread_mutex_t s_thread_torr_table_lock = PTHREAD_MUTEX_INITIALIZER; 59 | 60 | static dict_t *s_torr_stat_table; 61 | static pthread_mutex_t s_torr_stat_table_lock = PTHREAD_MUTEX_INITIALIZER; 62 | 63 | 64 | static torr_stat_t *torr_stat_new(void); 65 | static void torr_stat_free(torr_stat_t *torr_stat); 66 | static void torrent_to_key(const torrent_t *torrent, char *out); 67 | static void stats_log(bw_type_t type, unsigned nbytes); 68 | static unsigned long ts_usec(void); 69 | 70 | 71 | static torr_stat_t *torr_stat_new(void) 72 | { 73 | torr_stat_t *ret = malloc(sizeof(torr_stat_t)); 74 | if(!ret){ 75 | return NULL; 76 | } 77 | 78 | ret->total_up = 0UL; 79 | ret->total_down = 0UL; 80 | /* Time of registering is taken to be time that torrent has 'started' */ 81 | ret->start_time_us = ts_usec(); 82 | ret->last_sample_start_us = ts_usec(); 83 | ret->last_sample_up = 0; 84 | ret->last_sample_down = 0; 85 | 86 | return ret; 87 | } 88 | 89 | static void torr_stat_free(torr_stat_t *torr_stat) 90 | { 91 | free(torr_stat); 92 | } 93 | 94 | static void torrent_to_key(const torrent_t *torrent, char *out) 95 | { 96 | memcpy(out, &torrent, sizeof(torrent_t*)); 97 | out[sizeof(torrent_t*)] = '\0'; 98 | } 99 | 100 | static void stats_log(bw_type_t type, unsigned nbytes) 101 | { 102 | unsigned char *val; 103 | torrent_t *torrent = NULL; 104 | 105 | char thread_key[64]; 106 | peer_connection_queue_name(pthread_self(), thread_key, sizeof(thread_key)); 107 | 108 | /* Fetch the torrent for this thread */ 109 | pthread_mutex_lock(&s_thread_torr_table_lock); 110 | if(val = dict_get(s_thread_torr_table, thread_key)){ 111 | torrent = *(torrent_t**)val; 112 | } 113 | pthread_mutex_unlock(&s_thread_torr_table_lock); 114 | 115 | /* There is no torrent associated with this thread until after handshaking */ 116 | if(!torrent) 117 | return; 118 | 119 | char torrent_key[sizeof(torrent_t*) + 1]; 120 | torrent_to_key(torrent, torrent_key); 121 | 122 | /* And update the thread's stats */ 123 | pthread_mutex_lock(&s_torr_stat_table_lock); 124 | 125 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 126 | 127 | if(type == BW_UPLOAD){ 128 | stat->total_up += nbytes; 129 | stat->last_sample_up += nbytes; 130 | }else { 131 | stat->total_down += nbytes; 132 | stat->last_sample_down += nbytes; 133 | } 134 | 135 | unsigned long curr = ts_usec(); 136 | unsigned long diff; 137 | if((diff = curr - stat->last_sample_start_us) > SAMPLE_PERIOD_USEC) { 138 | stat->prev_sample_interval_us = diff; 139 | stat->prev_sample_up = stat->last_sample_up; 140 | stat->prev_sample_down = stat->last_sample_down; 141 | 142 | stat->last_sample_start_us = curr; 143 | stat->last_sample_up = 0; 144 | stat->last_sample_down = 0; 145 | } 146 | 147 | pthread_mutex_unlock(&s_torr_stat_table_lock); 148 | } 149 | 150 | static unsigned long ts_usec(void) 151 | { 152 | struct timeval tv; 153 | gettimeofday(&tv, NULL); 154 | return (tv.tv_sec * 1000000ul + tv.tv_usec); 155 | } 156 | 157 | int stats_init(void) 158 | { 159 | s_thread_torr_table = dict_init(256); 160 | if(!s_thread_torr_table) 161 | return -1; 162 | 163 | s_torr_stat_table = dict_init(32); 164 | if(!s_torr_stat_table){ 165 | dict_free(s_thread_torr_table); 166 | return -1; 167 | } 168 | 169 | return 0; 170 | } 171 | 172 | void stats_shutdown(void) 173 | { 174 | dict_free(s_thread_torr_table); 175 | 176 | const char *key; 177 | const unsigned char *val; 178 | FOREACH_KEY_AND_VAL(key, val, s_torr_stat_table){ 179 | torr_stat_free(*(torr_stat_t**)val); 180 | } 181 | 182 | dict_free(s_torr_stat_table); 183 | } 184 | 185 | void stats_register(pthread_t thread, const torrent_t *torrent) 186 | { 187 | char thread_key[64]; 188 | /* The thread queue name is already a unique string representation of the thread id */ 189 | peer_connection_queue_name(thread, thread_key, sizeof(thread_key)); 190 | 191 | /* Add thread:torrent mapping */ 192 | pthread_mutex_lock(&s_thread_torr_table_lock); 193 | dict_add(s_thread_torr_table, thread_key, (unsigned char*)&torrent, sizeof(torrent_t*)); 194 | pthread_mutex_unlock(&s_thread_torr_table_lock); 195 | 196 | log_printf(LOG_LEVEL_INFO, "Registered thread for torrent: %s\n", torrent->name); 197 | } 198 | 199 | void stats_unregister(pthread_t thread, const torrent_t *torrent) 200 | { 201 | char thread_key[64]; 202 | /* The thread queue name is already a unique string representation of the thread id */ 203 | peer_connection_queue_name(thread, thread_key, sizeof(thread_key)); 204 | 205 | /* Remove thread:torrent mapping */ 206 | pthread_mutex_lock(&s_thread_torr_table_lock); 207 | dict_remove(s_thread_torr_table, thread_key); 208 | pthread_mutex_unlock(&s_thread_torr_table_lock); 209 | 210 | log_printf(LOG_LEVEL_INFO, "Unregistered thread for torrent: %s\n", torrent->name); 211 | } 212 | 213 | void stats_add_entry(const torrent_t *torrent) 214 | { 215 | char torrent_key[sizeof(torrent_t*) + 1]; 216 | torrent_to_key(torrent, torrent_key); 217 | 218 | /* Add torrent:stat mapping */ 219 | pthread_mutex_lock(&s_torr_stat_table_lock); 220 | torr_stat_t *stat = torr_stat_new(); 221 | dict_add(s_torr_stat_table, torrent_key, (unsigned char*)&stat, sizeof(torrent_t*)); 222 | pthread_mutex_unlock(&s_torr_stat_table_lock); 223 | } 224 | 225 | void stats_remove_entry(const torrent_t *torrent) 226 | { 227 | char torrent_key[sizeof(torrent_t*) + 1]; 228 | torrent_to_key(torrent, torrent_key); 229 | 230 | /* Add torrent:stat mapping */ 231 | pthread_mutex_lock(&s_torr_stat_table_lock); 232 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 233 | torr_stat_free(stat); 234 | dict_remove(s_torr_stat_table, torrent_key); 235 | pthread_mutex_unlock(&s_torr_stat_table_lock); 236 | } 237 | 238 | ssize_t stats_send(int sockfd, const void *buf, size_t len, int flags) 239 | { 240 | ssize_t ret = send(sockfd, buf, len, flags); 241 | if(ret > 0){ 242 | stats_log(BW_UPLOAD, ret); 243 | } 244 | return ret; 245 | } 246 | 247 | ssize_t stats_recv(int sockfd, void *buf, size_t len, int flags) 248 | { 249 | ssize_t ret = recv(sockfd, buf, len, flags); 250 | if(ret > 0){ 251 | stats_log(BW_DOWNLOAD, ret); 252 | } 253 | return ret; 254 | } 255 | 256 | double stats_up_instrate(const torrent_t *torrent) 257 | { 258 | unsigned long tot_bits = 0; 259 | unsigned long tot_usecs; 260 | 261 | char torrent_key[sizeof(torrent_t*) + 1]; 262 | torrent_to_key(torrent, torrent_key); 263 | 264 | pthread_mutex_lock(&s_torr_stat_table_lock); 265 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 266 | tot_bits = stat->prev_sample_up * CHAR_BIT; 267 | tot_usecs = stat->prev_sample_interval_us; 268 | pthread_mutex_unlock(&s_torr_stat_table_lock); 269 | 270 | return tot_bits/(tot_usecs/1000000.0f); 271 | } 272 | 273 | double stats_up_avgrate(const torrent_t *torrent) 274 | { 275 | unsigned long tot_bits; 276 | unsigned long tot_usecs; 277 | 278 | char torrent_key[sizeof(torrent_t*) + 1]; 279 | torrent_to_key(torrent, torrent_key); 280 | 281 | pthread_mutex_lock(&s_torr_stat_table_lock); 282 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 283 | tot_bits = stat->total_up * CHAR_BIT; 284 | tot_usecs = (ts_usec() - stat->start_time_us); 285 | pthread_mutex_unlock(&s_torr_stat_table_lock); 286 | 287 | assert(tot_usecs > 0); 288 | return ((double)tot_bits)/(tot_usecs/1000000); 289 | } 290 | 291 | unsigned long stats_up_total(const torrent_t *torrent) 292 | { 293 | unsigned long ret; 294 | 295 | char torrent_key[sizeof(torrent_t*) + 1]; 296 | torrent_to_key(torrent, torrent_key); 297 | 298 | pthread_mutex_lock(&s_torr_stat_table_lock); 299 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 300 | ret = stat->total_up; 301 | pthread_mutex_unlock(&s_torr_stat_table_lock); 302 | 303 | return ret; 304 | } 305 | 306 | double stats_down_instrate(const torrent_t *torrent) 307 | { 308 | unsigned long tot_bits = 0; 309 | unsigned long tot_usecs; 310 | 311 | char torrent_key[sizeof(torrent_t*) + 1]; 312 | torrent_to_key(torrent, torrent_key); 313 | 314 | pthread_mutex_lock(&s_torr_stat_table_lock); 315 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 316 | tot_bits = stat->prev_sample_down * CHAR_BIT; 317 | tot_usecs = stat->prev_sample_interval_us; 318 | pthread_mutex_unlock(&s_torr_stat_table_lock); 319 | 320 | return tot_bits/(tot_usecs/1000000.0f); 321 | } 322 | 323 | double stats_down_avgrate(const torrent_t *torrent) 324 | { 325 | unsigned long tot_bits; 326 | unsigned long tot_usecs; 327 | 328 | char torrent_key[sizeof(torrent_t*) + 1]; 329 | torrent_to_key(torrent, torrent_key); 330 | 331 | pthread_mutex_lock(&s_torr_stat_table_lock); 332 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 333 | tot_bits = stat->total_down * CHAR_BIT; 334 | tot_usecs = (ts_usec() - stat->start_time_us); 335 | pthread_mutex_unlock(&s_torr_stat_table_lock); 336 | 337 | assert(tot_usecs > 0); 338 | return ((double)tot_bits)/(tot_usecs/1000000); 339 | } 340 | 341 | unsigned long stats_down_total(const torrent_t *torrent) 342 | { 343 | unsigned long ret; 344 | 345 | char torrent_key[sizeof(torrent_t*) + 1]; 346 | torrent_to_key(torrent, torrent_key); 347 | 348 | pthread_mutex_lock(&s_torr_stat_table_lock); 349 | torr_stat_t *stat = *(torr_stat_t**)dict_get(s_torr_stat_table, torrent_key); 350 | ret = stat->total_down; 351 | pthread_mutex_unlock(&s_torr_stat_table_lock); 352 | 353 | return ret; 354 | } 355 | 356 | -------------------------------------------------------------------------------- /src/libbf/stats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef STATS_H 21 | #define STATS_H 22 | 23 | #include 24 | #include 25 | #include "torrent.h" 26 | 27 | int stats_init(void); 28 | void stats_shutdown(void); 29 | 30 | /* Add/remove torrent:stats mapping */ 31 | void stats_add_entry(const torrent_t *torrent); 32 | void stats_remove_entry(const torrent_t *torrent); 33 | 34 | /* Add/remove thread:torrent mapping */ 35 | void stats_register(pthread_t thread, const torrent_t *torrent); 36 | void stats_unregister(pthread_t thread, const torrent_t *torrent); 37 | 38 | /* Wrappers around send/recv to update appropriate stats for torrent */ 39 | ssize_t stats_send(int sockfd, const void *buf, size_t len, int flags); 40 | ssize_t stats_recv(int sockfd, void *buf, size_t len, int flags); 41 | 42 | double stats_up_instrate(const torrent_t *torrent); /* bits/sec */ 43 | double stats_up_avgrate(const torrent_t *torrent); /* bits/sec */ 44 | unsigned long stats_up_total(const torrent_t *torrent); /* bytes */ 45 | double stats_down_instrate(const torrent_t *torrent); /* bits/sec */ 46 | double stats_down_avgrate(const torrent_t *torrent); /* bits/sec*/ 47 | unsigned long stats_down_total(const torrent_t *torrent); /* bytes */ 48 | 49 | #endif 50 | 51 | -------------------------------------------------------------------------------- /src/libbf/thread_reaper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "thread_reaper.h" 21 | #include "log.h" 22 | #include "torrent.h" 23 | #include "peer_connection.h" 24 | 25 | #include 26 | #include 27 | 28 | static void reap_periodic_cleanup(void *arg) 29 | { 30 | free(arg); 31 | } 32 | 33 | static void *reap_periodic(void *arg) 34 | { 35 | reaper_arg_t *rarg = (reaper_arg_t*)arg; 36 | pthread_cleanup_push(reap_periodic_cleanup, arg);{ 37 | 38 | while(true) { 39 | 40 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 41 | sleep(rarg->reap_interval); 42 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 43 | 44 | /* Reap unassociated peers */ 45 | pthread_mutex_lock(rarg->unassoc_peer_lock); 46 | 47 | const list_iter_t *iter = list_iter_first(rarg->unassoc_peers); 48 | while(iter) { 49 | pthread_t thread = *(pthread_t*)list_iter_get_value(iter); 50 | iter = list_iter_next(iter); 51 | 52 | void *ret; 53 | if(pthread_tryjoin_np(thread, &ret) == 0){ 54 | 55 | list_remove(rarg->unassoc_peers, (unsigned char*)&thread); 56 | log_printf(LOG_LEVEL_INFO, "Reaped exited unassociated peer thread\n"); 57 | } 58 | } 59 | pthread_mutex_unlock(rarg->unassoc_peer_lock); 60 | 61 | /* Reap associateed peers */ 62 | pthread_mutex_lock(rarg->torrents_lock); 63 | const unsigned char *entry; 64 | FOREACH_ENTRY(entry, rarg->torrents){ 65 | torrent_t *torrent = *(torrent_t**)entry; 66 | 67 | pthread_mutex_lock(&torrent->sh_lock); 68 | 69 | const list_iter_t *iter = list_iter_first(torrent->sh.peer_connections); 70 | while(iter){ 71 | peer_conn_t *conn = *(peer_conn_t**)list_iter_get_value(iter); 72 | iter = list_iter_next(iter); 73 | 74 | void *ret; 75 | if(pthread_tryjoin_np(conn->thread, &ret) == 0) { 76 | 77 | list_remove(torrent->sh.peer_connections, (unsigned char*)&conn); 78 | log_printf(LOG_LEVEL_INFO, "Reaped exited peer thread\n"); 79 | } 80 | } 81 | pthread_mutex_unlock(&torrent->sh_lock); 82 | 83 | } 84 | pthread_mutex_unlock(rarg->torrents_lock); 85 | } 86 | 87 | }pthread_cleanup_pop(0); 88 | 89 | } 90 | 91 | int thread_reaper_create(pthread_t *thread, reaper_arg_t *arg) 92 | { 93 | if(pthread_create(thread, NULL, reap_periodic, arg)) 94 | goto fail_create_thread; 95 | 96 | return 0; 97 | 98 | fail_create_thread: 99 | log_printf(LOG_LEVEL_ERROR, "Failed to create reaper thread\n"); 100 | return -1; 101 | 102 | } 103 | 104 | -------------------------------------------------------------------------------- /src/libbf/thread_reaper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef THREAD_REAPER_H 21 | #define THREAD_REAPER_H 22 | 23 | #include 24 | #include "list.h" 25 | 26 | typedef struct reaper_arg{ 27 | unsigned reap_interval; 28 | pthread_mutex_t *torrents_lock; 29 | list_t *torrents; 30 | pthread_mutex_t *unassoc_peer_lock; 31 | list_t *unassoc_peers; 32 | }reaper_arg_t; 33 | 34 | int thread_reaper_create(pthread_t *thread, reaper_arg_t *arg); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/libbf/torrent.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "torrent.h" 21 | #include "byte_str.h" 22 | #include "log.h" 23 | #include "dl_file.h" 24 | #include "peer_connection.h" 25 | #include "lbitfield.h" 26 | #include "piece_request.h" 27 | #include "sha1.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include //temp 33 | #include 34 | 35 | #include 36 | #include 37 | 38 | static dict_t *create_piece_dict(byte_str_t *raw); 39 | static int populate_files_from_list(torrent_t *torrent, list_t *files, 40 | const char *destdir, const char *name); 41 | static int populate_from_info_dic(torrent_t *torrent, dict_t *info, const char *destdir); 42 | 43 | 44 | static dict_t *create_piece_dict(byte_str_t *raw) 45 | { 46 | assert(raw->size % 20 == 0); 47 | dict_t *ret = dict_init(raw->size / 20); 48 | if(!ret) 49 | goto fail_alloc_dict; 50 | 51 | for(uint32_t i = 0; i < raw->size; i += 20) { 52 | byte_str_t *entry = byte_str_new(20, raw->str + i); 53 | if(!entry) 54 | goto fail_alloc_str; 55 | char key[9]; 56 | dict_key_for_uint32((i/20), key, sizeof(key)); 57 | dict_add(ret, key, (unsigned char*)&entry, sizeof(byte_str_t*)); 58 | } 59 | 60 | return ret; 61 | 62 | fail_alloc_str: ; 63 | const char *key; 64 | const unsigned char *val; 65 | FOREACH_KEY_AND_VAL(key, val, ret){ 66 | byte_str_free(*(byte_str_t**)val); 67 | } 68 | dict_free(ret); 69 | fail_alloc_dict: 70 | return NULL; 71 | } 72 | 73 | static int populate_files_from_list(torrent_t *torrent, list_t *files, 74 | const char *destdir, const char *name) 75 | { 76 | const unsigned char *entry; 77 | const char *key; 78 | const unsigned char *val; 79 | 80 | assert(name); 81 | 82 | char path[256]; 83 | strcpy(path, destdir); 84 | strcat(path, "/"); 85 | strcat(path, name); 86 | log_printf(LOG_LEVEL_INFO, "Creating directory: %s\n", path); 87 | mkdir(path, 0777); 88 | 89 | FOREACH_ENTRY(entry, files) { 90 | 91 | dict_t *filedict = (*(bencode_obj_t**)entry)->data.dictionary; 92 | unsigned len; 93 | 94 | char path[512]; 95 | strcpy(path, destdir); 96 | strcat(path, "/"); 97 | strcat(path, name); 98 | strcat(path, "/"); 99 | 100 | FOREACH_KEY_AND_VAL(key, val, filedict) { 101 | if(!strcmp(key, "length")) { 102 | len = (*(bencode_obj_t**)val)->data.integer; 103 | } 104 | 105 | if(!strcmp(key, "path")) { 106 | int i = 0; 107 | list_t *pathlist = (*(bencode_obj_t**)val)->data.list; 108 | const unsigned char *path_entry; 109 | 110 | FOREACH_ENTRY(path_entry, pathlist) { 111 | char *str = (char*)(*(bencode_obj_t**)path_entry)->data.string->str; 112 | strcat(path, str); 113 | 114 | if(i < list_get_size(pathlist) - 1) { 115 | mkdir(path, 0777); 116 | strcat(path, "/"); 117 | } 118 | i++; 119 | } 120 | } 121 | 122 | } 123 | 124 | dl_file_t *file = dl_file_create_and_open(len, path); 125 | if(file) 126 | list_add(torrent->files, (unsigned char*)&file, sizeof(dl_file_t*)); 127 | else 128 | return -1; 129 | } 130 | } 131 | 132 | static int populate_from_info_dic(torrent_t *torrent, dict_t *info, const char *destdir) 133 | { 134 | int ret = 0; 135 | char errbuff[64]; 136 | 137 | const char *key; 138 | const unsigned char *val; 139 | 140 | bool multifile = false; 141 | const char *name = NULL; 142 | unsigned len; 143 | 144 | FOREACH_KEY_AND_VAL(key, val, info) { 145 | if(!strcmp(key, "name")) { 146 | name = (char*)(*(bencode_obj_t**)val)->data.string->str; 147 | } 148 | } 149 | 150 | FOREACH_KEY_AND_VAL(key, val, info) { 151 | if(!strcmp(key, "pieces")) { 152 | torrent->pieces = create_piece_dict((*(bencode_obj_t**)val)->data.string); 153 | } 154 | 155 | if(!strcmp(key, "piece length")) { 156 | torrent->piece_len = (*(bencode_obj_t**)val)->data.integer; 157 | } 158 | 159 | if(!strcmp(key, "length")) { 160 | len = (*(bencode_obj_t**)val)->data.integer; 161 | } 162 | 163 | if(!strcmp(key, "files")) { 164 | multifile = true; 165 | 166 | list_t *files = (*(bencode_obj_t**)val)->data.list; 167 | if(populate_files_from_list(torrent, files, destdir, name)) 168 | ret = -1; 169 | } 170 | } 171 | 172 | if(!multifile) { 173 | char path[256]; 174 | strcpy(path, destdir); 175 | strcat(path, "/"); 176 | strcat(path, name); 177 | 178 | dl_file_t *file = dl_file_create_and_open(len, path); 179 | if(file) 180 | list_add(torrent->files, (unsigned char*)&file, sizeof(dl_file_t*)); 181 | else 182 | ret = -1; 183 | } 184 | 185 | if(ret && errno) { 186 | strerror_r(errno, errbuff, sizeof(errbuff)); 187 | log_printf(LOG_LEVEL_ERROR, "%s\n", errbuff); 188 | } 189 | return ret; 190 | } 191 | 192 | torrent_t *torrent_init(bencode_obj_t *meta, const char *name, const char *destdir) 193 | { 194 | torrent_t *ret = malloc(sizeof(torrent_t)); 195 | if(!ret) 196 | goto fail_alloc; 197 | 198 | const char *key; 199 | const unsigned char *val; 200 | 201 | memset(ret, 0, sizeof(*ret)); 202 | FOREACH_KEY_AND_VAL(key, val, meta->data.dictionary) { 203 | 204 | if(!strcmp(key, "info")) { 205 | bencode_obj_t *info_dic = *((bencode_obj_t**)val); 206 | memcpy(ret->info_hash, info_dic->sha1, DIGEST_LEN); 207 | 208 | ret->files = list_init(); 209 | populate_from_info_dic(ret, info_dic->data.dictionary, destdir); 210 | } 211 | 212 | if(!strcmp(key, "announce")) { 213 | byte_str_t *bstr = (*(bencode_obj_t**)val)->data.string; 214 | ret->announce = malloc(bstr->size + 1); 215 | memcpy(ret->announce, bstr->str, bstr->size); 216 | ret->announce[bstr->size] = '\0'; 217 | } 218 | 219 | if(!strcmp(key, "comment")) { 220 | byte_str_t *bstr = (*(bencode_obj_t**)val)->data.string; 221 | ret->comment = malloc(bstr->size + 1); 222 | memcpy(ret->comment, bstr->str, bstr->size); 223 | ret->comment[bstr->size] = '\0'; 224 | } 225 | 226 | if(!strcmp(key, "created by")) { 227 | byte_str_t *bstr = (*(bencode_obj_t**)val)->data.string; 228 | ret->created_by = malloc(bstr->size + 1); 229 | memcpy(ret->created_by, bstr->str, bstr->size); 230 | ret->created_by[bstr->size] = '\0'; 231 | } 232 | 233 | if(!strcmp(key, "creation date")) { 234 | ret->create_date = (*(bencode_obj_t**)val)->data.integer; 235 | } 236 | 237 | if(!strcmp(key, "announce-list")) { 238 | log_printf(LOG_LEVEL_WARNING, "Ignoring announce-list key in metainfo file\n"); 239 | //TODO 240 | } 241 | 242 | if(!strcmp(key, "encoding")) { 243 | log_printf(LOG_LEVEL_WARNING, "Ignoring encoding key in metainfo file\n"); 244 | //assert(0); 245 | //TODO 246 | } 247 | 248 | } 249 | 250 | assert(ret->announce); 251 | 252 | pthread_mutex_init(&ret->sh_lock, NULL); 253 | ret->max_peers = DEFAULT_MAX_PEERS; 254 | ret->sh.peer_connections = list_init(); 255 | ret->sh.piece_states = malloc(dict_get_size(ret->pieces)); 256 | memset(ret->sh.piece_states, PIECE_STATE_NOT_REQUESTED, dict_get_size(ret->pieces)); 257 | ret->sh.pieces_left = dict_get_size(ret->pieces); 258 | ret->sh.priority = DEFAULT_PRIORITY; 259 | ret->sh.state = TORRENT_STATE_LEECHING; 260 | ret->sh.completed = false; 261 | 262 | ret->name = malloc(strlen(name) + 1); 263 | strcpy(ret->name, name); 264 | 265 | return ret; 266 | 267 | fail_alloc: 268 | return NULL; 269 | } 270 | 271 | void torrent_free(torrent_t *torrent) 272 | { 273 | const char *key; 274 | const unsigned char *entry; 275 | 276 | pthread_mutex_destroy(&torrent->sh_lock); 277 | 278 | FOREACH_KEY_AND_VAL(key, entry, torrent->pieces){ 279 | byte_str_free(*(byte_str_t**)entry); 280 | } 281 | dict_free(torrent->pieces); 282 | if(torrent->sh.piece_states) 283 | free(torrent->sh.piece_states); 284 | 285 | FOREACH_ENTRY(entry, torrent->files){ 286 | dl_file_close_and_free(*(dl_file_t**)entry); 287 | } 288 | list_free(torrent->files); 289 | 290 | FOREACH_ENTRY(entry, torrent->sh.peer_connections) { 291 | free(*(peer_conn_t**)entry); 292 | } 293 | list_free(torrent->sh.peer_connections); 294 | 295 | if(torrent->announce) 296 | free(torrent->announce); 297 | 298 | if(torrent->comment) 299 | free(torrent->comment); 300 | 301 | if(torrent->created_by) 302 | free(torrent->created_by); 303 | 304 | free(torrent->name); 305 | 306 | free(torrent); 307 | } 308 | 309 | unsigned char *torrent_make_bitfield(const torrent_t *torrent) 310 | { 311 | unsigned num_pieces = dict_get_size(torrent->pieces); 312 | unsigned len = LBITFIELD_NUM_BYTES(num_pieces); 313 | unsigned char *ret = calloc(len, 1); 314 | 315 | if(!ret) 316 | return ret; 317 | 318 | for(int i = 0; i < num_pieces; i++) { 319 | if(torrent->sh.piece_states[i] == PIECE_STATE_HAVE) 320 | LBITFIELD_SET(i, ret); 321 | } 322 | return ret; 323 | } 324 | 325 | 326 | bool torrent_sha1_verify(const torrent_t *torrent, unsigned index) 327 | { 328 | assert(index < dict_get_size(torrent->pieces)); 329 | 330 | char key[9]; 331 | dict_key_for_uint32((uint32_t)index, key, sizeof(key)); 332 | byte_str_t *piece_hash = *(byte_str_t**)dict_get(torrent->pieces, key); 333 | 334 | piece_request_t *pr = piece_request_create(torrent, index); 335 | sha1_context_t *ctx = sha1_context_init(); 336 | 337 | const unsigned char *entry; 338 | FOREACH_ENTRY(entry, pr->block_requests){ 339 | block_request_t *br = *(block_request_t**)entry; 340 | 341 | const unsigned char *mementry; 342 | FOREACH_ENTRY(mementry, br->filemems){ 343 | filemem_t fmem = *(filemem_t*)mementry; 344 | 345 | sha1_update(ctx, fmem.mem, fmem.size); 346 | } 347 | } 348 | unsigned char sha1_digest[DIGEST_LEN]; 349 | sha1_finish(ctx, sha1_digest); 350 | 351 | sha1_context_free(ctx); 352 | piece_request_free(pr); 353 | return (memcmp(piece_hash->str, sha1_digest, DIGEST_LEN) == 0); 354 | } 355 | 356 | /* TODO: Eventually add keeping of piece frequency in the torrent and change piece selection to 357 | * use rarest first algorithm. Also add more fine-grained locking for piece states */ 358 | int torrent_next_request(torrent_t *torrent, unsigned char *peer_have_bf, unsigned *out) 359 | { 360 | unsigned nr, r; 361 | bool has_nr = false, has_r = false; 362 | unsigned ret; 363 | 364 | pthread_mutex_lock(&torrent->sh_lock); 365 | for(int i = 0; i < dict_get_size(torrent->pieces); i++){ 366 | 367 | if(torrent->sh.piece_states[i] == PIECE_STATE_REQUESTED && 368 | LBITFIELD_ISSET(i, peer_have_bf)) { 369 | r = i; 370 | has_r = true; 371 | } 372 | 373 | if(torrent->sh.piece_states[i] == PIECE_STATE_NOT_REQUESTED && 374 | LBITFIELD_ISSET(i, peer_have_bf)) { 375 | nr = i; 376 | has_nr = true; 377 | break; 378 | } 379 | } 380 | 381 | if(!has_nr && !has_r){ 382 | pthread_mutex_unlock(&torrent->sh_lock); 383 | return -1; 384 | } 385 | 386 | ret = has_nr ? nr : r; 387 | torrent->sh.piece_states[ret] = PIECE_STATE_REQUESTED; 388 | 389 | pthread_mutex_unlock(&torrent->sh_lock); 390 | 391 | log_printf(LOG_LEVEL_INFO, "Requesting piece: %u\n", has_nr ? nr : r); 392 | 393 | *out = ret; 394 | return 0; 395 | } 396 | 397 | int torrent_complete(torrent_t *torrent) 398 | { 399 | pthread_mutex_lock(&torrent->sh_lock); 400 | torrent->sh.completed = true; 401 | torrent->sh.state = TORRENT_STATE_SEEDING; 402 | pthread_mutex_unlock(&torrent->sh_lock); 403 | 404 | const unsigned char *entry; 405 | FOREACH_ENTRY(entry, torrent->files){ 406 | dl_file_t *file = *(dl_file_t**)entry; 407 | dl_file_complete(file); 408 | } 409 | log_printf(LOG_LEVEL_INFO, "Torrent completed!\n"); 410 | 411 | //TODO: send an immediate "completed" event to the tracker at this point 412 | } 413 | 414 | unsigned torrent_left_to_download(torrent_t *torrent) 415 | { 416 | unsigned ret; 417 | pthread_mutex_lock(&torrent->sh_lock); 418 | ret = torrent->sh.pieces_left * PEER_REQUEST_SIZE; 419 | pthread_mutex_unlock(&torrent->sh_lock); 420 | return ret; 421 | } 422 | 423 | //temp 424 | void print_torrent(torrent_t *torrent) 425 | { 426 | printf("TORRENT DETAILS:\n"); 427 | printf("\tname: %s", torrent->name); 428 | printf("\tpieces: %p, size: %u\n", torrent->pieces, dict_get_size(torrent->pieces)); 429 | printf("\tpiece len: %u\n", torrent->piece_len); 430 | printf("\tfiles: %p, size: %u\n", torrent->files, list_get_size(torrent->files)); 431 | printf("\tpeer connections: %p, size: %u\n", torrent->sh.peer_connections, 432 | list_get_size(torrent->sh.peer_connections)); 433 | printf("\tpriority: %u\n", torrent->sh.priority); 434 | printf("\tstate: %d\n", torrent->sh.state); 435 | printf("\tcompleted: %hhd\n", torrent->sh.completed); 436 | printf("\tinfo hash: "); 437 | for(int i = 0; i < 20; i++) { 438 | printf("%02X", (unsigned char)torrent->info_hash[i]); 439 | } 440 | printf("\n"); 441 | printf("\tannounce: %s\n", torrent->announce); 442 | printf("\tcomment: %s\n", torrent->comment); 443 | printf("\tcreated by: %s\n", torrent->created_by); 444 | printf("\tcreate date: %u\n", torrent->create_date); 445 | } 446 | -------------------------------------------------------------------------------- /src/libbf/torrent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef TORRENT_H 21 | #define TORRENT_H 22 | 23 | #include 24 | #include "list.h" 25 | #include "tracker_connection.h" 26 | #include "bencode.h" 27 | 28 | #define DEFAULT_PRIORITY 3 29 | #define DEFAULT_MAX_PEERS 50 30 | 31 | typedef enum { 32 | TORRENT_STATE_LEECHING, 33 | TORRENT_STATE_SEEDING, 34 | TORRENT_STATE_PAUSED 35 | }torrent_state_t; 36 | 37 | typedef enum { 38 | PIECE_STATE_NOT_REQUESTED, 39 | PIECE_STATE_REQUESTED, 40 | PIECE_STATE_HAVE 41 | }piece_state_t; 42 | 43 | typedef struct torrent { 44 | dict_t *pieces; 45 | unsigned piece_len; 46 | list_t *files; 47 | char info_hash[20]; 48 | char *name; 49 | char *announce; 50 | char *comment; 51 | char *created_by; 52 | uint32_t create_date; 53 | pthread_t tracker_thread; 54 | unsigned max_peers; 55 | struct { 56 | torrent_state_t state; 57 | char *piece_states; 58 | unsigned pieces_left; 59 | list_t *peer_connections; 60 | unsigned priority; /* [0-6] */ 61 | bool completed; 62 | }sh; 63 | pthread_mutex_t sh_lock; 64 | }torrent_t; 65 | 66 | torrent_t *torrent_init(bencode_obj_t *meta, const char *name, const char *destdir); 67 | void torrent_free(torrent_t *torrent); 68 | unsigned char *torrent_make_bitfield(const torrent_t *torrent); 69 | bool torrent_sha1_verify(const torrent_t *torrent, unsigned index); 70 | /* sh_lock of torrent is taken in this function */ 71 | int torrent_next_request(torrent_t *torrent, unsigned char *peer_have_bf, unsigned *out); 72 | /* sh_lock of torrent is taken in this function */ 73 | int torrent_complete(torrent_t *torrent); 74 | /* sh_lock of torrent is taken in this function */ 75 | unsigned torrent_left_to_download(torrent_t *torrent); 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/libbf/torrent_file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "torrent_file.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | typedef struct torrent_file{ 33 | int fd; 34 | size_t size; 35 | unsigned char *data; 36 | }torrent_file_t; 37 | 38 | static torrent_file_t *torrent_file_open(const char *path) 39 | { 40 | unsigned char *mem; 41 | int fd; 42 | struct stat stats; 43 | 44 | fd = open(path, O_RDWR); 45 | if(fd < 0) 46 | goto fail_open; 47 | fstat(fd, &stats); 48 | 49 | mem = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0); 50 | if(!mem) 51 | goto fail_map; 52 | 53 | torrent_file_t *file = malloc(sizeof(torrent_file_t)); 54 | if(!file) 55 | goto fail_alloc; 56 | 57 | file->fd = fd; 58 | file->size = stats.st_size; 59 | file->data = mem; 60 | 61 | return file; 62 | 63 | fail_alloc: 64 | munmap(file->data, file->size); 65 | fail_map: 66 | close(fd); 67 | fail_open: 68 | return NULL; 69 | } 70 | 71 | static int torrent_file_close_and_free(torrent_file_t *file) 72 | { 73 | if(munmap(file->data, file->size)) 74 | goto fail; 75 | 76 | if(!close(file->fd)) 77 | goto fail; 78 | 79 | free(file); 80 | return 0; 81 | 82 | fail: 83 | free(file); 84 | return -1; 85 | } 86 | 87 | bencode_obj_t *torrent_file_parse(const char *path) 88 | { 89 | torrent_file_t *file; 90 | bencode_obj_t *ret; 91 | 92 | file = torrent_file_open(path); 93 | if(!file) 94 | goto fail_open; 95 | 96 | const char *endptr; 97 | ret = bencode_parse_object(file->data, &endptr); 98 | assert(endptr = file->data + file->size); 99 | 100 | torrent_file_close_and_free(file); 101 | return ret; 102 | 103 | fail_open: 104 | return NULL; 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/libbf/torrent_file.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef TORRENT_FILE_H 21 | #define TORRENT_FILE_H 22 | 23 | #include "bencode.h" 24 | 25 | bencode_obj_t *torrent_file_parse(const char *path); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/libbf/tracker_announce.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "tracker_announce.h" 21 | #include "url.h" 22 | #include "tracker_udp.h" 23 | #include "tracker_http.h" 24 | #include "log.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | static int tracker_connect(url_t *url); 39 | 40 | static int tracker_connect(url_t *url) 41 | { 42 | struct addrinfo hints, *tracker, *head;; 43 | int sockfd; 44 | 45 | memset(&hints, 0, sizeof(struct addrinfo)); 46 | hints.ai_family = AF_UNSPEC; 47 | hints.ai_socktype = url->protocol == PROTOCOL_HTTP ? SOCK_STREAM : SOCK_DGRAM; 48 | 49 | char port[6]; 50 | int n = snprintf(port, sizeof(port), "%hu", url->port); 51 | port[n] = '\0'; 52 | 53 | int rv; 54 | if(rv = getaddrinfo(url->hostname, port, &hints, &head)) 55 | goto fail_getaddrinfo; 56 | 57 | for(tracker = head; tracker; tracker = tracker->ai_next) { 58 | if((sockfd = socket(tracker->ai_family, tracker->ai_socktype, 59 | tracker->ai_protocol)) < 0) { 60 | continue; 61 | } 62 | 63 | if(connect(sockfd, tracker->ai_addr, tracker->ai_addrlen) < 0) { 64 | close(sockfd); 65 | continue; 66 | } 67 | 68 | break; 69 | } 70 | 71 | if(!tracker){ 72 | log_printf(LOG_LEVEL_ERROR, "Unable to connect to tracker: %s\n", url->hostname); 73 | goto fail_connect; 74 | } 75 | 76 | freeaddrinfo(head); 77 | errno = 0; 78 | log_printf(LOG_LEVEL_INFO, "Successfully connected (socket fd: %d) to tracker: %s\n", 79 | sockfd, url->hostname); 80 | return sockfd; 81 | 82 | fail_connect: 83 | freeaddrinfo(head); 84 | fail_getaddrinfo: 85 | return -1; 86 | } 87 | 88 | tracker_announce_resp_t *tracker_announce(const char *urlstr, tracker_announce_request_t *request) 89 | { 90 | int sockfd; 91 | tracker_announce_resp_t *ret; 92 | char errbuff[64]; 93 | 94 | log_printf(LOG_LEVEL_INFO, "Announcing to tracker: %s\n", urlstr); 95 | 96 | url_t *url = url_from_str(urlstr); 97 | if(!url) 98 | goto fail_parse_url; 99 | 100 | if(url->protocol == PROTOCOL_HTTPS){ 101 | log_printf(LOG_LEVEL_ERROR, "No support for HTTPS tracker protocol\n"); 102 | goto fail_protocol; 103 | } 104 | 105 | if((sockfd = tracker_connect(url)) < 0) 106 | goto fail_connect; 107 | 108 | if(url->protocol == PROTOCOL_UDP) { 109 | ret = tracker_udp_announce(sockfd, request); 110 | }else if(url->protocol == PROTOCOL_HTTP) { 111 | ret = tracker_http_announce(sockfd, url, request); 112 | } 113 | 114 | close(sockfd); 115 | url_free(url); 116 | return ret; 117 | 118 | fail_connect: 119 | fail_protocol: 120 | url_free(url); 121 | fail_parse_url: 122 | if(errno) { 123 | strerror_r(errno, errbuff, sizeof(errbuff)); 124 | log_printf(LOG_LEVEL_ERROR, "%s\n", errbuff); 125 | } 126 | return NULL; 127 | } 128 | 129 | void tracker_announce_request_free(tracker_announce_request_t *req) 130 | { 131 | if(HAS(req, REQUEST_HAS_TRACKER_ID)) 132 | free(req->tracker_id); 133 | 134 | if(HAS(req, REQUEST_HAS_KEY)) 135 | free(req->key); 136 | 137 | free(req); 138 | } 139 | 140 | void tracker_announce_resp_free(tracker_announce_resp_t *resp) 141 | { 142 | if(HAS(resp, RESPONSE_HAS_TRACKER_ID)) 143 | free(resp->tracker_id); 144 | 145 | if(HAS(resp, RESPONSE_HAS_FAILURE_REASON)) 146 | free(resp->failure_reason); 147 | 148 | if(HAS(resp, RESPONSE_HAS_WARNING_MESSAGE)) 149 | free(resp->warning_message); 150 | 151 | const unsigned char *entry; 152 | FOREACH_ENTRY(entry, resp->peers) { 153 | peer_t *peer = *((peer_t**)entry); 154 | free(peer); 155 | } 156 | list_free(resp->peers); 157 | 158 | free(resp); 159 | } 160 | 161 | //TEMP 162 | 163 | void print_tracker_response(tracker_announce_resp_t *resp) 164 | { 165 | printf("TRACKER RESPONSE:\n"); 166 | printf("\tinterval: %u\n", resp->interval); 167 | if(HAS(resp, RESPONSE_HAS_TRACKER_ID)) 168 | printf("\ttracker_id: %s\n", resp->tracker_id); 169 | printf("\tcomplete: %u\n", resp->complete); 170 | printf("\tincomplete: %u\n", resp->incomplete); 171 | printf("\tpeers: %p, size: %u\n", resp->peers, list_get_size(resp->peers)); 172 | 173 | const unsigned char *entry; 174 | FOREACH_ENTRY(entry, resp->peers) { 175 | peer_t *peer = *((peer_t**)entry); 176 | 177 | char buff[INET6_ADDRSTRLEN]; 178 | uint16_t port; 179 | 180 | if(peer->addr.sas.ss_family == AF_INET) { 181 | inet_ntop(AF_INET, &peer->addr.sa_in.sin_addr, buff, INET_ADDRSTRLEN); 182 | port = ntohs(peer->addr.sa_in.sin_port); 183 | }else{ 184 | inet_ntop(AF_INET6, &peer->addr.sa_in6.sin6_addr, buff, INET6_ADDRSTRLEN); 185 | port = ntohs(peer->addr.sa_in6.sin6_port); 186 | } 187 | printf("\t\tpeer: %s [port: %u]\n", buff, port); 188 | } 189 | 190 | if(HAS(resp, RESPONSE_HAS_FAILURE_REASON)) 191 | printf("\tfailure reason: %s\n", resp->failure_reason); 192 | if(HAS(resp, RESPONSE_HAS_WARNING_MESSAGE)) 193 | printf("\twarning message: %s\n", resp->warning_message); 194 | } 195 | 196 | -------------------------------------------------------------------------------- /src/libbf/tracker_announce.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef TRACKER_ANNOUNCE_H 21 | #define TRACKER_ANNOUNCE_H 22 | 23 | #include "list.h" 24 | #include "byte_str.h" 25 | #include "peer.h" 26 | #include 27 | 28 | typedef enum { 29 | TORRENT_EVENT_NONE = 0, 30 | TORRENT_EVENT_COMPLETED = 1, 31 | TORRENT_EVENT_STARTED = 2, 32 | TORRENT_EVENT_STOPPED = 3 33 | }torrent_event_t; 34 | 35 | enum { 36 | REQUEST_HAS_IP = (1 << 0), 37 | REQUEST_HAS_NUMWANT = (1 << 1), 38 | REQUEST_HAS_NO_PEER_ID = (1 << 2), 39 | REQUEST_HAS_COMPACT = (1 << 3), 40 | REQUEST_HAS_KEY = (1 << 4), 41 | REQUEST_HAS_TRACKER_ID = (1 << 5) 42 | }; 43 | 44 | enum { 45 | RESPONSE_HAS_FAILURE_REASON = (1 << 0), 46 | RESPONSE_HAS_WARNING_MESSAGE = (1 << 1), 47 | RESPONSE_HAS_MIN_INTERVAL = (1 << 2), 48 | RESPONSE_HAS_TRACKER_ID = (1 << 3) 49 | }; 50 | 51 | #define SET_HAS(_ptr, _has) ((_ptr)->has |= (_has)) 52 | #define CLR_HAS(_ptr, _has) ((_ptr)->has &= ~(_has)) 53 | #define HAS(_ptr, _has) !!((_ptr)->has & (_has)) 54 | 55 | typedef struct tracker_announce_request { 56 | char has; 57 | char info_hash[20]; 58 | char peer_id[20]; 59 | struct sockaddr_in ip; 60 | uint16_t port; 61 | unsigned long uploaded; 62 | unsigned long downloaded; 63 | unsigned long left; 64 | torrent_event_t event; 65 | unsigned numwant; 66 | bool no_peer_id; 67 | bool compact; 68 | char *key; 69 | char *tracker_id; 70 | }tracker_announce_request_t; 71 | 72 | typedef struct tracker_announce_resp { 73 | char has; 74 | unsigned interval; 75 | char *tracker_id; 76 | unsigned complete; 77 | unsigned incomplete; 78 | list_t *peers; 79 | char *failure_reason; 80 | char *warning_message; 81 | int64_t min_interval; 82 | }tracker_announce_resp_t; 83 | 84 | tracker_announce_resp_t *tracker_announce(const char *urlstr, tracker_announce_request_t *request); 85 | void tracker_announce_request_free(tracker_announce_request_t *req); 86 | void tracker_announce_resp_free(tracker_announce_resp_t *resp); 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/libbf/tracker_connection.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "tracker_connection.h" 21 | #include "torrent.h" 22 | #include "byte_str.h" 23 | #include "tracker_announce.h" 24 | #include "peer_id.h" 25 | #include "log.h" 26 | #include "peer_connection.h" 27 | #include "stats.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include //temp 33 | #include //temp 34 | 35 | #include 36 | 37 | #define TRACKER_RETRY_INTERVAL 15 38 | 39 | static tracker_announce_request_t *create_tracker_request(const void *arg) 40 | { 41 | const tracker_arg_t *targ = (tracker_arg_t*)arg; 42 | 43 | tracker_announce_request_t *ret = malloc(sizeof(tracker_announce_request_t)); 44 | if(ret) { 45 | 46 | ret->has = 0; 47 | memcpy(ret->info_hash, targ->torrent->info_hash, sizeof(ret->info_hash)); 48 | memcpy(ret->peer_id, g_local_peer_id, sizeof(ret->peer_id)); 49 | ret->port = targ->port; 50 | ret->compact = true; 51 | SET_HAS(ret, REQUEST_HAS_COMPACT); 52 | ret->event = TORRENT_EVENT_NONE; 53 | 54 | pthread_mutex_lock(&targ->torrent->sh_lock); 55 | unsigned num_conns = list_get_size(targ->torrent->sh.peer_connections); 56 | pthread_mutex_unlock(&targ->torrent->sh_lock); 57 | 58 | ret->numwant = targ->torrent->max_peers - num_conns; 59 | SET_HAS(ret, REQUEST_HAS_NUMWANT); 60 | 61 | ret->uploaded = stats_up_total(targ->torrent); 62 | ret->downloaded = stats_down_total(targ->torrent); 63 | ret->left = torrent_left_to_download(targ->torrent); 64 | } 65 | 66 | return ret; 67 | } 68 | 69 | static int create_peer_connection(peer_t *peer, torrent_t *torrent) 70 | { 71 | peer_conn_t *conn = malloc(sizeof(peer_conn_t)); 72 | if(!conn) 73 | return -1; 74 | conn->peer = *peer; 75 | 76 | peer_arg_t *arg = malloc(sizeof(peer_arg_t)); 77 | if(!arg) { 78 | free(conn); 79 | return -1; 80 | } 81 | arg->torrent = torrent; 82 | arg->has_torrent = true; 83 | arg->has_sockfd = false; 84 | arg->peer = *peer; 85 | 86 | if(peer_connection_create(&conn->thread, arg)) 87 | goto fail_create; 88 | 89 | pthread_mutex_lock(&torrent->sh_lock); 90 | 91 | unsigned num_conns = list_get_size(torrent->sh.peer_connections); 92 | if(num_conns == torrent->max_peers) { 93 | pthread_mutex_unlock(&torrent->sh_lock); 94 | free(arg); 95 | free(conn); 96 | return 0; 97 | } 98 | 99 | list_add(torrent->sh.peer_connections, (unsigned char*)&conn, sizeof(peer_conn_t*)); 100 | pthread_mutex_unlock(&torrent->sh_lock); 101 | 102 | return 0; 103 | 104 | fail_create: 105 | log_printf(LOG_LEVEL_ERROR, "Failed to create peer thread\n"); 106 | free(arg); 107 | free(conn); 108 | return -1; 109 | } 110 | 111 | static void periodic_announce_cleanup(void *arg) 112 | { 113 | log_printf(LOG_LEVEL_INFO, "Sending one last \"stopped\" event to tracker\n"); 114 | const tracker_arg_t *targ = (tracker_arg_t*)arg; 115 | 116 | tracker_announce_request_t *req = create_tracker_request(arg); 117 | req->event = TORRENT_EVENT_STOPPED; 118 | 119 | tracker_announce_resp_t *resp = tracker_announce(targ->torrent->announce, req); 120 | 121 | tracker_announce_request_free(req); 122 | if(resp) 123 | tracker_announce_resp_free(resp); 124 | free(arg); 125 | } 126 | 127 | static void *periodic_announce(void *arg) 128 | { 129 | const tracker_arg_t *targ = (tracker_arg_t*)arg; 130 | bool completed = false; 131 | unsigned interval; 132 | 133 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 134 | pthread_cleanup_push(periodic_announce_cleanup, arg); 135 | 136 | pthread_mutex_lock(&targ->torrent->sh_lock); 137 | completed = targ->torrent->sh.completed; 138 | pthread_mutex_unlock(&targ->torrent->sh_lock); 139 | 140 | bool started = false; 141 | while(true) { 142 | tracker_announce_request_t *req = create_tracker_request(arg); 143 | tracker_announce_resp_t *resp; 144 | 145 | if(!started){ 146 | req->event = TORRENT_EVENT_STARTED; 147 | started = true; 148 | } 149 | 150 | pthread_mutex_lock(&targ->torrent->sh_lock); 151 | bool read_completed = targ->torrent->sh.completed; 152 | pthread_mutex_unlock(&targ->torrent->sh_lock); 153 | 154 | if(completed == false && read_completed == true) { 155 | req->event = TORRENT_EVENT_COMPLETED; 156 | } 157 | completed = read_completed; 158 | 159 | resp = tracker_announce(targ->torrent->announce, req); 160 | 161 | if(resp) { 162 | //extern void print_tracker_response(tracker_announce_resp_t *resp); 163 | //print_tracker_response(resp); 164 | 165 | interval = resp->interval; 166 | log_printf(LOG_LEVEL_DEBUG, "Re-announcing to tracker again in %d seconds\n", interval); 167 | 168 | const unsigned char *entry; 169 | FOREACH_ENTRY(entry, resp->peers) { 170 | create_peer_connection(*(peer_t**)entry, targ->torrent); 171 | } 172 | }else{ 173 | interval = TRACKER_RETRY_INTERVAL; 174 | log_printf(LOG_LEVEL_INFO, "Retrying announcing to tracker in %d seconds\n", interval); 175 | } 176 | 177 | tracker_announce_request_free(req); 178 | if(resp) 179 | tracker_announce_resp_free(resp); 180 | 181 | pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 182 | /* Cancellation point */ 183 | sleep(interval); 184 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 185 | 186 | } 187 | 188 | pthread_cleanup_pop(0); 189 | } 190 | 191 | int tracker_connection_create(pthread_t *thread, tracker_arg_t *arg) 192 | { 193 | int rv; 194 | if(pthread_create(thread, NULL, periodic_announce, (void*)arg)) 195 | goto fail_create_thread; 196 | 197 | return 0; 198 | 199 | fail_create_thread: 200 | return -1; 201 | } 202 | 203 | -------------------------------------------------------------------------------- /src/libbf/tracker_connection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef TRACKER_CONNECTION_H 21 | #define TRACKER_CONNECTION_H 22 | 23 | #include 24 | #include 25 | 26 | struct torrent; 27 | 28 | typedef struct tracker_arg { 29 | struct torrent *torrent; 30 | uint16_t port; 31 | }tracker_arg_t; 32 | 33 | int tracker_connection_create(pthread_t *thread, tracker_arg_t *arg); 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/libbf/tracker_http.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "tracker_http.h" 21 | #include "log.h" 22 | #include "tracker_resp_parser.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | static bool is_valid_url_char(const unsigned char c); 36 | static int print_url_encoded_char(char *out, size_t n, unsigned char c); 37 | static int print_url_encoded_str(char *out, size_t n, const unsigned char *orig, size_t olen); 38 | static char *build_http_request(url_t *url, tracker_announce_request_t *request); 39 | static byte_str_t *content_from_chunked(char *buff); 40 | static byte_str_t *content_from_tracker_resp(char *buff, size_t len); 41 | static int tracker_sendall(int sockfd, const char *buff, size_t len); 42 | static int tracker_recv_resp(int sockfd, byte_str_t **outcont); 43 | 44 | 45 | static bool is_valid_url_char(const unsigned char c) 46 | { 47 | return isdigit(c) || isalpha(c) || (c == '.') || (c == '-') || (c == '_') || (c == '~'); 48 | } 49 | 50 | static int print_url_encoded_char(char *out, size_t n, unsigned char c) 51 | { 52 | return snprintf(out, n, "%%%1X%1X", (c >> 4), (c & 0xF)); 53 | } 54 | 55 | static int print_url_encoded_str(char *out, size_t n, const unsigned char *orig, size_t olen) 56 | { 57 | int written = 0; 58 | for(const unsigned char *i = orig; i < orig + olen; i++) { 59 | if(is_valid_url_char(*i)) 60 | written += snprintf(out + written, n - written, "%1c", (char)(*i)); 61 | else 62 | written += print_url_encoded_char(out + written, n - written, *i); 63 | } 64 | return written; 65 | } 66 | 67 | static char *build_http_request(url_t *url, tracker_announce_request_t *request) 68 | { 69 | int written = 0; 70 | char buff[512]; 71 | 72 | written += snprintf(buff, sizeof(buff), "GET /%s", url->path); 73 | 74 | written += snprintf(buff + written, sizeof(buff) - written, "?info_hash="); 75 | written += print_url_encoded_str(buff + written, sizeof(buff) - written, request->info_hash, 20); 76 | 77 | written += snprintf(buff + written, sizeof(buff) - written, "&peer_id="); 78 | written += print_url_encoded_str(buff + written, sizeof(buff) - written, request->peer_id, 20); 79 | 80 | written += snprintf(buff + written, sizeof(buff) - written, "&port=%hu", request->port); 81 | written += snprintf(buff + written, sizeof(buff) - written, "&uploaded=%lu", request->uploaded); 82 | written += snprintf(buff + written, sizeof(buff) - written, "&downloaded=%lu", request->downloaded); 83 | written += snprintf(buff + written, sizeof(buff) - written, "&left=%lu", request->left); 84 | 85 | if(HAS(request, REQUEST_HAS_COMPACT)) { 86 | written += snprintf(buff + written, sizeof(buff) - written, "&compact=%1hhu", !!(request->compact)); 87 | } 88 | 89 | if(HAS(request, REQUEST_HAS_NO_PEER_ID)) { 90 | written += snprintf(buff + written, sizeof(buff) - written, "&no_peer_id=%1hhu", 91 | !!(request->no_peer_id)); 92 | } 93 | 94 | if(request->event != TORRENT_EVENT_NONE) { 95 | char *event_str; 96 | switch(request->event) { 97 | case TORRENT_EVENT_STARTED: 98 | event_str = "started"; break; 99 | case TORRENT_EVENT_COMPLETED: 100 | event_str = "completed"; break; 101 | case TORRENT_EVENT_STOPPED: 102 | event_str = "stopped"; break; 103 | default: 104 | assert(0); 105 | } 106 | written += snprintf(buff + written, sizeof(buff) - written, "&event=%s", event_str); 107 | } 108 | 109 | #if 0 //TODO 110 | if(HAS(request, REQUEST_HAS_IP)) { 111 | } 112 | #endif 113 | 114 | if(HAS(request, REQUEST_HAS_NUMWANT)) { 115 | written += snprintf(buff + written, sizeof(buff) - written, "&numwant=%u", request->numwant); 116 | } 117 | 118 | if(HAS(request, REQUEST_HAS_KEY)) { 119 | written += snprintf(buff + written, sizeof(buff) - written, "&key=%s", request->key); 120 | } 121 | 122 | if(HAS(request, REQUEST_HAS_TRACKER_ID)) { 123 | written += snprintf(buff + written, sizeof(buff) - written, "&trackerid=%s", request->tracker_id); 124 | } 125 | 126 | written += snprintf(buff + written, sizeof(buff) - written, " HTTP/1.1\r\n"); 127 | written += snprintf(buff + written, sizeof(buff) - written, "Host: %s\r\n\r\n", url->hostname); 128 | 129 | assert(written < sizeof(buff)); 130 | buff[written] = '\0'; 131 | 132 | char *ret = malloc(written + 1); 133 | if(ret){ 134 | strcpy(ret, buff); 135 | } 136 | return ret; 137 | } 138 | 139 | static byte_str_t *content_from_chunked(char *buff) 140 | { 141 | char newbuff[2048]; 142 | char *line,*saveptr; 143 | size_t chunk_sz; 144 | size_t newsize = 0; 145 | 146 | line = strtok_r(buff, "\r\n", &saveptr); 147 | chunk_sz = strtoul(line, (char**)NULL, 16); 148 | while(chunk_sz > 0) { 149 | line = strtok_r(NULL, "\r\n", &saveptr); 150 | memcpy(newbuff + newsize, line, chunk_sz); 151 | newsize += chunk_sz; 152 | 153 | line = strtok_r(NULL, "\r\n", &saveptr); 154 | chunk_sz = strtoul(line, (char**)NULL, 16); 155 | } 156 | 157 | byte_str_t *ret = byte_str_new(newsize, newbuff); 158 | return ret; 159 | } 160 | 161 | static byte_str_t *content_from_tracker_resp(char *buff, size_t len) 162 | { 163 | char *line,*saveptr; 164 | char *token, *saveptrtok; 165 | unsigned cont_len = 0; 166 | bool chunked = false; 167 | 168 | line = strtok_r(buff, "\n", &saveptr); 169 | if(strncmp(line, "HTTP/1.0 200 OK", strlen("HTTP/1.0 200 OK")) && 170 | strncmp(line, "HTTP/1.1 200 OK", strlen("HTTP/1.1 200 OK"))) 171 | goto fail_parse; 172 | 173 | do { 174 | line = strtok_r(NULL, "\n", &saveptr); 175 | 176 | if(!strncmp(line, "Transfer-Encoding: chunked", strlen("Transfer-Encoding: chunked"))){ 177 | chunked = true; 178 | } 179 | 180 | if(!strncmp(line, "Content-Length:", strlen("Content-Length:"))) { 181 | token = strtok_r(line, ":", &saveptrtok); 182 | token = strtok_r(NULL, ":", &saveptrtok); 183 | cont_len = strtoul(token, NULL, 0); 184 | } 185 | 186 | }while(strlen(line) != 1); 187 | 188 | if(chunked){ 189 | return content_from_chunked(line + strlen(line) + 1); 190 | }else{ 191 | return byte_str_new(cont_len, line + strlen(line) + 1); 192 | } 193 | 194 | fail_parse: 195 | log_printf(LOG_LEVEL_ERROR, "Tracker returned non-OK HTTP response\n"); 196 | return NULL; 197 | } 198 | 199 | static int tracker_sendall(int sockfd, const char *buff, size_t len) 200 | { 201 | ssize_t tot_sent = 0; 202 | while(tot_sent < len) { 203 | ssize_t sent = send(sockfd, buff, len - tot_sent, 0); 204 | if(sent < 0) 205 | return -1; 206 | 207 | tot_sent += sent; 208 | buff += sent; 209 | } 210 | return 0; 211 | } 212 | 213 | static int tracker_recv_resp(int sockfd, byte_str_t **outcont) 214 | { 215 | char buff[2048]; 216 | size_t tot_recv = 0; 217 | ssize_t nb; 218 | 219 | do { 220 | nb = recv(sockfd, buff + tot_recv, sizeof(buff) - tot_recv, 0); 221 | if(nb < 0) 222 | return -1; 223 | 224 | tot_recv += nb; 225 | }while(nb > 0); 226 | 227 | log_printf(LOG_LEVEL_INFO, "Tracker HTTP response received\n"); 228 | 229 | *outcont = content_from_tracker_resp(buff, tot_recv); 230 | if(!*outcont) 231 | return -1; 232 | 233 | return 0; 234 | } 235 | 236 | tracker_announce_resp_t *tracker_http_announce(int sockfd, url_t *url, tracker_announce_request_t *req) 237 | { 238 | tracker_announce_resp_t *ret; 239 | byte_str_t *raw; 240 | char *request_str = build_http_request(url, req); 241 | log_printf(LOG_LEVEL_DEBUG, "%s", request_str); 242 | 243 | if(tracker_sendall(sockfd, request_str, strlen(request_str))) 244 | goto fail_comm; 245 | 246 | if(tracker_recv_resp(sockfd, &raw)) 247 | goto fail_comm; 248 | 249 | ret = tracker_resp_parse_bencode(raw); 250 | if(!ret) 251 | goto fail_parse; 252 | 253 | free(request_str); 254 | byte_str_free(raw); 255 | 256 | return ret; 257 | 258 | fail_parse: 259 | byte_str_free(raw); 260 | fail_comm: 261 | free(request_str); 262 | return NULL; 263 | } 264 | 265 | -------------------------------------------------------------------------------- /src/libbf/tracker_http.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef TRACKER_HTTP_H 21 | #define TRAKCER_HTTP_H 22 | 23 | #include "tracker_announce.h" 24 | #include "url.h" 25 | 26 | tracker_announce_resp_t *tracker_http_announce(int sockfd, url_t *url, tracker_announce_request_t *req); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/libbf/tracker_resp_parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "tracker_resp_parser.h" 21 | #include "bencode.h" 22 | #include "log.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include //temp 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | list_t *tracker_resp_parse_peerlist(const char *buff, size_t len) 34 | { 35 | list_t *peers = list_init(); 36 | if(!peers) 37 | goto fail_alloc; 38 | 39 | assert(len % 6 == 0); 40 | for(int i = 0; i < len; i+= 6) { 41 | 42 | uint32_t ip; 43 | memcpy(&ip, buff + i, sizeof(uint32_t)); 44 | 45 | uint16_t port; 46 | memcpy(&port, buff + i + sizeof(uint32_t), sizeof(uint16_t)); 47 | 48 | peer_t *peer = malloc(sizeof(peer_t)); 49 | struct sockaddr_in ipv4; 50 | peer->addr.sas.ss_family = AF_INET; 51 | peer->addr.sa_in.sin_addr.s_addr = ip; 52 | peer->addr.sa_in.sin_port = port; 53 | memset(peer->addr.sa_in.sin_zero, 0, sizeof(peer->addr.sa_in.sin_zero)); 54 | 55 | memset(peer->peer_id, 0, sizeof(peer->peer_id)); 56 | 57 | list_add(peers, (unsigned char*)&peer, sizeof(peer)); 58 | } 59 | 60 | return peers; 61 | 62 | fail_alloc: 63 | return NULL; 64 | } 65 | 66 | static list_t *parse_peerlist_list(list_t *list) 67 | { 68 | list_t *peers = list_init(); 69 | if(!peers) 70 | goto fail_alloc; 71 | 72 | const unsigned char *entry; 73 | FOREACH_ENTRY(entry, list) { 74 | 75 | bencode_obj_t *peer_dict = *((bencode_obj_t**)entry); 76 | peer_t *peer = malloc(sizeof(peer_t)); 77 | bool valid = true; 78 | 79 | const char *key; 80 | const unsigned char *val; 81 | FOREACH_KEY_AND_VAL(key, val, peer_dict->data.dictionary) { 82 | 83 | if(!strcmp(key, "peer id") || !strcmp(key, "id")) { 84 | memcpy(peer->peer_id, (*(bencode_obj_t**)val)->data.string->str, sizeof(peer->peer_id)); 85 | } 86 | 87 | if(!strcmp(key, "ip")) { 88 | char *ipstr = (char*)(*(bencode_obj_t**)val)->data.string->str; 89 | struct addrinfo hint, *res = NULL; 90 | 91 | memset(&hint, 0, sizeof(hint)); 92 | hint.ai_family = PF_UNSPEC; 93 | hint.ai_flags = AI_NUMERICHOST; 94 | 95 | int ret = getaddrinfo(ipstr, NULL, &hint, &res); 96 | if(!ret) { 97 | peer->addr.sas.ss_family = res->ai_family; 98 | memcpy(&peer->addr.sas, res->ai_addr, sizeof(struct sockaddr)); 99 | freeaddrinfo(res); 100 | }else{ 101 | valid = false; 102 | break; 103 | } 104 | } 105 | 106 | if(!strcmp(key, "port")) { 107 | uint16_t port = (uint16_t)(*(bencode_obj_t**)val)->data.integer; 108 | 109 | if(peer->addr.sas.ss_family = AF_INET) { 110 | peer->addr.sa_in.sin_port = htons(port); 111 | }else{ 112 | peer->addr.sa_in6.sin6_port = htons(port); 113 | } 114 | } 115 | } 116 | 117 | if(valid) 118 | list_add(peers, (unsigned char*)&peer, sizeof(peer_t*)); 119 | else 120 | free(peer); 121 | 122 | } 123 | return peers; 124 | 125 | fail_alloc: 126 | return NULL; 127 | } 128 | 129 | tracker_announce_resp_t *tracker_resp_parse_bencode(const byte_str_t *raw) 130 | { 131 | const char *endptr; 132 | bencode_obj_t *obj = bencode_parse_object(raw->str, &endptr); 133 | if(!obj) 134 | goto fail_parse; 135 | 136 | tracker_announce_resp_t *ret = malloc(sizeof(tracker_announce_resp_t)); 137 | if(!ret) 138 | goto fail_alloc; 139 | memset(ret, 0, sizeof(*ret)); 140 | 141 | assert(obj->type == BENCODE_TYPE_DICT); 142 | const char *key; 143 | const unsigned char *val; 144 | 145 | FOREACH_KEY_AND_VAL(key, val, obj->data.dictionary) { 146 | if(!strcmp(key, "failure reason")) { 147 | char *str = (char*)(*(bencode_obj_t**)val)->data.string->str; 148 | ret->failure_reason = malloc(strlen(str) + 1); 149 | memcpy(ret->failure_reason, str, strlen(str) + 1); 150 | SET_HAS(ret, RESPONSE_HAS_FAILURE_REASON); 151 | } 152 | 153 | if(!strcmp(key, "warning message")) { 154 | char *str = (char*)(*(bencode_obj_t**)val)->data.string->str; 155 | ret->warning_message = malloc(strlen(str) + 1); 156 | memcpy(ret->warning_message, str, strlen(str) + 1); 157 | SET_HAS(ret, RESPONSE_HAS_WARNING_MESSAGE); 158 | } 159 | 160 | if(!strcmp(key, "interval")) { 161 | ret->interval = (*(bencode_obj_t**)val)->data.integer; 162 | } 163 | 164 | if(!strcmp(key, "min interval")) { 165 | ret->min_interval = ((bencode_obj_t*)val)->data.integer; 166 | SET_HAS(ret, RESPONSE_HAS_MIN_INTERVAL); 167 | } 168 | 169 | if(!strcmp(key, "tracker id")) { 170 | char *str = (char*)(*(bencode_obj_t**)val)->data.string->str; 171 | ret->tracker_id = malloc(strlen(str) + 1); 172 | memcpy(ret->tracker_id, str, strlen(str) + 1); 173 | SET_HAS(ret, RESPONSE_HAS_TRACKER_ID); 174 | } 175 | 176 | if(!strcmp(key, "complete")) { 177 | ret->complete = (*(bencode_obj_t**)val)->data.integer; 178 | } 179 | 180 | if(!strcmp(key, "incomplete")) { 181 | ret->incomplete = (*(bencode_obj_t**)val)->data.integer; 182 | } 183 | 184 | if(!strcmp(key, "peers")) { 185 | if((*(bencode_obj_t**)val)->type == BENCODE_TYPE_STRING) { 186 | byte_str_t *bstr = (*(bencode_obj_t**)val)->data.string; 187 | ret->peers = tracker_resp_parse_peerlist(bstr->str, bstr->size); 188 | }else { 189 | ret->peers = parse_peerlist_list((*(bencode_obj_t**)val)->data.list); 190 | } 191 | } 192 | 193 | if(!strcmp(key, "peers_ipv6")) { 194 | //TODO 195 | assert(0); 196 | } 197 | } 198 | 199 | /* In case there was a failure and there was no "peers" key */ 200 | if(!ret->peers) 201 | ret->peers = list_init(); 202 | if(!ret->peers) 203 | goto fail_alloc; 204 | 205 | bencode_free_obj_and_data_recursive(obj); 206 | return ret; 207 | 208 | fail_alloc: 209 | bencode_free_obj_and_data_recursive(obj); 210 | fail_parse: 211 | return NULL; 212 | } 213 | 214 | -------------------------------------------------------------------------------- /src/libbf/tracker_resp_parser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef TRACKER_RESP_PARSER_H 21 | #define TRACKER_RESP_PARSER_H 22 | 23 | #include "tracker_announce.h" 24 | #include "byte_str.h" 25 | #include "list.h" 26 | 27 | tracker_announce_resp_t *tracker_resp_parse_bencode(const byte_str_t *raw); 28 | list_t *tracker_resp_parse_peerlist(const char *buff, size_t len); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/libbf/tracker_udp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "tracker_udp.h" 21 | #include "log.h" 22 | #include "tracker_resp_parser.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #define PROT_ID_MAGIC 0x41727101980 35 | #define CONN_EXPIRE_TIME_SEC 60 36 | #define CONN_EXPIRED(_time) (time(NULL) > (_time) + CONN_EXPIRE_TIME_SEC) 37 | #define MAX_RECV_BUFF_SZ 2048 38 | 39 | typedef struct __attribute__ ((packed)) conn_req { 40 | uint64_t protocol_id; 41 | uint32_t action; 42 | uint32_t transaction_id; 43 | }conn_req_t; 44 | 45 | typedef struct __attribute__ ((packed)) conn_resp { 46 | uint32_t action; 47 | uint32_t transaction_id; 48 | uint64_t connection_id; 49 | }conn_resp_t; 50 | 51 | typedef struct __attribute__ ((packed)) ipv4_req { 52 | uint64_t connection_id; 53 | uint32_t action; 54 | uint32_t transaction_id; 55 | char info_hash[20]; 56 | char peer_id[20]; 57 | uint64_t downloaded; 58 | uint64_t left; 59 | uint64_t uploaded; 60 | uint32_t event; 61 | uint32_t ip; 62 | uint32_t key; 63 | uint32_t num_want; 64 | uint16_t port; 65 | }ipv4_req_t; 66 | 67 | typedef struct __attribute__ ((packed)) ipv4_resp_hdr { 68 | uint32_t action; 69 | uint32_t transaction_id; 70 | uint32_t interval; 71 | uint32_t leechers; 72 | uint32_t seeders; 73 | }ipv4_resp_hdr_t; 74 | 75 | typedef struct __attribute__ ((packed)) ipv4_err_resp_hdr { 76 | uint32_t action; 77 | uint32_t transaction_id; 78 | char message[]; 79 | }ipv4_err_resp_hdr_t; 80 | 81 | typedef enum tracker_action { 82 | TRACKER_ACTION_CONNECT = 0, 83 | TRACKER_ACTION_ANNOUNCE = 1, 84 | TRACKER_ACTION_SCRAPE = 2, 85 | TRACKER_ACTION_ERROR = 3 86 | }tracker_action_t; 87 | 88 | static int tracker_send_dgram(int sockfd, const char *buff, size_t len); 89 | static int tracker_recv_dgram(int sockfd, char *buff, size_t max, size_t *dgram_size, time_t timeout); 90 | static int tracker_udp_tryconnect(int sockfd, uint32_t trans_id, 91 | conn_resp_t *out, size_t *outlen, time_t timeout); 92 | static int tracker_udp_tryannounce(int sockfd, ipv4_req_t *req, char *out, 93 | size_t *outlen, time_t timeout); 94 | static void fill_announce_dgram(tracker_announce_request_t *req, ipv4_req_t *out, 95 | uint64_t conn_id, uint32_t trans_id); 96 | 97 | 98 | static inline time_t timeout(int n) 99 | { 100 | return 15 * pow(2, n); 101 | } 102 | 103 | static inline uint32_t new_transaction_id(void) 104 | { 105 | unsigned int seed = time(NULL); 106 | return rand_r(&seed); 107 | } 108 | 109 | static int tracker_send_dgram(int sockfd, const char *buff, size_t len) 110 | { 111 | ssize_t sent = send(sockfd, buff, len, 0); 112 | if(sent < 0) 113 | return -1; 114 | 115 | assert(sent == len); 116 | return 0; 117 | } 118 | 119 | static int tracker_recv_dgram(int sockfd, char *buff, size_t max, size_t *dgram_size, time_t timeout) 120 | { 121 | struct timeval tv; 122 | tv.tv_sec = timeout; 123 | tv.tv_usec = 0; 124 | setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); 125 | 126 | ssize_t nb = recv(sockfd, buff, max, 0); 127 | if(nb < 0) 128 | return -1; 129 | 130 | *dgram_size = nb; 131 | return 0; 132 | } 133 | 134 | static int tracker_udp_tryconnect(int sockfd, uint32_t trans_id, 135 | conn_resp_t *out, size_t *outlen, time_t timeout) 136 | { 137 | conn_req_t req; 138 | req.protocol_id = htobe64(PROT_ID_MAGIC); 139 | req.action = htonl(TRACKER_ACTION_CONNECT); 140 | req.transaction_id = trans_id; 141 | assert(sizeof(req) == sizeof(uint64_t) + sizeof(uint32_t)*2); 142 | 143 | if(tracker_send_dgram(sockfd, (char*)&req, sizeof(req))) 144 | return -1; 145 | 146 | if(tracker_recv_dgram(sockfd, (char*)out, sizeof(conn_resp_t), outlen, timeout)) 147 | return -1; 148 | 149 | return 0; 150 | } 151 | 152 | static void fill_announce_dgram(tracker_announce_request_t *req, ipv4_req_t *out, 153 | uint64_t conn_id, uint32_t trans_id) 154 | { 155 | out->connection_id = conn_id; 156 | out->action = htonl(TRACKER_ACTION_ANNOUNCE); 157 | out->transaction_id = trans_id; 158 | memcpy(out->info_hash, req->info_hash, sizeof(out->info_hash)); 159 | memcpy(out->peer_id, req->peer_id, sizeof(out->peer_id)); 160 | out->downloaded = htonl(req->downloaded); 161 | out->left = htonl(req->left); 162 | out->uploaded = htonl(out->uploaded); 163 | out->event = htonl(req->event); 164 | out->ip = 0; //TODO 165 | out->key = 0; 166 | out->num_want = htonl(req->numwant); 167 | out->port = htons(req->port); 168 | } 169 | 170 | static int tracker_udp_tryannounce(int sockfd, ipv4_req_t *req, 171 | char *out, size_t *outlen, time_t timeout) 172 | { 173 | if(tracker_send_dgram(sockfd, (char*)req, sizeof(*req))) 174 | return -1; 175 | 176 | if(tracker_recv_dgram(sockfd, out, MAX_RECV_BUFF_SZ, outlen, timeout)) 177 | return -1; 178 | 179 | return 0; 180 | } 181 | 182 | tracker_announce_resp_t *tracker_udp_announce(int sockfd, tracker_announce_request_t *req) 183 | { 184 | tracker_announce_resp_t *ret = NULL; 185 | int n = 0; 186 | 187 | uint32_t trans_id = new_transaction_id(); 188 | 189 | time_t conn_time; 190 | conn_resp_t conn_resp; 191 | size_t dgram_len; 192 | 193 | reconnect: 194 | while(tracker_udp_tryconnect(sockfd, trans_id, &conn_resp, &dgram_len, timeout(n++))) { 195 | if(n == 8) 196 | goto fail; 197 | if(errno != EAGAIN && errno != EWOULDBLOCK) 198 | goto fail; 199 | log_printf(LOG_LEVEL_WARNING, "Didn't get a connect response from the UDP tracker. Retrying...\n"); 200 | } 201 | conn_time = time(NULL); 202 | 203 | assert(dgram_len == sizeof(conn_resp)); 204 | log_printf(LOG_LEVEL_DEBUG, "UDP Tracker: Connection successful [Connection Id:0x%lx]\n", 205 | conn_resp.connection_id); 206 | 207 | ipv4_req_t ann_req; 208 | assert(sizeof(ipv4_req_t) == 98); 209 | fill_announce_dgram(req, &ann_req, conn_resp.connection_id, trans_id); 210 | 211 | union{ 212 | ipv4_resp_hdr_t header; 213 | ipv4_err_resp_hdr_t err_header; 214 | char all[MAX_RECV_BUFF_SZ]; 215 | }ann_resp; 216 | 217 | while(tracker_udp_tryannounce(sockfd, &ann_req, ann_resp.all, &dgram_len, timeout(n++))) { 218 | if(n == 8) 219 | goto fail; 220 | if(errno != EAGAIN && errno != EWOULDBLOCK) 221 | goto fail; 222 | if(CONN_EXPIRED(conn_time)) 223 | goto reconnect; 224 | log_printf(LOG_LEVEL_WARNING, "Didn't get an announce response from the UDP tracker. Retrying...\n"); 225 | } 226 | 227 | if(dgram_len < 20){ 228 | log_printf(LOG_LEVEL_ERROR, "Invalid datagram size from tracker\n"); 229 | goto fail; 230 | } 231 | 232 | if(ntohl(ann_resp.header.action) == TRACKER_ACTION_ERROR){ 233 | log_printf(LOG_LEVEL_ERROR, "Error response returned: %.*s\n", dgram_len - sizeof(ipv4_err_resp_hdr_t), 234 | ann_resp.err_header.message); 235 | goto fail; 236 | } 237 | 238 | if(ann_resp.header.transaction_id != trans_id){ 239 | log_printf(LOG_LEVEL_ERROR, "Invalid transaction id from tracker\n"); 240 | goto fail; 241 | } 242 | 243 | log_printf(LOG_LEVEL_DEBUG, "UDP Tracker: Announce successful, dgram size: %zu\n", dgram_len); 244 | 245 | ret = malloc(sizeof(tracker_announce_resp_t)); 246 | if(!ret) 247 | goto fail; 248 | 249 | ret->has = 0; 250 | ret->interval = ntohl(ann_resp.header.interval); 251 | ret->complete = ntohl(ann_resp.header.seeders); 252 | ret->incomplete = ntohl(ann_resp.header.leechers); 253 | ret->peers = tracker_resp_parse_peerlist(ann_resp.all + sizeof(ann_resp.header), 254 | dgram_len - sizeof(ann_resp.header)); 255 | 256 | return ret; 257 | 258 | fail: 259 | log_printf(LOG_LEVEL_ERROR, "UDP Tracker: Announcement failed\n"); 260 | return NULL; 261 | } 262 | 263 | -------------------------------------------------------------------------------- /src/libbf/tracker_udp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef TRACKER_UDP_H 21 | #define TRACKER_UDP_H 22 | 23 | #include "tracker_announce.h" 24 | #include 25 | 26 | tracker_announce_resp_t *tracker_udp_announce(int sockfd, tracker_announce_request_t *req); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/libbf/url.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "url.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | url_t *url_from_str(const char *str) 27 | { 28 | char buff[strlen(str) + 1]; 29 | char *saveptr; 30 | 31 | strcpy(buff, str); 32 | 33 | url_t *ret = malloc(sizeof(url_t)); 34 | if(!ret) 35 | return NULL; 36 | 37 | if(!strncmp(buff, "http:", 5)) 38 | ret->protocol = PROTOCOL_HTTP; 39 | else if(!strncmp(buff, "https:", 6)) 40 | ret->protocol = PROTOCOL_HTTPS; 41 | else if(!strncmp(buff, "udp://", 6)) 42 | ret->protocol = PROTOCOL_UDP; 43 | else 44 | ret->protocol = PROTOCOL_UNKNOWN; 45 | 46 | const char *hostname = strtok_r(buff, ":/", &saveptr); 47 | hostname = strtok_r(NULL, ":/", &saveptr); 48 | ret->hostname = malloc(strlen(hostname) + 1); 49 | if(!ret->hostname) 50 | goto fail_alloc_hostname; 51 | strcpy(ret->hostname, hostname); 52 | str += strlen(hostname) + (hostname - buff); 53 | 54 | if(strstr(str, ":")){ 55 | const char *port = strtok_r(NULL, ":/", &saveptr); 56 | ret->port = (uint16_t)strtoul(port, NULL, 0); 57 | }else if(ret->protocol == PROTOCOL_HTTP){ 58 | ret->port = 80; 59 | }else if(ret->protocol == PROTOCOL_HTTPS){ 60 | ret->port = 443; 61 | } 62 | 63 | const char *p; 64 | const char *path = (p = strtok_r(NULL, ":/", &saveptr)) ? p : ""; 65 | ret->path = malloc(strlen(path) + 1); 66 | if(!ret->path) 67 | goto fail_alloc_path; 68 | strcpy(ret->path, path); 69 | 70 | return ret; 71 | 72 | fail_alloc_path: 73 | free(ret->hostname); 74 | fail_alloc_hostname: 75 | free(ret); 76 | return NULL; 77 | } 78 | 79 | void url_free(url_t *url) 80 | { 81 | free(url->hostname); 82 | free(url->path); 83 | free(url); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /src/libbf/url.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef URL_H 21 | #define URL_H 22 | 23 | #include 24 | 25 | typedef enum 26 | { 27 | PROTOCOL_UNKNOWN = -1, 28 | PROTOCOL_HTTP, 29 | PROTOCOL_HTTPS, 30 | PROTOCOL_UDP 31 | }protocol_t; 32 | 33 | typedef struct url{ 34 | protocol_t protocol; 35 | char *hostname; 36 | char *path; 37 | uint16_t port; 38 | }url_t; 39 | 40 | url_t *url_from_str(const char *str); 41 | void url_free(url_t *url); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/ui/cli/commands.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "commands.h" 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define ARR_SIZE(a) (sizeof(a)/sizeof((a)[0])) 31 | 32 | typedef struct cmd { 33 | const char *name; 34 | int (*func)(int argc, char **argv); 35 | char *usage; 36 | }cmd_t; 37 | 38 | static int exit_cmd(int argc, char **argv); 39 | static int help_cmd(int argc, char **argv); 40 | static int ls_cmd(int argc, char **argv); 41 | static int add_cmd(int argc, char **argv); 42 | static int rm_cmd(int argc, char **argv); 43 | static int stat_cmd(int argc, char **argv); 44 | 45 | static cmd_t s_cmd_table[] = { 46 | {"exit", exit_cmd, "exit"}, 47 | {"help", help_cmd, "help"}, 48 | {"ls", ls_cmd, "ls"}, 49 | {"add", add_cmd, "add "}, 50 | {"rm", rm_cmd, "rm "}, 51 | {"stat", stat_cmd, "stat "} 52 | }; 53 | 54 | static void search_handle_func(bf_htorrent_t *torrent, void *arg); 55 | static bf_htorrent_t *handle_at_index(int index); 56 | static int num_torrents(void); 57 | static void print_usage(const char *cmdname); 58 | static void ls_cmd_foreach_func(bf_htorrent_t *torrent, void *arg); 59 | 60 | struct search_arg { 61 | int target; 62 | int curr; 63 | bf_htorrent_t *out; 64 | }; 65 | 66 | static void search_handle_func(bf_htorrent_t *torrent, void *arg) 67 | { 68 | struct search_arg *sarg = (struct search_arg*)arg; 69 | 70 | if(sarg->target == sarg->curr){ 71 | sarg->out = torrent; 72 | } 73 | 74 | sarg->curr++; 75 | } 76 | 77 | static bf_htorrent_t *handle_at_index(int index) 78 | { 79 | struct search_arg arg; 80 | arg.target = index; 81 | arg.curr = 0; 82 | arg.out = NULL; 83 | 84 | bitfiend_foreach_torrent(search_handle_func, &arg); 85 | 86 | return arg.out; 87 | } 88 | 89 | static int num_torrents(void) 90 | { 91 | struct search_arg arg; 92 | arg.target = -1; 93 | arg.curr = 0; 94 | 95 | bitfiend_foreach_torrent(search_handle_func, &arg); 96 | 97 | return arg.curr; 98 | } 99 | 100 | static void print_usage(const char *cmdname) 101 | { 102 | for(int i = 0; i < ARR_SIZE(s_cmd_table); i++) { 103 | if(!strcmp(cmdname, s_cmd_table[i].name)){ 104 | printf("Command usage: "); 105 | printf("%s\n", s_cmd_table[i].usage); 106 | break; 107 | } 108 | } 109 | } 110 | 111 | static int exit_cmd(int argc, char **argv) 112 | { 113 | if(num_torrents() > 0) { 114 | printf("Announcing to torrent tracker and closing ongoing connections. " 115 | "This may take a few seconds...\n"); 116 | } 117 | bitfiend_shutdown(); 118 | exit(EXIT_SUCCESS); 119 | } 120 | 121 | static int help_cmd(int argc, char **argv) 122 | { 123 | if(argc != 1){ 124 | print_usage(argv[0]); 125 | return CMD_FAIL_CMD; 126 | } 127 | 128 | for(int i = 0; i < ARR_SIZE(s_cmd_table); i++) { 129 | printf(" %s\n", s_cmd_table[i].usage); 130 | } 131 | 132 | return CMD_SUCCESS; 133 | } 134 | 135 | static void ls_cmd_foreach_func(bf_htorrent_t *torrent, void *arg) 136 | { 137 | int *index = (int*)arg; 138 | 139 | bf_stat_t stat; 140 | bitfiend_stat_torrent(torrent, &stat); 141 | float percent = (1.0f - ((float)stat.pieces_left)/stat.tot_pieces) * 100; 142 | printf("%3u: %-60s %3.2f%%\n", *index, stat.name, percent); 143 | 144 | (*index)++; 145 | } 146 | 147 | static int ls_cmd(int argc, char **argv) 148 | { 149 | if(argc != 1){ 150 | print_usage(argv[0]); 151 | return CMD_FAIL_CMD; 152 | } 153 | 154 | int index = 0; 155 | bitfiend_foreach_torrent(ls_cmd_foreach_func, &index); 156 | 157 | return CMD_SUCCESS; 158 | } 159 | 160 | static int add_cmd(int argc, char **argv) 161 | { 162 | int ret; 163 | 164 | if(argc != 3){ 165 | print_usage(argv[0]); 166 | return CMD_FAIL_CMD; 167 | } 168 | 169 | ret = bitfiend_add_torrent(argv[1], argv[2]) ? CMD_SUCCESS : CMD_FAIL_CMD; 170 | if(ret == BITFIEND_SUCCESS) 171 | printf("Successfully added torrent.\n"); 172 | else 173 | printf("Could not add torrent.\n"); 174 | 175 | return ret; 176 | } 177 | 178 | static int rm_cmd(int argc, char **argv) 179 | { 180 | if(argc != 2){ 181 | print_usage(argv[0]); 182 | return CMD_FAIL_CMD; 183 | } 184 | 185 | char *end; 186 | int index = strtoul(argv[1], &end, 0); 187 | if(argv[1] == end){ 188 | printf("Could not parse %s as an index.\n", argv[1]); 189 | return CMD_FAIL_CMD; 190 | } 191 | 192 | bf_htorrent_t *handle = handle_at_index(index); 193 | 194 | if(handle){ 195 | printf("Announcing to torrent tracker and closing ongoing connections. " 196 | "This may take a few seconds...\n"); 197 | 198 | bitfiend_remove_torrent(handle); 199 | 200 | printf("Successfully removed torrent at index %d.\n", index); 201 | }else{ 202 | printf("Could not remove torrent at index: %d.\n", index); 203 | } 204 | 205 | return CMD_SUCCESS; 206 | } 207 | 208 | static int stat_cmd(int argc, char **argv) 209 | { 210 | if(argc != 2){ 211 | print_usage(argv[0]); 212 | return CMD_FAIL_CMD; 213 | } 214 | 215 | int index; 216 | char *end; 217 | index = strtoul(argv[1], &end, 0); 218 | if(end == argv[1]){ 219 | printf("Could not parse %s as an index.\n", argv[1]); 220 | return CMD_FAIL_CMD; 221 | } 222 | 223 | bf_htorrent_t *handle = handle_at_index(index); 224 | if(!handle){ 225 | printf("Could not find torrent at index %d.\n", index); 226 | return CMD_FAIL_CMD; 227 | } 228 | 229 | bf_stat_t stat; 230 | bitfiend_stat_torrent(handle, &stat); 231 | float percent = (1.0f - ((float)stat.pieces_left)/stat.tot_pieces) * 100; 232 | 233 | printf("%-20s: %s\n", "Torrent", stat.name); 234 | printf("%-20s: %2.2f%%\n", "Completion", percent); 235 | printf("%-20s: %lu bytes\n", "Total Downloaded", stat.tot_downloaded); 236 | printf("%-20s: %lu bytes\n", "Total Uploaded", stat.tot_uploaded); 237 | printf("%-20s: %.2f bits/sec\n", "Avg. upload rate", stat.avg_uprate); 238 | printf("%-20s: %.2f bits/sec\n", "Avg. download rate", stat.avg_downrate); 239 | printf("%-20s: %.2f bits/sec\n", "Inst. upload rate", stat.inst_uprate); 240 | printf("%-20s: %.2f bits/sec\n", "Inst. download rate", stat.inst_downrate); 241 | 242 | return CMD_SUCCESS; 243 | } 244 | 245 | int command_exec(int argc, char **argv) 246 | { 247 | for(int i = 0; i < ARR_SIZE(s_cmd_table); i++) { 248 | if(!strcmp(argv[0], s_cmd_table[i].name)){ 249 | return s_cmd_table[i].func(argc, argv); 250 | } 251 | } 252 | return CMD_FAIL_EXEC; 253 | } 254 | 255 | int command_parse_and_exec(char *str) 256 | { 257 | int (*func)(int argc, char **argv) = NULL; 258 | const char *delims = " \t\n"; 259 | char *token; 260 | int argc = 0; 261 | 262 | char copy[strlen(str) + 1]; 263 | strcpy(copy, str); 264 | token = strtok(copy, delims); 265 | while(token) { 266 | argc++; 267 | token = strtok(NULL, delims); 268 | } 269 | 270 | if(argc == 0) 271 | return CMD_FAIL_EXEC; 272 | 273 | token = strtok(str, delims); 274 | for(int i = 0; i < ARR_SIZE(s_cmd_table); i++){ 275 | if(!strcmp(token, s_cmd_table[i].name)) { 276 | func = s_cmd_table[i].func; 277 | break; 278 | } 279 | } 280 | 281 | if(!func) 282 | return CMD_FAIL_EXEC; 283 | 284 | char *argv[argc]; 285 | int i = 0; 286 | do { 287 | assert(i < argc); 288 | argv[i++] = token; 289 | token = strtok(NULL, delims); 290 | }while(token); 291 | assert(i == argc); 292 | 293 | return func(argc, argv); 294 | } 295 | 296 | -------------------------------------------------------------------------------- /src/ui/cli/commands.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef COMMANDS_H 21 | #define COMMANDS_H 22 | 23 | #include 24 | 25 | enum { 26 | CMD_SUCCESS = 0, 27 | CMD_FAIL_EXEC, 28 | CMD_FAIL_CMD 29 | }; 30 | 31 | int command_exec(int argc, char **argv); 32 | int command_parse_and_exec(char *str); 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/ui/cli/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include 21 | #include "printclr.h" 22 | #include "commands.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define CLI_VER_MAJOR 0 33 | #define CLI_VER_MINOR 1 34 | #define CLI_VER_PATCH 0 35 | 36 | static void print_welcome(void) 37 | { 38 | printf(" ____ _ __ _______ __\n"); 39 | printf(" / __ )(_) /_/ ____(_)__ ____ ____/ /\n"); 40 | printf(" / __ / / __/ /_ / / _ \\/ __ \\/ __ / \n"); 41 | printf(" / /_/ / / /_/ __/ / / __/ / / / /_/ / \n"); 42 | printf("/_____/_/\\__/_/ /_/\\___/_/ /_/\\__,_/ "); 43 | printf("libbf %d.%d.%d cli %d.%d.%d\n\n", LIBBF_VER_MAJOR, LIBBF_VER_MINOR, LIBBF_VER_PATCH, 44 | CLI_VER_MAJOR, CLI_VER_MINOR, CLI_VER_PATCH); 45 | printf("Copyright (c) 2017 Eduard Permyakov\n"); 46 | printf("This is free software: you are free to change and redistribute it.\n"); 47 | printf("Type \"help\" for a list of commands or \"exit\" to quit.\n"); 48 | } 49 | 50 | static void print_prompt(void) 51 | { 52 | printclr(FG_YELLOW, "BitFiend> "); 53 | } 54 | 55 | static void next_line(char *out, size_t n) 56 | { 57 | fgets(out, n, stdin); 58 | out[n-1] = '\0'; 59 | /* If input was too long, consume the rest of stdin buffer */ 60 | if(out[strlen(out)-1] != '\n'){ 61 | char c; 62 | while (((c = getchar()) != '\n') && (c != EOF)); 63 | } 64 | } 65 | 66 | static bool is_empty_line(const char *str) 67 | { 68 | while (*str) { 69 | if (!isspace(*str)) 70 | return false; 71 | str++; 72 | } 73 | return true; 74 | } 75 | 76 | int main(int argc, char **argv) 77 | { 78 | if(argc != 1){ 79 | printf("Usage: %s\n", argv[0]); 80 | exit(EXIT_FAILURE); 81 | } 82 | 83 | print_welcome(); 84 | 85 | signal(SIGINT, SIG_IGN); //nicer handling of SIGINT eventually 86 | signal(SIGPIPE, SIG_IGN); 87 | 88 | if(bitfiend_init("./bitfiend.log") != BITFIEND_SUCCESS){ 89 | fprintf(stderr, "Failed initializing libbf. Check the logs!\n"); 90 | exit(EXIT_FAILURE); 91 | } 92 | 93 | while(true) { 94 | print_prompt(); 95 | 96 | char line[256]; 97 | next_line(line, sizeof(line)); 98 | 99 | if(is_empty_line(line)) 100 | continue; 101 | 102 | if(command_parse_and_exec(line) == CMD_FAIL_EXEC) 103 | printf("%s is not a valid command. See \"help\".\n", line); 104 | } 105 | 106 | /* Client will be shutdown by exit command */ 107 | assert(0); 108 | } 109 | 110 | -------------------------------------------------------------------------------- /src/ui/cli/printclr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #include "printclr.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #define FG_RESET "\x1b[0m" 27 | 28 | static const char *s_ansi_fg_table[] = { 29 | [FG_BLACK] = "\x1b[30m", 30 | [FG_RED] = "\x1b[31m", 31 | [FG_GREEN] = "\x1b[32m", 32 | [FG_YELLOW] = "\x1b[33m", 33 | [FG_BLUE] = "\x1b[34m", 34 | [FG_MAGENTA] = "\x1b[35m", 35 | [FG_CYAN] = "\x1b[36m", 36 | [FG_WHITE] = "\x1b[37m" 37 | }; 38 | 39 | void printclr(fg_clr_t fg, const char *fmt, ...) 40 | { 41 | va_list args; 42 | 43 | assert(fg > 0 && fg <= sizeof(s_ansi_fg_table)/sizeof(char*)); 44 | printf("%s", s_ansi_fg_table[fg]); 45 | 46 | va_start(args, fmt); 47 | vprintf(fmt, args); 48 | va_end(args); 49 | 50 | printf(FG_RESET); 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/ui/cli/printclr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of BitFiend. 3 | * Copyright (C) 2017 Eduard Permyakov 4 | * 5 | * BitFiend is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * BitFiend is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | */ 19 | 20 | #ifndef PRINTCLR 21 | #define PRINTCLR 22 | 23 | typedef enum { 24 | FG_BLACK = 0, 25 | FG_RED, 26 | FG_GREEN, 27 | FG_YELLOW, 28 | FG_BLUE, 29 | FG_MAGENTA, 30 | FG_CYAN, 31 | FG_WHITE 32 | }fg_clr_t; 33 | 34 | void printclr(fg_clr_t fg, const char *fmt, ...); 35 | 36 | #endif 37 | --------------------------------------------------------------------------------