masquito 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ pkg
4
+ test/tmp
5
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012-2013 Dmitry Vorotilin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # Masquito
2
+
3
+ This gem is for DNS masquerading for rubists. Everyone knows `Pow`, but what
4
+ I really don't like that it uses `node.js` to run ruby applications. Sometimes
5
+ we have to run our apps on the certain domain. Masquito allows you to do it,
6
+ you can create dns records in the similar Pow's way by creating symbolic links
7
+ in `.masquito` directory to your apps or just files because masquito doesn't
8
+ run rack applications just does dns masquerading.
9
+
10
+ ## Installation
11
+
12
+ First of all, uninstall Pow if you have it installed.
13
+ I suppose you're on rbenv(we will add rvm support as soon as we can).
14
+
15
+ Install gem:
16
+
17
+ $ gem install masquito
18
+
19
+ Run these commands:
20
+
21
+ $ masquito daemon install
22
+ $ sudo masquito resolver install
23
+
24
+ The first one installs masquito dns server as a daemon in your system which will
25
+ be started at each boot. This daemon will respond on dns queries from your
26
+ system. The second one registers dns resolver (`man 5 resolver` for more info)
27
+ for `.dev` domain. You have to run it with sudo because we must write config to
28
+ `/etc/resolver` path.
29
+
30
+ ## Usage
31
+
32
+ Once you have installed masquito you can add domains to ~/.masquito
33
+
34
+ $ cd ~/.masquito
35
+ $ touch example
36
+
37
+ It will create for you example.dev domain and you can already ping and use it.
38
+
39
+ ## Contributing
40
+
41
+ 1. Fork it
42
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
43
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
44
+ 4. Push to the branch (`git push origin my-new-feature`)
45
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'rake/testtask'
4
+ require 'bundler/gem_tasks'
5
+
6
+ task :default => [:test]
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << 'test'
10
+ t.test_files = FileList['test/*_test.rb']
11
+ t.verbose = false
12
+ end
data/bin/masquito ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'masquito'
5
+
6
+ abort 'Works only on Mac OS X' if RUBY_PLATFORM !~ /darwin/
7
+
8
+ command = ARGV[0]
9
+ subcommand = ARGV[1]
10
+ filename = File.basename(__FILE__)
11
+
12
+ case command
13
+ when 'start'
14
+ Masquito::DNS.new
15
+ when /daemon|resolver/
16
+ require 'masquito/install'
17
+ options = %w(install uninstall)
18
+ unless options.include?(subcommand)
19
+ abort "Usage: #{filename} #{command} #{options.join(' | ')}"
20
+ end
21
+ Masquito.send("#{command}_#{subcommand}")
22
+ when 'version'
23
+ require 'masquito/version'
24
+ puts Masquito::VERSION
25
+ else
26
+ puts "Usage: #{filename} #{%w(daemon resolver version).join(' | ')}"
27
+ end
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>com.evrone.masquito</string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string>sh</string>
10
+ <string>-i</string>
11
+ <string>-c</string>
12
+ <string>$SHELL --login -c "<%= masquito_bin %> start"</string>
13
+ </array>
14
+ <key>KeepAlive</key>
15
+ <true/>
16
+ <key>RunAtLoad</key>
17
+ <true/>
18
+ </dict>
19
+ </plist>
@@ -0,0 +1,3 @@
1
+ nameserver 127.0.0.1
2
+ port <%= Masquito::DNS::PORT %>
3
+ domain dev
data/lib/masquito.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Masquito
2
+ CONFIG_PATH = File.join(ENV['HOME'], '.masquito')
3
+
4
+ autoload :DNS, 'masquito/dns'
5
+ autoload :Settings, 'masquito/settings'
6
+ autoload :VERSION, 'masquito/version'
7
+ end
@@ -0,0 +1,62 @@
1
+ require 'socket'
2
+ require 'resolv'
3
+
4
+ module Masquito
5
+ class DNS
6
+ ADDRESS = '127.0.0.1'
7
+ PORT = 53532
8
+
9
+ @@resource = {
10
+ 'A' => Resolv::DNS::Resource::IN::A.new(ADDRESS),
11
+ 'AAAA' => Resolv::DNS::Resource::IN::AAAA.new('::1')
12
+ }
13
+ @@ttl = 10800 # 3 hours
14
+
15
+ def initialize(addr = ADDRESS, port = PORT, config_path = CONFIG_PATH)
16
+ puts "Starting Masquito on #{addr}:#{port}"
17
+ @settings = Settings.new(config_path)
18
+
19
+ # Bind port to receive requests
20
+ socket = UDPSocket.new
21
+ socket.bind(addr, port)
22
+
23
+ loop do
24
+ # Receive and parse query
25
+ data, sender_addrinfo = socket.recvfrom(512)
26
+
27
+ Thread.new(data, sender_addrinfo) do |data, sender_addrinfo|
28
+ sender_port, sender_ip = sender_addrinfo[1], sender_addrinfo[2]
29
+ query = Resolv::DNS::Message.decode(data)
30
+ answer = setup_answer(query)
31
+ socket.send(answer.encode, 0, sender_ip, sender_port) # Send the response
32
+ end
33
+ end
34
+ end
35
+
36
+ # Setup answer
37
+ def setup_answer(query)
38
+ # Standard fields
39
+ answer = Resolv::DNS::Message.new(query.id)
40
+ answer.qr = 1 # 0 = Query, 1 = Response
41
+ answer.opcode = query.opcode # Type of Query; copy from query
42
+ answer.aa = 1 # Is this an authoritative response: 0 = No, 1 = Yes
43
+ answer.rd = query.rd # Is Recursion Desired, copied from query
44
+ answer.ra = 0 # Does name server support recursion: 0 = No, 1 = Yes
45
+ answer.rcode = 0 # Response code: 0 = No errors
46
+ each_question(query, answer) # There may be multiple questions per query
47
+ end
48
+
49
+ def each_question(query, answer)
50
+ query.each_question do |name, typeclass|
51
+ type = typeclass.name.split('::').last
52
+ if type == 'A' || type == 'AAAA' # We need only A and AAAA records
53
+ if @settings.include?(name)
54
+ answer.add_answer(name, @@ttl, @@resource[type]) # Setup answer to this name
55
+ answer.encode # Don't forget encode it
56
+ end
57
+ end
58
+ end
59
+ answer
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,90 @@
1
+ require 'erb'
2
+ require 'fileutils'
3
+ require 'lunchy'
4
+ require 'tempfile'
5
+
6
+ module Masquito
7
+ GEM_PATH = File.expand_path('../../../', __FILE__)
8
+ TEMPLATE_PLIST_PATH = File.join(GEM_PATH, 'config', 'com.evrone.masquito.plist.erb')
9
+ PLIST_NAME = File.basename(TEMPLATE_PLIST_PATH, '.erb')
10
+ SERVICE_NAME = File.basename(PLIST_NAME, '.plist')
11
+
12
+ RESOLVER_TEMPLATE_PATH = File.join(GEM_PATH, 'config', 'masquito.erb')
13
+ RESOLVER_PATH = '/etc/resolver/masquito'
14
+
15
+ class << self
16
+ def daemon_install
17
+ abort_if_superuser
18
+ FileUtils.mkdir_p(CONFIG_PATH)
19
+
20
+ plist = ERB.new(File.read(TEMPLATE_PLIST_PATH))
21
+ masquito_bin = File.join(GEM_PATH, 'bin', 'masquito')
22
+ template = plist.result(binding)
23
+
24
+ filename = File.join(Dir.tmpdir, PLIST_NAME)
25
+ File.open(filename, 'w') { |f| f.write(template) }
26
+ lunchy.install([filename])
27
+ lunchy.start([SERVICE_NAME])
28
+ File.unlink(filename)
29
+
30
+ puts 'Daemon was successfully installed.'
31
+ puts 'Run: sudo masquito resolver install'
32
+ end
33
+
34
+ def daemon_uninstall
35
+ abort_if_superuser
36
+ lunchy.stop([SERVICE_NAME])
37
+ lunchy.uninstall([PLIST_NAME])
38
+ puts "You can remove #{CONFIG_PATH} if you don't need these settings"
39
+ end
40
+
41
+ def resolver_install
42
+ abort_unless_superuser
43
+ resolver = ERB.new(File.read(RESOLVER_TEMPLATE_PATH))
44
+ template = resolver.result(binding)
45
+ File.open(RESOLVER_PATH, 'w') { |f| f.write(template) }
46
+ end
47
+
48
+ def resolver_uninstall
49
+ abort_unless_superuser
50
+ FileUtils.rm_rf(RESOLVER_PATH)
51
+ end
52
+
53
+ private
54
+
55
+ def lunchy
56
+ @lunchy ||= Lunchy.new
57
+ end
58
+
59
+ def abort_unless_superuser
60
+ unless superuser?
61
+ abort 'We need superuser privileges, run this command with sudo'
62
+ end
63
+ end
64
+
65
+ def abort_if_superuser
66
+ if superuser?
67
+ abort 'No need superuser privileges, run this command without sudo'
68
+ end
69
+ end
70
+
71
+ def superuser?
72
+ [Process.uid, Process.euid] == [0, 0]
73
+ end
74
+ end
75
+ end
76
+
77
+ class Lunchy
78
+ CONFIG = { :verbose => false, :write => false }
79
+
80
+ def uninstall(params)
81
+ raise ArgumentError, "uninstall [file]" if params.empty?
82
+ filename = params[0]
83
+ %w(~/Library/LaunchAgents /Library/LaunchAgents).each do |dir|
84
+ if File.exist?(File.expand_path(dir))
85
+ FileUtils.rm_rf(File.join(File.expand_path(dir), File.basename(filename)))
86
+ return puts "#{filename} uninstalled from #{dir}"
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,35 @@
1
+ require 'resolv'
2
+ require 'pathname'
3
+
4
+ module Masquito
5
+ class Settings
6
+ def initialize(config_path)
7
+ @pattern = File.join(config_path, '*')
8
+ end
9
+
10
+ def files
11
+ Dir.glob(@pattern).select do |file|
12
+ is_symlink = File.lstat(file).symlink?
13
+ !is_symlink || (is_symlink && Pathname.new(file).exist?)
14
+ end
15
+ end
16
+
17
+ def domains
18
+ files.map do |symlink|
19
+ name = File.basename(symlink)
20
+ Resolv::DNS::Name.create(has_domain?(name) ? "#{name}." : "#{name}.dev.")
21
+ end
22
+ end
23
+
24
+ def include?(name)
25
+ name = Resolv::DNS::Name.create(name) if name.kind_of?(String)
26
+ domains.any? { |n| name.eql?(n) || name.subdomain_of?(n) }
27
+ end
28
+
29
+ private
30
+
31
+ def has_domain?(name)
32
+ name.split('.').length >= 2
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Masquito
2
+ VERSION = '1.0'
3
+ end
data/masquito.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'masquito/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'masquito'
7
+ gem.version = Masquito::VERSION
8
+ gem.authors = ['Dmitry Vorotilin', 'Kir Shatrov']
9
+ gem.email = ['d.vorotilin@gmail.com', 'kirs@evrone.ru']
10
+ gem.description = 'Masquito is a dns masquerading server for rubists'
11
+ gem.summary = 'It masquerades your dns records'
12
+ gem.homepage = 'https://github.com/evrone/masquito'
13
+
14
+ gem.files = `git ls-files`.split($\)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^test/})
17
+ gem.require_paths = ['lib']
18
+
19
+ gem.add_runtime_dependency 'lunchy'
20
+ end
data/test/dns_test.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'resolv'
2
+ require 'fileutils'
3
+ require 'test_helper'
4
+
5
+ require 'masquito'
6
+
7
+ class TestMasquitoDNS < Test::Unit::TestCase
8
+ TEMP_DIR = File.join(Dir.pwd, 'test/tmp')
9
+
10
+ def setup
11
+ FileUtils.mkdir_p(TEMP_DIR)
12
+
13
+ file = File.new(File.join(TEMP_DIR, 'file'), 'w')
14
+ ['link', 'symlink.dev', 'symlink.domain'].each do |name|
15
+ symlink = File.join(TEMP_DIR, name)
16
+ FileUtils.ln_s(file.path, symlink)
17
+ end
18
+
19
+ @thread = Thread.new do
20
+ Masquito::DNS.new('127.0.0.1', '51234', TEMP_DIR)
21
+ end
22
+ end
23
+
24
+ def teardown
25
+ @thread.kill
26
+
27
+ FileUtils.rm_rf(TEMP_DIR)
28
+ end
29
+
30
+ def test_responses
31
+ silence_warnings do
32
+ Resolv::DNS.const_set(:Port, 51234)
33
+ end
34
+
35
+ response = Resolv::IPv4.create('127.0.0.1')
36
+ Resolv::DNS.open(:nameserver => ['127.0.0.1']) do |dns|
37
+ assert_equal response, dns.getaddress('link.dev.')
38
+ assert_equal response, dns.getaddress('sym.sym.link.dev.')
39
+ assert_equal response, dns.getaddress('symlink.dev.')
40
+ assert_equal response, dns.getaddress('www.symlink.dev.')
41
+ assert_equal response, dns.getaddress('symlink.domain.')
42
+ assert_equal response, dns.getaddress('asd.symlink.domain.')
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,51 @@
1
+ require 'fileutils'
2
+ require 'test_helper'
3
+
4
+ require 'masquito'
5
+
6
+ class TestMasquitoSettings < Test::Unit::TestCase
7
+ TEMP_DIR = File.join(Dir.pwd, 'test/tmp')
8
+
9
+ attr_reader :settings
10
+
11
+ def setup
12
+ FileUtils.mkdir_p(TEMP_DIR)
13
+
14
+ @settings = Masquito::Settings.new(TEMP_DIR)
15
+
16
+ file = File.new(File.join(TEMP_DIR, 'file'), 'w')
17
+ ['link', 'symlink.dev', 'symlink.domain'].each do |name|
18
+ symlink = File.join(TEMP_DIR, name)
19
+ FileUtils.ln_s(file.path, symlink)
20
+ end
21
+ end
22
+
23
+ def teardown
24
+ FileUtils.rm_rf(TEMP_DIR)
25
+ end
26
+
27
+ def test_files
28
+ result = ['file', 'link', 'symlink.dev', 'symlink.domain'].map do |name|
29
+ File.join(TEMP_DIR, name)
30
+ end
31
+
32
+ assert_equal result, settings.files
33
+ end
34
+
35
+ def test_domains
36
+ result = ['file.dev.', 'link.dev.', 'symlink.dev.', 'symlink.domain.'].map do |name|
37
+ Resolv::DNS::Name.create(name)
38
+ end
39
+
40
+ assert_equal result, settings.domains
41
+ end
42
+
43
+ def test_include
44
+ assert settings.include?('link.dev.')
45
+ assert settings.include?('sym.sym.link.dev.')
46
+ assert settings.include?('symlink.dev.')
47
+ assert settings.include?('www.symlink.dev.')
48
+ assert settings.include?('symlink.domain.')
49
+ assert settings.include?('asd.symlink.domain.')
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ require 'test/unit'
2
+
3
+ class Test::Unit::TestCase
4
+ def silence_warnings
5
+ old_verbose, $VERBOSE = $VERBOSE, nil
6
+ yield
7
+ ensure
8
+ $VERBOSE = old_verbose
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: masquito
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dmitry Vorotilin
9
+ - Kir Shatrov
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-04-05 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: lunchy
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ description: Masquito is a dns masquerading server for rubists
32
+ email:
33
+ - d.vorotilin@gmail.com
34
+ - kirs@evrone.ru
35
+ executables:
36
+ - masquito
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - .gitignore
41
+ - Gemfile
42
+ - LICENSE
43
+ - README.md
44
+ - Rakefile
45
+ - bin/masquito
46
+ - config/com.evrone.masquito.plist.erb
47
+ - config/masquito.erb
48
+ - lib/masquito.rb
49
+ - lib/masquito/dns.rb
50
+ - lib/masquito/install.rb
51
+ - lib/masquito/settings.rb
52
+ - lib/masquito/version.rb
53
+ - masquito.gemspec
54
+ - test/dns_test.rb
55
+ - test/settings_test.rb
56
+ - test/test_helper.rb
57
+ homepage: https://github.com/evrone/masquito
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.23
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: It masquerades your dns records
81
+ test_files:
82
+ - test/dns_test.rb
83
+ - test/settings_test.rb
84
+ - test/test_helper.rb