├── .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 |
--------------------------------------------------------------------------------