votifier 0.0.1
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.
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.md +38 -0
- data/Rakefile +8 -0
- data/bin/votifier-client +6 -0
- data/bin/votifier-server +6 -0
- data/config/sample-private.key +1 -0
- data/config/sample-public.key +1 -0
- data/lib/votifier.rb +4 -0
- data/lib/votifier/client.rb +81 -0
- data/lib/votifier/key.rb +51 -0
- data/lib/votifier/server.rb +64 -0
- data/lib/votifier/version.rb +3 -0
- data/lib/votifier/vote.rb +27 -0
- data/test/test_votifer.rb +8 -0
- data/votifier.gemspec +18 -0
- metadata +81 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 pr4xt3roy
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Votifier Gem
|
2
|
+
|
3
|
+
Ruby Gem for a Votifier Server and Client for Minecraft
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
For a manual installation, type this on your console:
|
8
|
+
|
9
|
+
$ gem install votifier
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
require 'votifier'
|
14
|
+
Votifier::Client.new(public_key_file, "MyMinecraftServerList.info", :username => "Notch").send
|
15
|
+
|
16
|
+
If you have the player's IP addess and the timestamp, you can pass them
|
17
|
+
|
18
|
+
require 'votifier'
|
19
|
+
Votifier::Client.new(public_key_file, "MyMinecraftServerList.info", :username => "Notch", :ip_address => @ip_address, :timestamp => @timestamp).send
|
20
|
+
|
21
|
+
For the server:
|
22
|
+
|
23
|
+
require 'votifier'
|
24
|
+
Votifier::Server.new(:private_key => private_key_file).listen
|
25
|
+
|
26
|
+
## TODO
|
27
|
+
|
28
|
+
* Unit Tests
|
29
|
+
* Support .pem, .key keys or string keys.
|
30
|
+
* The username, ip and timestamp should be passed to Votifier::Client#send instead of the contructor
|
31
|
+
|
32
|
+
## Contributing
|
33
|
+
|
34
|
+
1. Fork it
|
35
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
36
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
37
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
38
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/votifier-client
ADDED
data/bin/votifier-server
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
MIIEogIBAAKCAQEAqEyG+i/SNvCRsvM4jt3tm2fresIPCBpfU/5j0Pq4i2IjPzX0YQqHmBqrKqzIpMTgj9ITrxJ4v3FO8WHjjArEAJhYPc26w9cniVaXAFavQX07q0wZ9DJRdei6lchd5ecLQ5J90TrR4BpwxFGtw55vOpPElQi2jG1crr3fIqVspMtqPUXYesmD5Ymoace0ycPs46yS6ItWwgbPBaryeimUG0ErHoYV2aWjfP/FHnawU2n3fNWy3ZD9e7AWSwdFxYnPSZ3R9+V/YSlyYX/ls0k8SC/L9xB63rh8ifg66sgN5hlxbMIIwX2trGueeDYbYkiL499Hzj4KPfWT1L+t3a8HdwIDAQABAoIBAHcXSAAr7L3xt7gAggoeoq9wduI0Bfz+5GwEPE+5yvLerABjxp0QnWiVnn+hKo3lvt4mksU09frKTElksmb7izy8kNyTAtDoI9L8UffsXzGhU6LgRBFXtesQl0+8LC2uM3tKYvu2eqLxMtDI14PwTdcOj3HCa6KtFIOWWhojIuDs5f45V64pBqrC0a4Alcppzjb1rJTsg7FXp1EIwx+WEc+sV34y9dwogokQrpU7mD2LdTOT4jcQ/NnpSMXMGpbi+CtynOhM6G4vwy2hLaV5dENwchlyWYXnDPuDEZK6/+1WWWJnHV1mhKDK1PEvia7JRxWNlZeNrDKMpDgoDg5BuSECgYEA2wU7euS4ekugZ1XHakrno55phdS39jS4YaB9RoheAt2IusbYK309q8PtcsDt8mtT9F3ji5C66YJsvuht7Dfb4HXVvpPkKU8eN3wOzI3zBEZ0HEmbKf6AKTE4msjd3/zAeMGr2KSllJbomqrQBieMDdffRZ8qyo10WENlH5miw0cCgYEAxLbyygzV6r0mRxtw4H8SjrjAPyA9eOhwDB07peGi7kv6dEUYhu4gcaUPQSMigqV0ADNTed8ueq2HzJ4V5nqkg97sK6Q00yLOxUkwq5aKD4Fq1vctotawX9SOPyjzKwtu8BXzJDESIyrxuWgDB2ZGQtfvaI+guOVHakwq1+SJ0lECgYAtmchJjQdvxwEtoqlzUwcphNj5mVF3/IALK4wJvGi+dft6z93AKEJH2fI9OzgPihMfxNCUhVy1M4eKs//296Za9I29MTOG+kcTTEwUgNWBJV4VaanZs3hhmzLq3zl64jf8rG4oHBexeT4Svc+0CFXbp/9/kmUpaQw5OEb8rlM6DwKBgHUNe9kY3C2LEfzb6kXbt1ovu02UzJ9miE3dE0I4ym/LzCNlfhvEFJArLLGQy5c8BpOb9v8c+5ZKLfVm2BdGQSCrBJNPaZN9MdaZH5nqwQpK8x0pBgHQiDDt1RrmLhQeAIxiM0UE4gsFIcM2jh7YD9T0kuQAGx4KaH1rB09iEVSRAoGALrDquzyGNU75h9X46wFibbLbRs5H/pjwFn2kXjdfCatrPRYhCDBp2cM2jRLt3xua/Icg8b3tzAEcyabIuieH0tTI+Cc+IzrJeLspHHOvgqa1voJEwnkmsCorwh4umV+5yCtFWuVQNNpbU7Kk50FhlSaS75JQsrxmkUJaQ7+9Lt0=
|
@@ -0,0 +1 @@
|
|
1
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqEyG+i/SNvCRsvM4jt3tm2fresIPCBpfU/5j0Pq4i2IjPzX0YQqHmBqrKqzIpMTgj9ITrxJ4v3FO8WHjjArEAJhYPc26w9cniVaXAFavQX07q0wZ9DJRdei6lchd5ecLQ5J90TrR4BpwxFGtw55vOpPElQi2jG1crr3fIqVspMtqPUXYesmD5Ymoace0ycPs46yS6ItWwgbPBaryeimUG0ErHoYV2aWjfP/FHnawU2n3fNWy3ZD9e7AWSwdFxYnPSZ3R9+V/YSlyYX/ls0k8SC/L9xB63rh8ifg66sgN5hlxbMIIwX2trGueeDYbYkiL499Hzj4KPfWT1L+t3a8HdwIDAQAB
|
data/lib/votifier.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'socket' # Sockets are in standard library
|
2
|
+
|
3
|
+
module Votifier
|
4
|
+
class Client
|
5
|
+
|
6
|
+
attr_reader :public_key, :service_name
|
7
|
+
attr_accessor :username, :ip_address, :timestamp
|
8
|
+
|
9
|
+
def initialize(public_key_file, service_name, opts = {})
|
10
|
+
init_public_key(public_key_file)
|
11
|
+
@service_name = service_name
|
12
|
+
@username = opts.fetch(:username, nil)
|
13
|
+
@ip_address = opts.fetch(:ip_address, detect_ip_address)
|
14
|
+
@timestamp = opts.fetch(:timestamp, Time.now.to_i)
|
15
|
+
end
|
16
|
+
|
17
|
+
def init_public_key(public_key_file)
|
18
|
+
@public_key = Votifer::Key.from_public_key_file(public_key_file)
|
19
|
+
end
|
20
|
+
|
21
|
+
def detect_ip_address
|
22
|
+
remote_ip = ENV['HTTP_X_FORWARDED_FOR'] || ENV['REMOTE_ADDR'] || my_best_ip_address
|
23
|
+
remote_ip = remote_ip.ip_address if remote_ip.respond_to?(:ip_address)
|
24
|
+
remote_ip = remote_ip.scan(/[\d.]+/).first
|
25
|
+
end
|
26
|
+
|
27
|
+
def my_best_ip_address
|
28
|
+
my_first_public_ipv4 || my_first_private_ipv4
|
29
|
+
end
|
30
|
+
|
31
|
+
def my_first_public_ipv4
|
32
|
+
ips.detect{|intf| intf.ipv4? and !intf.ipv4_loopback? and !intf.ipv4_multicast? and !intf.ipv4_private?}
|
33
|
+
end
|
34
|
+
|
35
|
+
def my_first_private_ipv4
|
36
|
+
ips.detect{|intf| intf.ipv4_private?}
|
37
|
+
end
|
38
|
+
|
39
|
+
def ips
|
40
|
+
@ips ||= Socket.ip_address_list
|
41
|
+
end
|
42
|
+
|
43
|
+
def send
|
44
|
+
validate!
|
45
|
+
votifier_string = to_a.join("\n")
|
46
|
+
encrypted = encrypt(votifier_string)
|
47
|
+
send_to_server(encrypted)
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate!
|
51
|
+
raise 'Usename must be present' unless !username.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
def encrypt(votifier_string)
|
55
|
+
encrypted = public_key.public_encrypt(votifier_string)
|
56
|
+
raise "Error encrypting, invalid size: #{encrypted.bytesize}" unless encrypted.bytesize == 256
|
57
|
+
encrypted
|
58
|
+
end
|
59
|
+
|
60
|
+
def send_to_server(encrypted)
|
61
|
+
hostname = 'localhost'
|
62
|
+
port = 8193
|
63
|
+
|
64
|
+
s = TCPSocket.open(hostname, port)
|
65
|
+
|
66
|
+
s.print encrypted
|
67
|
+
s.close
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_a
|
71
|
+
[
|
72
|
+
"VOTE",
|
73
|
+
service_name,
|
74
|
+
username,
|
75
|
+
ip_address,
|
76
|
+
timestamp
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/votifier/key.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'openssl'
|
3
|
+
module Votifer
|
4
|
+
class Key
|
5
|
+
|
6
|
+
def initialize(pem_key_content)
|
7
|
+
@key = OpenSSL::PKey::RSA.new(pem_key_content)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.from_pem_key_file(key_file)
|
11
|
+
key_content = File.read(key_file)
|
12
|
+
self.from_perm_key_content(key_content)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_public_key_file(key_file)
|
16
|
+
self.from_key_file(key_file, :public)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_key_file(key_file, type = :private)
|
20
|
+
key_content = File.read(key_file)
|
21
|
+
self.from_key_content(key_content, type)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_key_content(key_content, type = :private)
|
25
|
+
pem_key_content = convert_to_pem_format(key_content, type)
|
26
|
+
from_pem_key_content(pem_key_content)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.from_pem_key_content(pem_key_content)
|
30
|
+
key = self.new(pem_key_content)
|
31
|
+
key
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.convert_to_pem_format(key_content, type = :private)
|
35
|
+
key_headers = {
|
36
|
+
:private => "RSA PRIVATE",
|
37
|
+
:public => "PUBLIC"
|
38
|
+
}
|
39
|
+
header = key_headers[type]
|
40
|
+
wrapped_key_content = key_content.scan(/.{1,65}/).join("\n")
|
41
|
+
pem_key_content = "-----BEGIN #{header} KEY-----\n" +
|
42
|
+
wrapped_key_content + "\n" +
|
43
|
+
"-----END #{header} KEY-----"
|
44
|
+
pem_key_content
|
45
|
+
end
|
46
|
+
|
47
|
+
extend Forwardable
|
48
|
+
def_delegators :@key, :private_decrypt, :public_encrypt
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'socket' # Get sockets from stdlib
|
2
|
+
module Votifier
|
3
|
+
class Server
|
4
|
+
|
5
|
+
attr_reader :private_key
|
6
|
+
|
7
|
+
def initialize(opts)
|
8
|
+
raise ':private_key options is required' unless opts.key?(:private_key)
|
9
|
+
init_private_key(opts[:private_key])
|
10
|
+
end
|
11
|
+
|
12
|
+
def init_private_key(private_key_file)
|
13
|
+
@private_key = Votifer::Key.from_key_file(private_key_file)
|
14
|
+
end
|
15
|
+
|
16
|
+
def listen
|
17
|
+
server = TCPServer.open(8193) # Socket to listen on port
|
18
|
+
loop { # Servers run forever
|
19
|
+
Thread.start(server.accept) do |client| # Wait for a client to connect
|
20
|
+
handle_connection(client)
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def handle_connection(client)
|
26
|
+
lines = ""
|
27
|
+
while line = client.gets
|
28
|
+
lines += line
|
29
|
+
end
|
30
|
+
begin
|
31
|
+
vote_info = parse_content(lines)
|
32
|
+
vote = Vote.from_array(vote_info)
|
33
|
+
puts vote.to_h
|
34
|
+
client.close # Disconnect from the client
|
35
|
+
rescue => e
|
36
|
+
puts e.inspect
|
37
|
+
puts e.backtrace
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_content(content)
|
42
|
+
raise Error::InvalidVotePacket, "Must be 256 bytes: is #{content.bytesize}" unless content.bytesize == 256
|
43
|
+
vote_info_string = decrypt(content)
|
44
|
+
raise Error::InvalidVotePacket, 'Must contains newline characters' unless vote_info_string.match(/\n/)
|
45
|
+
vote_info = vote_info_string.split("\n")
|
46
|
+
raise Error::InvalidVotePacket, 'Must contain 5 elements or more' unless vote_info.size >= 5
|
47
|
+
raise Error::InvalidVotePacket, 'Invalid opcode: must be VOTE' unless vote_info[0] == "VOTE"
|
48
|
+
vote_info
|
49
|
+
end
|
50
|
+
|
51
|
+
def decrypt(encrypted_string)
|
52
|
+
string = private_key.private_decrypt(encrypted_string)
|
53
|
+
string
|
54
|
+
end
|
55
|
+
|
56
|
+
class Error
|
57
|
+
class InvalidVotePacket < StandardError
|
58
|
+
def initialize(str)
|
59
|
+
super("Invalid Vote Packet: #{str}")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Votifier
|
2
|
+
class Vote
|
3
|
+
attr_reader :service_name, :username, :ip_address, :timestamp
|
4
|
+
|
5
|
+
def initialize(service_name, username, ip_address, timestamp)
|
6
|
+
@service_name = service_name
|
7
|
+
@username = username
|
8
|
+
@ip_address = ip_address
|
9
|
+
@timestamp = timestamp
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_array(vote_info)
|
13
|
+
opcode, service_name, username, ip_address, timestamp, empty_space = vote_info
|
14
|
+
vote = Vote.new(service_name, username, ip_address, timestamp)
|
15
|
+
vote
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
{
|
20
|
+
:service_name => service_name,
|
21
|
+
:username => username,
|
22
|
+
:ip_address => ip_address,
|
23
|
+
:timestamp => timestamp
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/votifier.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/votifier/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'votifier'
|
6
|
+
gem.summary = %q{Votifier Server and Client for Minecraft}
|
7
|
+
gem.description = %q{This allows you to accept Votifier connections OR send them to a Minecraft Server.}
|
8
|
+
gem.authors = ["Pr4xt3roy"]
|
9
|
+
gem.email = %W(pr4xt3roy@capricatown.net)
|
10
|
+
gem.homepage = 'https://github.com/pr4xt3roy/votifier-gem'
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.license = 'MIT'
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
|
16
|
+
gem.version = Votifier::VERSION
|
17
|
+
gem.date = '2013-09-08'
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: votifier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Pr4xt3roy
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-09-08 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: This allows you to accept Votifier connections OR send them to a Minecraft Server.
|
22
|
+
email:
|
23
|
+
- pr4xt3roy@capricatown.net
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- LICENSE
|
33
|
+
- README.md
|
34
|
+
- Rakefile
|
35
|
+
- bin/votifier-client
|
36
|
+
- bin/votifier-server
|
37
|
+
- config/sample-private.key
|
38
|
+
- config/sample-public.key
|
39
|
+
- lib/votifier.rb
|
40
|
+
- lib/votifier/client.rb
|
41
|
+
- lib/votifier/key.rb
|
42
|
+
- lib/votifier/server.rb
|
43
|
+
- lib/votifier/version.rb
|
44
|
+
- lib/votifier/vote.rb
|
45
|
+
- test/test_votifer.rb
|
46
|
+
- votifier.gemspec
|
47
|
+
homepage: https://github.com/pr4xt3roy/votifier-gem
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 3
|
70
|
+
segments:
|
71
|
+
- 0
|
72
|
+
version: "0"
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.8.15
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Votifier Server and Client for Minecraft
|
80
|
+
test_files: []
|
81
|
+
|