torrenter 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f2ef8e3760de9d724d24826b929e2d40bcbea4be
4
- data.tar.gz: 58aa8774b9ca139ec06d6c44751b48745f339147
3
+ metadata.gz: 992e0e356ed722f0cb7131a5029b454e6fdba9e2
4
+ data.tar.gz: 64ae0a3e57481d38ac75668773303d2ab601fa91
5
5
  SHA512:
6
- metadata.gz: ce174384bf065513a2b487098c7430d195280c87d1ed0c9a3b1205b768b0effe4339c419710a7c99477780b6a6be12e4bafac6d41c4950393124082ade4002e6
7
- data.tar.gz: 8985f1f8769bccb328fefbb122f7d024537c9958fdd6d07e89041935291c89f63f61fc487c8dc8237ee15751fb0789441b497ad435131b133fb2f64850bea53e
6
+ metadata.gz: bb19b53d0d740111e96941af9a02a07352979196ead79e1c34c796f42fdcec5d1aab7d6527b309f8cea84dec1513cc6b76b680f02c562f14c8993b7fa7a0353e
7
+ data.tar.gz: a2870db5b56bfc2a887a2530ea924671fa677e0fb26ce0b80311148bf83a29b9457a2b8282e0ec9de832bfa8b401cecd9479eacac3441dd36f7ae342072e9273
@@ -1,9 +1,16 @@
1
+ require 'socket'
2
+ require 'digest/sha1'
3
+ require 'bencode'
4
+ require 'fileutils'
5
+
1
6
  require 'torrenter/message/messager'
2
7
  require 'torrenter/message/message_types'
3
8
  require 'torrenter/peer'
4
9
  require 'torrenter/reactor'
5
- require 'torrenter/udp'
6
10
  require 'torrenter/torrent_reader'
11
+ require 'torrenter/http_tracker'
12
+ require 'torrenter/udp_tracker'
13
+
7
14
  module Torrenter
8
15
  class Torrent
9
16
  def start(file)
@@ -0,0 +1,40 @@
1
+ module Torrenter
2
+ class HTTPTracker < TorrentReader
3
+
4
+ def initialize(address, stream)
5
+ @address = URI(address)
6
+ super(stream)
7
+ end
8
+
9
+ def connect
10
+ @address.query = URI.encode_www_form(tracker_params)
11
+ @response =
12
+ begin
13
+ BEncode.load(Net::HTTP.get(@address))
14
+ rescue
15
+ false
16
+ end
17
+ end
18
+
19
+ def address_hashes
20
+ @response['peers']
21
+ end
22
+
23
+ def address_list
24
+ parse_addresses(address_hashes, address_hashes.bytesize / 6)
25
+ end
26
+
27
+ def tracker_params
28
+ {
29
+ :info_hash => @sha,
30
+ :peer_id => PEER_ID,
31
+ :left => @piece_length,
32
+ :pieces => @file_list
33
+ }
34
+ end
35
+
36
+ def connected?
37
+ @response
38
+ end
39
+ end
40
+ end
@@ -4,7 +4,6 @@ module Torrenter
4
4
  # these methods get mixed in with the Peer class as a way to help
5
5
  # organize and parse the byte-encoded data. The intention is to shorten
6
6
  # and shrink the complexity of the Peer class.
7
-
8
7
  # the following methods are responsible solely for data retrieval and data transmission
9
8
 
10
9
  def send_data(msg, opts={})
@@ -1,15 +1,15 @@
1
- require 'socket'
2
- require 'digest/sha1'
3
- require 'bencode'
4
- require 'pry'
5
- require 'fileutils'
1
+
6
2
 
7
3
 
8
4
  module Torrenter
9
5
  class TorrentReader
10
6
  attr_reader :stream
11
7
  def initialize(stream)
12
- @stream = stream
8
+ @stream = stream
9
+ @file_list = stream['info']['files'] || stream['info']
10
+ @sha = Digest::SHA1.digest(stream['info'].bencode)
11
+ @piece_length = stream['info']['piece length']
12
+ @sha_list = stream['info']['pieces']
13
13
  end
14
14
 
15
15
  def determine_protocol
@@ -18,115 +18,51 @@ module Torrenter
18
18
  else
19
19
  [@stream['announce']]
20
20
  end
