summaryrefslogtreecommitdiff
path: root/tests/sync_daemon.pl
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sync_daemon.pl')
-rw-r--r--tests/sync_daemon.pl294
1 files changed, 294 insertions, 0 deletions
diff --git a/tests/sync_daemon.pl b/tests/sync_daemon.pl
new file mode 100644
index 0000000..9e4bdb9
--- /dev/null
+++ b/tests/sync_daemon.pl
@@ -0,0 +1,294 @@
1#!/usr/bin/perl
2
3# This software was written by Philipp Wuensche <cryx-otsync@h3q.com>
4# It is considered beerware.
5
6use strict;
7
8#use Convert::Bencode_XS qw(:all);
9use Convert::Bencode qw(:all);
10use Data::Dumper;
11use LWP::UserAgent;
12use URI::Escape;
13
14# enable verbose output
15my $debug = 0;
16
17# tracker from where we get our sync data
18my @trackers = ('127.0.0.1:8989');
19# tracker to upload merged data
20my @client_tracker = ('127.0.0.1:8989');
21
22# time to wait between syncs
23my $sleeptime = '300';
24
25# SSL cert and key
26my $ssl_cert = 'cert.pem';
27my $ssl_key = 'key.pem';
28
29foreach(@trackers) {
30 print "Syncing from: $_\n";
31}
32foreach(@client_tracker) {
33 print "Syncing to: $_\n";
34}
35
36my $file = shift;
37
38
39# global hash for storing the merged syncs
40my %merged_syncs;
41
42while(1) {
43 %merged_syncs;
44 my @bencoded_sync_data;
45
46 # fetch the sync from every tracker and put it into an array in bencoded form
47 foreach my $tracker (@trackers) {
48 my $bencoded_sync = fetch_sync($tracker);
49# my $bencoded_sync = fetch_sync_from_file($file);
50 if($bencoded_sync ne 0 && $bencoded_sync =~ /^d4\:sync/) {
51 push(@bencoded_sync_data,$bencoded_sync);
52 }
53 }
54
55 # bdecode every sync and throw it into the merged-sync
56 foreach my $bencoded_sync (@bencoded_sync_data) {
57 print "Doing merge...\n";
58 merge_sync(bdecode($bencoded_sync));
59 my $num_torrents = keys(%merged_syncs);
60
61 print "number of torrents: $num_torrents\n";
62 }
63
64 # number of max. peers in one changeset
65 my $peer_limit = 500;
66 # max number of changesets per commit
67 my $max_changesets = 10;
68 my $hash_count = 0;
69 my $peer_count = 0;
70 my $changeset;
71 my @escaped_changesets;
72
73 # run until all hashes are put into changesets and commited to the trackers
74 while(keys(%merged_syncs) != 0) {
75
76 foreach my $hash (keys(%merged_syncs)) {
77 print "Starting new changeset\n" if($peer_count == 0 && $debug);
78 my $num_peers = keys(%{$merged_syncs{$hash}});
79
80 print "\t$peer_count peers for $hash_count hashes in changeset\n" if($debug);
81
82 my $pack_hash = pack('H*',$hash);
83
84 # as long as the peer_limit is not reached, add new hashes with peers to the changeset hash-table
85 if($peer_count < $peer_limit) {
86 print "\t\tAdd $num_peers peers for $hash changeset\n" if($debug);
87 $peer_count = $peer_count + $num_peers;
88 foreach my $peer_socket (keys(%{$merged_syncs{$hash}})) {
89
90 my $flags = $merged_syncs{$hash}{$peer_socket};
91
92 print "\t\t\tAdd $peer_socket $flags\n" if($debug);
93
94 my $pack_peer = packme($peer_socket,$flags);
95
96 $changeset->{'sync'}->{$pack_hash} = $changeset->{'sync'}->{$pack_hash}.$pack_peer;
97 }
98 $hash_count++;
99 # hash is stored in the changeset, delete it from the hash-table
100 delete $merged_syncs{$hash};
101 }
102
103 # the peer_limit is reached or we are out of torrents, so start preparing a changeset
104 if($peer_count >= $peer_limit || keys(%merged_syncs) == 0) {
105
106 print "Commit changeset for $hash_count hashes with $peer_count peers total\n" if($debug);
107
108 # bencode the changeset
109 my $enc_changeset = bencode($changeset);
110
111 # URL-escape the changeset and put into an array of changesets
112 my $foobar = uri_escape($enc_changeset);
113 push(@escaped_changesets,$foobar);
114
115 # the changeset is ready and stored, so delete it from the changeset hash-table
116 delete $changeset->{'sync'};
117
118 $hash_count = 0;
119 $peer_count = 0;
120 print "\n\n\n" if($debug);
121 }
122
123 # if enought changesets are prepared or we are out of torrents for more changesets,
124 # sync the changesets to the trackers
125 if($#escaped_changesets == $max_changesets || keys(%merged_syncs) == 0) {
126 print "\tSync...\n";
127 sync_to_tracker(\@escaped_changesets);
128 undef @escaped_changesets;
129 }
130
131 }
132 }
133
134 print "Sleeping for $sleeptime seconds\n";
135 sleep $sleeptime;
136}
137
138sub connect_tracker {
139 # connect a tracker via HTTPS, returns the body of the response
140 my $url = shift;
141
142 $ENV{HTTPS_DEBUG} = 0;
143 $ENV{HTTPS_CERT_FILE} = $ssl_cert;
144 $ENV{HTTPS_KEY_FILE} = $ssl_key;
145
146 my $ua = new LWP::UserAgent;
147 my $req = new HTTP::Request('GET', $url);
148 my $res = $ua->request($req);
149
150 my $content = $res->content;
151
152 if($res->is_success()) {
153 return $content;
154 } else {
155 print $res->code."|".$res->status_line."\n";
156 return 0;
157 }
158}
159
160sub sync_to_tracker {
161 # commit changesets to a tracker
162 my @changesets = @{(shift)};
163
164 # prepare the URI with URL-encoded changesets concatenated by a &
165 my $uri = 'sync?';
166 foreach my $set (@changesets) {
167 $uri .= 'changeset='.$set.'&';
168 }
169 my $uri_length = length($uri);
170
171 # commit the collection of changesets to the tracker via HTTPS
172 foreach my $tracker (@client_tracker) {
173 print "\t\tTracker: $tracker (URI: $uri_length)\n";
174 my $url = "https://$tracker/".$uri;
175 connect_tracker($url);
176 }
177}
178
179sub packme {
180 # pack data
181 # returns ipaddr, port and flags in packed format
182 my $peer_socket = shift;
183 my $flags = shift;
184
185 my($a,$b,$c,$d,$port) = split(/[\.,\:]/,$peer_socket);
186 my $pack_peer = pack('C4 n1 b16',$a,$b,$c,$d,$port,$flags);
187
188 return $pack_peer;
189}
190
191sub unpackme {
192 # unpack packed data
193 # returns ipaddr. in quad-form with port (a.b.c.d:port) and flags as bitfield
194 # data is packed as:
195 # - 4 byte ipaddr. (unsigned char value)
196 # - 2 byte port (unsigned short in "network" (big-endian) order)
197 # - 2 byte flags (bit string (ascending bit order inside each byte))
198 my $data = shift;
199
200 my($a, $b, $c, $d, $port, $flags) = unpack('C4 n1 b16',$data);
201 my $peer_socket = "$a\.$b\.$c\.$d\:$port";
202
203 return($peer_socket,$flags);
204}
205
206sub fetch_sync {
207 # fetch sync from a tracker
208 my $tracker = shift;
209 my $url = "https://$tracker/sync";
210
211 print "Fetching from $url\n";
212
213 my $body = connect_tracker($url);
214
215 if($body && $body =~ /^d4\:sync/) {
216 return $body;
217 } else {
218 return 0;
219 }
220}
221
222sub fetch_sync_from_file {
223 # fetch sync from a file
224 my $file = shift;
225 my $body;
226 print "Fetching from file $file\n";
227 open(FILE,"<$file");
228 while(<FILE>) {
229 $body .= $_;
230 }
231 close(FILE);
232 return $body;
233}
234
235sub merge_sync {
236 # This builds a hash table with the torrenthash as keys. The value is a hash table again with the peer-socket as keys
237 # and flags in the value
238 # Example:
239 # 60dd2beb4197f71677c0f5ba92b956f7d04651e5 =>
240 # 192.168.23.23:2323 => 0000000000000000
241 # 23.23.23.23:2342 => 0000000100000000
242 # b220b4d7136e84a88abc090db88bec8604a808f3 =>
243 # 42.23.42.23:55555 => 0000000000000000
244
245 my $hashref = shift;
246
247 my $nonuniq_hash_counter = 0;
248 my $nonuniq_peer_counter = 0;
249 my $hash_counter = 0;
250 my $peer_counter = 0;
251
252 foreach my $key (keys(%{$hashref->{'sync'}})) {
253 # start merge for every sha1-hash in the sync
254 my $hash = unpack('H*',$key);
255
256 $hash_counter++;
257 $nonuniq_hash_counter++ if exists $merged_syncs{$hash};
258
259 while(${$hashref->{'sync'}}{$key} ne "")
260 {
261 # split the value into 8-byte and unpack it for getting peer-socket and flags
262 my($peer_socket,$flags) = unpackme(substr(${$hashref->{'sync'}}{$key},0,8,''));
263
264 $peer_counter++;
265 $nonuniq_peer_counter++ if exists $merged_syncs{$hash}{$peer_socket};
266
267 # Create a hash table with sha1-hash as key and a hash table as value.
268 # The hash table in the value has the peer-socket as key and flags as value
269 # If the entry already exists, the flags are ORed together, if not it is ORed with 0
270 $merged_syncs{$hash}{$peer_socket} = $flags | $merged_syncs{$hash}{$peer_socket};
271 }
272 }
273 print "$hash_counter hashes $nonuniq_hash_counter non-uniq, $peer_counter peers $nonuniq_peer_counter non-uniq.\n";
274
275}
276
277sub test_decode {
278 my $hashref = shift;
279
280 print "CHANGESET DEBUG OUTPUT\n";
281
282 print Dumper $hashref;
283 foreach my $key (keys(%{$hashref->{'sync'}})) {
284 my $hash = unpack('H*',$key);
285
286 print "Changeset for $hash\n";
287 while(${$hashref->{'sync'}}{$key} ne "")
288 {
289
290 my($peer_socket,$flags) = unpackme(substr(${$hashref->{'sync'}}{$key},0,8,''));
291 print "\tSocket: $peer_socket Flags: $flags\n";
292 }
293 }
294}