torrenter 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +0,0 @@
1
- require 'bencode'
2
- require 'pry'
3
- require 'digest/sha1'
4
- require './lib/torrenter/torrent_reader.rb'
5
- require './lib/torrenter/udp_tracker.rb'
6
- require './lib/torrenter/http_tracker.rb'
7
-
8
- stream = BEncode.load_file('thrones.torrent')
9
- reader = TorrentReader.new(stream)
10
- binding.pry
@@ -1,166 +0,0 @@
1
- module Torrenter
2
- # KEEP_ALIVE = "\x00\x00\x00\x00"
3
- # BLOCK = 2**14
4
- # these methods get mixed in with the Peer class as a way to help
5
- # organize and parse the byte-encoded data. The intention is to shorten
6
- # and shrink the complexity of the Peer class.
7
- # the following methods are responsible solely for data retrieval and data transmission
8
-
9
- def state(master_index)
10
- recv_data if @buffer.size <= 3
11
- if @buffer[4].nil?
12
- @buffer.slice!(0..3) if @buffer[0..3] == KEEP_ALIVE
13
- else
14
- @length = @buffer[0..3].unpack("N*").last
15
- @buffer_id = @buffer[4]
16
- case @buffer_id
17
- when BITFIELD
18
- process_bitfield(master_index.size, clear_msg)
19
- when HAVE
20
- process_have(clear_msg)
21
- when INTERESTED
22
- @buffer = ''
23
-
24
-
25
- return :interested
26
- when PIECE
27
- process_piece
28
- when CHOKE
29
- choke_message
30
- when HANDSHAKE
31
- process_handshake
32
- end
33
- end
34
- end
35
-
36
- def choke_message
37
- @peer_state = false
38
- end
39
-
40
- def clear_msg
41
- @buffer.slice!(0..@length + 3)
42
- end
43
-
44
- def request_message(bytes=BLOCK)
45
- @msg = "index: #{@index} offset: #{@blocks.size * BLOCK} bytes: #{bytes}"
46
- send_data(pack(13) + "\x06" + pack(@index) + pack(@blocks.size * BLOCK) + pack(bytes))
47
- end
48
-
49
- def pack(msg)
50
- [msg].pack("I>")
51
- end
52
-
53
- def process_bitfield(size, msg)
54
- index = msg[5..-1].unpack("B#{size}").join.split('')
55
- @piece_index = index.map { |bit| bit == '1' ? :free : :unavailable }
56
- send_interested if @buffer.empty?
57
- end
58
-
59
- def process_have(msg)
60
- @piece_index[msg[5..-1].unpack("C*").last] = :free
61
- send_interested if @buffer.empty?
62
- end
63
-
64
- def request_piece(index)
65
- @index = index
66
- @piece_index[@index] = :downloading
67
-
68
- request_message
69
- end
70
-
71
- def send_interested
72
- send_data("\x00\x00\x00\x01\x02")
73
- end
74
-
75
- def buffer_length?
76
- @buffer.bytesize >= @length + 4
77
- end
78
-
79
- def process_piece
80
- if buffer_length?
81
- if buffer_complete?
82
- pack_buffer
83
- end
84
-
85
- diff = @remaining - @blocks.join('').bytesize
86
- if diff < BLOCK
87
- request_message(diff)
88
- else
89
- request_message
90
- end
91
- end
92
- recv_data
93
- end
94
-
95
- def mark_complete
96
- @blocks = []
97
- @piece_index[@index] = :downloaded
98
- end
99
-
100
- def last_piece?
101
- @remaining
102
- end
103
-
104
- def piece_complete?
105
- piece_size == @piece_length
106
- end
107
-
108
- def buffer_size
109
- @buffer[13..-1].bytesize
110
- end
111
-
112
- def pack_buffer
113
- @blocks << @buffer.slice!(13..-1)
114
- @buffer.clear
115
- end
116
-
117
- def buffer_complete?
118
- (@buffer.length - 13) == BLOCK || (@buffer.length - 13) == @remaining
119
- end
120
-
121
- # will need a last piece sort of thing inserted in here
122
-
123
- def piece_full?
124
- @blocks.join('').size == @piece_len
125
- end
126
-
127
- def piece_size
128
- @blocks.join('').bytesize + (@buffer.bytesize - 13)
129
- end
130
-
131
- def process_handshake
132
- @buffer.slice!(0..67) if hash_check?
133
- end
134
-
135
- def hash_check?
136
- @buffer[28..47] == @info_hash
137
- end
138
-
139
- def send_data(msg)
140
- begin
141
- Timeout::timeout(2) { @socket.sendmsg_nonblock(msg) }
142
- rescue Timeout::Error
143
- ''
144
- rescue IO::EAGAINWaitReadable
145
- ''
146
- rescue *EXCEPTIONS
147
- ''
148
- rescue Errno::ETIMEDOUT
149
- @peer_state = false
150
- end
151
- end
152
-
153
- def recv_data(bytes=BLOCK)
154
- begin
155
- Timeout::timeout(5) { @buffer << @socket.recv_nonblock(bytes) }
156
- rescue Timeout::Error
157
- ''
158
- rescue Errno::ETIMEDOUT
159
- @peer_state = false
160
- rescue *EXCEPTIONS
161
- ''
162
- rescue IO::EAGAINWaitReadable
163
- ''
164
- end
165
- end
166
- end
@@ -1,219 +0,0 @@
1
- module Torrenter
2
- class Reactor < Torrenter::TorrentReader
3
-
4
- attr_accessor :master_index
5
- def initialize(peers, stream)
6
- super(stream)
7
- @peers = peers
8
- @master_index = Array.new(sha_hashes.bytesize / 20) { :free }
9
- @time_counter = 0
10
- @time = Time.now.to_i
11
- @byte_counter = 0
12
- end
13
-
14
- def check_for_existing_data
15
- # if the torrent data file is not present, one will be created
16
- IO.write($data_dump, '', 0) unless File.file?($data_dump)
17
-
18
- 0.upto(@master_index.size - 1) do |n|
19
- # this needs to get changed in order to account for
20
- # the last piece not being equal to the piece_length
21
- data = IO.read($data_dump, piece_length, n * piece_length) || ''
22
- # binding.pry
23
- if verified?(n, data)
24
- @master_index[n] = :downloaded
25
- @byte_counter += data.bytesize
26
- end
27
- end
28
- puts "#{@master_index.count(:downloaded)} pieces downloaded"
29
- end
30
-
31
- def verified?(loc, piece)
32
- Digest::SHA1.digest(piece) == sha_hashes[loc * 20..(loc * 20) + 19]
33
- end
34
-
35
- def finished?
36
- @master_index.all? { |index| index == :downloaded }
37
- end
38
-
39
- def connect_peers
40
- @peers.each { |peer| peer.connect if active_peers.size < 8 }
41
- end
42
-
43
- # sends the handshake messages.
44
-
45
- def message_reactor
46
- @peers.each { |peer| peer.connect if active_peers.length < 8 }
47
- if !finished?
48
- peer.state(@master_index) do |loc, buf|
49
-
50
- end
51
- end
52
- end
53
-
54
- def piece_indices
55
- active_peers.select { |peer| peer.piece_index }
56
- .map { |peer| peer.piece_index }
57
- end
58
-
59
- def tally
60
- @master_index.map.with_index do |p,i|
61
- p == :free ? piece_indices.map { |x| x[i] } : []
62
- end
63
- end
64
-
65
- def piece_select(peer, limit=1)
66
- binding.pry if @master_index.count(:free) == 1
67
- puts "#{peer.ip} picking a piece"
68
- piece_index = peer.piece_index
69
- @tally = tally
70
- index = nil
71
-
72
- loop do
73
- if limit == active_peers.size + 1
74
- index = piece_index.index(:free)
75
- break
76
- elsif index.is_a?(Integer)
77
- break
78
- end
79
- i = 0
80
- loop do
81
- break if i > piece_index.size - 1|| !index.nil?
82
- if @tally[i].count(:free) == limit && piece_index[i] == :free
83
- index = i
84
- else
85
- i += 1
86
- end
87
- end
88
-
89
- limit += 1 if index.nil?
90
- end
91
-
92
- if index
93
- @master_index[index] = :downloading
94
- peer.request_piece(index)
95
- p index
96
- return index
97
- else
98
- binding.pry
99
- end
100
- end
101
-
102
- def show_status
103
- if (Time.now.to_i - @time) == 1
104
- system('clear')
105
- puts "#{download_bar} \n Downloading #{$data_dump[/(.+)(?=torrent-data)/]} at #{real} KB/sec with #{pieces_left} pieces left to download"
106
- puts "and #{data_remaining} MB remaining ------ #{active_peers.size} connected."
107
- end
108
- end
109
-
110
- def remaining
111
- total_file_size - @byte_counter
112
- end
113
-
114
- def select_index(index)
115
- poo = @master_index.map.with_index do |peer,index|
116
- active_peers.map { |x| x.piece_index[index] }
117
- end
118
- end
119
-
120
- def peer_indices
121
- active_peers.map { |peer| peer.piece_index }
122
- end
123
-
124
- def data_remaining
125
- (total_file_size - @byte_counter).fdiv(1024).fdiv(1024).round(2)
126
- end
127
-
128
- def pieces_left
129
- @master_index.count(:free)
130
- end
131
-
132
- def real
133
- @time_counter.fdiv(1024).round(1)
134
- end
135
-
136
- def reattempt_disconnected_peer
137
- disconnected_peers.sample.connect
138
- end
139
-
140
- def disconnected_peers
141
- @peers.select { |peer| !peer.connected? }
142
- end
143
-
144
- def pack_piece(peer)
145
- IO.write($data_dump, peer.piece_data, piece_length * peer.index)
146
- end
147
-
148
- def indices
149
- @master_index.map do |piece|
150
- if piece == :free
151
- 0
152
- elsif piece == :downloaded
153
- 1
154
- else
155
- get_status @master_index.index(piece)
156
- end
157
- end
158
- end
159
-
160
- def get_status(i)
161
- peer = @peers.find { |peer| peer.piece_index[i] == :downloading }
162
- (peer.buffer.bytesize + peer.piece_size).fdiv(@piece_length)
163
- end
164
-
165
- def active_peers
166
- @peers.select { |peer| peer.peer_state }
167
- end
168
-
169
- def index_percentages
170
- active_peers.map do |peer|
171
- size = peer.buffer.bytesize + peer.piece_size
172
- [peer.index, (size.fdiv @piece_length) * 100]
173
- end
174
- end
175
-
176
- def download_bar
177
- ("\u2588" * pieces(:downloaded)) + ("\u2593" * pieces(:downloading)) + (" " * pieces(:free)) + " %#{pieces(:downloaded)} downloaded "
178
- end
179
-
180
- def pieces(type)
181
- (@master_index.count(type).fdiv(@master_index.size) * 100).round
182
- end
183
-
184
- def seperate_data_dump_into_files
185
- folder = $data_dump[/.+(?=\.torrent-data)/]
186
-
187
- if multiple_files?
188
- offset = 0
189
-
190
- file_list.each do |file|
191
-
192
- length = file['length']
193
- filename = file['path'].pop
194
-
195
- if multiple_sub_folders?(file)
196
- subfolders = file['path'].join("/")
197
- folder = folder + "/" + subfolders
198
- FileUtils.mkdir_p("#{folder}", force: true)
199
- end
200
-
201
- File.open("#{folder}/#{filename}", 'a+') { |data| data << IO.read($data_dump, length, offset) }
202
- offset += length
203
- end
204
- else
205
- FileUtils.mkdir("#{folder}")
206
- File.open("#{folder}/#{file_list['name']}", 'w') { |data| data << File.read($data_dump) }
207
- end
208
- File.delete($data_dump)
209
- end
210
-
211
- def multiple_sub_folders?(file)
212
- file['path'].size > 1
213
- end
214
-
215
- def multiple_files?
216
- file_list.is_a?(Array)
217
- end
218
- end
219
- end
@@ -1,96 +0,0 @@
1
- module Torrenter
2
- class UDPTracker < TorrentReader
3
-
4
- attr_reader :socket, :response
5
-
6
- def initialize(tracker, stream)
7
- super(stream)
8
- @url = tracker[/(?<=udp\:\/\/).+(?=\:\d+)/]
9
- @port = tracker[/\d+$/].to_i
10
- @socket = UDPSocket.new
11
- @connection_id = [0x41727101980].pack("Q>")
12
- end
13
-
14
- def connect
15
- @transaction_id = [rand(1000)].pack("I>")
16
- @socket.connect(ip_addr, @port)
17
- begin
18
- send_msg(connect_msg)
19
- read_response
20
- rescue
21
- false
22
- end
23
- end
24
-
25
- def send_msg(msg)
26
- begin
27
- @socket.send(msg, 0)
28
- rescue
29
- false
30
- end
31
- end
32
-
33
- def connected?
34
- @response
35
- end
36
-
37
- def ip_addr
38
- Socket.getaddrinfo(@url, @port)[0][3]
39
- end
40
-
41
- def bound_peers
42
- @connection_id = @response[-8..-1]
43
- @transaction_id = [rand(10000)].pack("I>")
44
- send_msg(announce_msg)
45
-
46
- read_response
47
-
48
- parse_announce if @response[0..3] == action(1)
49
-
50
- peer_list(@response)
51
- end
52
-
53
- def parse_announce
54
- if @response[4..7] == @transaction_id
55
- res = @response.slice!(0..11)
56
- leechers = @response.slice!(0..3).unpack("I>").first
57
- seeders = @response.slice!(0..3).unpack("I>").first
58
- end
59
- end
60
-
61
- def send_message
62
- begin
63
- @socket.send(@msg, 0)
64
- rescue
65
- end
66
- end
67
-
68
- def read_response
69
- begin
70
- @response = @socket.recv(1028)
71
- rescue Exception => e
72
- e
73
- end
74
- end
75
-
76
- def connect_match?
77
- data[0] == (action(0) + @transaction_id + @connection_id)
78
- end
79
-
80
- def announce_input
81
- @connection_id + action(1) + @transaction_id + info_hash + PEER_ID
82
- end
83
-
84
- def connect_msg
85
- @connection_id + action(0) + @transaction_id
86
- end
87
-
88
- def action(n)
89
- [n].pack("I>")
90
- end
91
-
92
- def announce_msg
93
- @connection_id + action(1) + @transaction_id + info_hash + PEER_ID + [0].pack("Q>") + [0].pack("Q>") + [0].pack("Q>") + action(0) + action(0) + action(0) + action(-1) + [@socket.addr[1]].pack(">S")
94
- end
95
- end
96
- end