21
-
22
- @http_trackers = trackers.select { |tracker| tracker =~ /http\:\/\// }
23
- @udp_trackers = trackers.select { |tracker| tracker =~ /udp\:\/\// }
24
- # first try the http trackers...
25
21
 
26
- connect_to_http_tracker if @http_trackers.size > 0
27
- connect_to_udp_tracker if @peers.nil?
28
- peer_list
29
- establish_reactor
30
-
31
- end
32
-
33
- def connect_to_http_tracker
34
- @uri = URI(@http_trackers.shift)
35
- @uri.query = URI.encode_www_form(peer_hash)
36
- @peers = raw_peers
37
- end
38
-
39
- def connect_to_udp_tracker
40
- @udp_trackers.map! do |udp|
41
- port = udp[/\d+/]
42
- udp.gsub!(/udp\:\/\/|\:\d+|\/announce/, '')
43
- begin
44
- ip = Socket.getaddrinfo(udp, 80)[0][3]
45
- udp_socket = UDPConnection.new(ip, port.to_i, sha)
46
- udp_socket.connect_to_udp_host
47
- rescue SocketError
48
- puts 'Unuseable UDP site'
49
- end
50
- end
22
+ trackers.each do |track|
23
+ tracker = build_tracker(track)
51
24
 
52
- while @peers.nil?
53
- udp = @udp_trackers.shift
54
- begin
55
- @peers = udp.message_relay
56
- rescue
25
+ tracker.connect
26
+ if tracker.connected?
27
+ @peers = tracker.address_list
28
+ break
57
29
  end
58
-
59
- end
60
- end
61
-
62
-
63
- # url gets reformatted to include query parameters
64
-
65
- def raw_peers
66
- begin
67
- BEncode.load(Net::HTTP.get(@uri))['peers']
68
- rescue
69
- nil
70
30
  end
71
- end
72
-
73
31
 
74
- def piece_length
75
- stream['info']['piece length']
32
+ establish_reactor if @peers
76
33
  end
77
34
 
78
- def sha
79
- Digest::SHA1.digest(stream['info'].bencode)
80
- end
81
-
82
- # data stored as a hash in the order made necessary
83
-
84
- def peer_hash
35
+ def peer_info
85
36
  {
86
- :info_hash => sha,
87
- :peer_id => PEER_ID,
88
- :left => piece_length,
89
- :pieces => stream['info']['files'] || stream['info']['name']
37
+ :info_hash => @sha,
38
+ :piece_length => @piece_length,
39
+ :sha_list => @sha_list,
40
+ :piece_index => Array.new(@sha_list.size) { false }
90
41
  }
91
42
  end
92
43
 
93
- # Using the peers key of the torrent file, the hex-encoded data gets reinterpreted as ips addresses.
94
-
95
- def peer_list
96
- ip_list = []
97
- until @peers.empty?
98
- ip_list << { ip: @peers.slice!(0..3).bytes.join('.'), port: @peers.slice!(0..1).unpack("S>").first }
44
+ def parse_addresses(addr, size)
45
+ Array.new(size) do
46
+ peer = { :ip => addr.slice!(0..3).bytes.join('.'),
47
+ :port => port(addr.slice!(0..1)) }
48
+ Peer.new(peer, @file_list, peer_info)
99
49
  end
100
-
101
- @peers = ip_list.map { |peer| Peer.new(peer, file_list, peer_info) }
102
50
  end
103
51
 
104
- def sha_list
105
- n, e = 0, 20
106
- list = []
107
- until stream['info']['pieces'].bytesize < e
108
- list << stream['info']['pieces'].byteslice(n...e)
109
- n += 20
110
- e += 20
111
- end
112
- list
52
+ def port(addr)
53
+ addr.unpack("S>").first
113
54
  end
114
55
 
115
- def peer_info
116
- {
117
- :info_hash => sha,
118
- :piece_length => piece_length,
119
- :sha_list => sha_list,
120
- :piece_index => Array.new(sha_list.size) { false }
121
- }
56
+ def build_tracker(track)
57
+ track.include?('http://') ? HTTPTracker.new(track, stream) : UDPTracker.new(track, stream)
122
58
  end
123
59
 
124
- def file_list
125
- stream['info']['files'] || stream['info']
60
+ def sha_pieces
61
+ Array.new(@sha_list.bytesize / 20) { @sha_list.slice!(0..19) }
126
62
  end
127
63
 
128
64
  def establish_reactor
129
- react = Reactor.new(@peers, sha_list, piece_length, file_list)
65
+ react = Reactor.new(@peers, @sha_list, @piece_length, @file_list)
130
66
  begin
131
67
  react.message_reactor
132
68
  end
@@ -0,0 +1,104 @@
1
+ module Torrenter
2
+ class UDPTracker < TorrentReader
3
+ attr_reader :socket, :response
4
+
5
+ def initialize(tracker, stream)
6
+ @url = tracker[/(?<=udp\:\/\/).+(?=\:\d+)/]
7
+ @port = tracker[/\d+$/].to_i
8
+ @socket = UDPSocket.new
9
+ @connection_id = [0x41727101980].pack("Q>")
10
+ super(stream)
11
+ end
12
+
13
+ def connect
14
+ begin
15
+ @transaction_id = [rand(1000)].pack("I>")
16
+ @socket.connect(ip_addr, @port)
17
+ send_msg(connect_msg)
18
+ read_response
19
+ rescue
20
+ false
21
+ end
22
+ end
23
+
24
+ def send_msg(msg)
25
+ begin
26
+ @socket.send(msg, 0)
27
+ rescue
28
+ false
29
+ end
30
+ end
31
+
32
+ def connected?
33
+ @response
34
+ end
35
+
36
+ def ip_addr
37
+ Socket.getaddrinfo(@url, @port)[0][3]
38
+ end
39
+
40
+ def connect_to_udp_host
41
+ begin
42
+ @socket.connect(@ip, @port)
43
+ return self
44
+ rescue
45
+ false
46
+ end
47
+ end
48
+
49
+ def address_list
50
+
51
+ @connection_id = @response[-8..-1]
52
+ @transaction_id = [rand(10000)].pack("I>")
53
+ send_msg(announce_msg)
54
+
55
+ read_response
56
+
57
+ parse_announce if @response[0..3] == action(1)
58
+ parse_addresses(@response, @response.size)
59
+ end
60
+
61
+ def parse_announce
62
+ if @response[4..7] == @transaction_id
63
+ res = @response.slice!(0..11)
64
+ leechers = @response.slice!(0..3).unpack("I>").first
65
+ seeders = @response.slice!(0..3).unpack("I>").first
66
+ end
67
+ end
68
+
69
+ def send_message
70
+ begin
71
+ @socket.send(@msg, 0)
72
+ rescue
73
+ end
74
+ end
75
+
76
+ def read_response
77
+ begin
78
+ @response = @socket.recv(1028)
79
+ rescue Exception => e
80
+ e
81
+ end
82
+ end
83
+
84
+ def connect_match?
85
+ data[0] == (action(0) + @transaction_id + @connection_id)
86
+ end
87
+
88
+ def announce_input
89
+ @connection_id + action(1) + @transaction_id + @sha + PEER_ID
90
+ end
91
+
92
+ def connect_msg
93
+ @connection_id + action(0) + @transaction_id
94
+ end
95
+
96
+ def action(n)
97
+ [n].pack("I>")
98
+ end
99
+
100
+ def announce_msg
101
+ @connection_id + action(1) + @transaction_id + @sha + PEER_ID + [0].pack("Q>") + [0].pack("Q>") + [0].pack("Q>") + action(0) + action(0) + action(0) + action(-1) + [@socket.addr[1]].pack(">S")
102
+ end
103
+ end
104
+ end
@@ -1,3 +1,3 @@
1
1
  module Torrenter
2
- VERSION = '0.0.3'
2
+ VERSION = '0.0.4'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torrenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - wismer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-25 00:00:00.000000000 Z
11
+ date: 2014-04-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: BitTorrent Client written in Ruby
14
14
  email:
@@ -20,12 +20,13 @@ extra_rdoc_files: []
20
20
  files:
21
21
  - bin/torrenter
22
22
  - lib/torrenter.rb
23
+ - lib/torrenter/http_tracker.rb
23
24
  - lib/torrenter/message/message_types.rb
24
25
  - lib/torrenter/message/messager.rb
25
26
  - lib/torrenter/peer.rb
26
27
  - lib/torrenter/reactor.rb
27
28
  - lib/torrenter/torrent_reader.rb
28
- - lib/torrenter/udp.rb
29
+ - lib/torrenter/udp_tracker.rb
29
30
  - lib/torrenter/version.rb
30
31
  homepage: http://wismer.github.io
31
32
  licenses:
@@ -1,89 +0,0 @@
1
- module Torrenter
2
- class UDPConnection
3
- attr_reader :sender, :response
4
- def initialize(ip, port, info_hash)
5
- @ip = ip
6
- @port = port
7
- @sender = UDPSocket.new
8
- @t = rand(10000)
9
- @connection_id = [0x41727101980].pack("Q>")
10
- @transaction_id = [@t].pack("I>")
11
- @info_hash = info_hash
12
- end
13
-
14
- def connect_to_udp_host
15
- begin
16
- @sender.connect(@ip, @port)
17
- return self
18
- rescue
19
- false
20
- end
21
- end
22
-
23
- def message_relay
24
- @sender.send(connect_msg, 0)
25
-
26
- read_response
27
-
28
- if @response
29
- @connection_id = @response[-8..-1]
30
- @transaction_id = [rand(10000)].pack("I>")
31
- @sender.send(announce_msg, 0)
32
- end
33
-
34
- read_response
35
-
36
- if @response
37
- parse_announce if @response[0..3] == action(1)
38
- return @response
39
- end
40
- end
41
-
42
- def parse_announce
43
- if @response[4..7] == @transaction_id
44
- res = @response.slice!(0..11)
45
- leechers = @response.slice!(0..3).unpack("I>").first
46
- seeders = @response.slice!(0..3).unpack("I>").first
47
- end
48
- end
49
-
50
- def send_message
51
- begin
52
- @sender.send(@msg, 0)
53
- rescue
54
- end
55
- end
56
-
57
- def read_response
58
- begin
59
- @response = @sender.recv(1028)
60
- rescue Exception => e
61
- e
62
- end
63
- end
64
-
65
- def connect_match?
66
- data[0] == (action(0) + @transaction_id + @connection_id)
67
- end
68
-
69
- def announce_input
70
- @connection_id + action(1) + @transaction_id + @info_hash + PEER_ID
71
- end
72
-
73
- def connect_msg
74
- @connection_id + action(0) + @transaction_id
75
- end
76
-
77
- def port
78
- @sender.addr[1]
79
- end
80
-
81
- def action(n)
82
- [n].pack("I>")
83
- end
84
-
85
- def announce_msg
86
- @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) + [port].pack(">S")
87
- end
88
- end
89
- end