diff --git a/.gitignore b/.gitignore index f541d9a..d87d4be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,17 @@ -.idea -.idea/* -.idea/**/* +*.gem +*.rbc +.bundle +.config +.yardoc +Gemfile.lock +InstalledFiles +_yardoc +coverage +doc/ +lib/bundler/man +pkg +rdoc +spec/reports +test/tmp +test/version_tmp +tmp diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..1215358 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in passageway.gemspec +gemspec diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3730a3b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2013 R. Tyler Croy + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Manifest b/Manifest deleted file mode 100644 index 2364226..0000000 --- a/Manifest +++ /dev/null @@ -1,6 +0,0 @@ -Rakefile -lib/localtunnel.rb -lib/localtunnel/tunnel.rb -lib/localtunnel/net_ssh_gateway_patch.rb -bin/localtunnel -Manifest diff --git a/README.md b/README.md new file mode 100644 index 0000000..f20e0c0 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Passageway + +TODO: Write a gem description + +## Installation + +Add this line to your application's Gemfile: + + gem 'passageway' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install passageway + +## Usage + +TODO: Write usage instructions here + +## Contributing + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 984e74a..0000000 --- a/README.rdoc +++ /dev/null @@ -1,65 +0,0 @@ -= localtunnel -- instant public tunnel to your local web server - -== Install - -To get the dependencies if you don't have them, type: - - sudo apt-get install ruby ruby1.8-dev rubygems1.8 libopenssl-ruby - -Now you can install localtunnel with RubyGems: - - sudo gem install localtunnel - -or to get the source: - - git clone http://github.com/progrium/localtunnel.git - -== Usage - - localtunnel [options] - -k, --key FILE upload a public key for authentication - -Localtunnel is a client to a free and open source reverse tunneling -service made specifically for web traffic. It's intended to be used to -temporarily expose local web servers to the greater Internet for -debugging, unit tests, demos, etc. - -This is how you make your local port 8080 public: - - $ localtunnel 8080 - Port 8080 is now publicly accessible from http://8bv2.localtunnel.com ... - -Using localtunnel is comparable to using SSH reverse/remote port -forwarding on a remote host that has GatewayPorts enabled, but without -all the configuration or the need of a host. The localtunnel command -works with a server component that is running on localtunnel.com, -which is provided as a free service. - -If you have never run localtunnel before, you'll need to upload a public -key to authenticate. You do this once: - - $ localtunnel -k ~/.ssh/id_rsa.pub 8080 - -After that, you shouldn't have to use -k again. - -Localtunnel can be started before or after the local web server. It -tunnels through to the url given in that status message "publicly -accessible from..." for as long as the command is running. The tunnel -is closed if the command exits. - -Localtunnel will search for the file .localtunnel_callback in the CWD. -If it exists, it will execute the file with one argument, the public -endpoint, when the tunnel is opened. This is useful for starting other -tools or processes that need the name of the endpoint. - -== Contributors - - andyl (andy@r210.com) - Charles Merriam (charles.merriam@gmail.com) - Hunter Gillane (hunter.gillane@gmail.com) - Michael Sofaer (msofaer@pivotallabs.com) - Jeff Lindsay (progrium@gmail.com) - -== License - -MIT diff --git a/Rakefile b/Rakefile index 25a44c9..2995527 100644 --- a/Rakefile +++ b/Rakefile @@ -1,16 +1 @@ -require 'rubygems' -require 'rake' -require 'echoe' - -Echoe.new('localtunnel', '0.3.1') do |p| - p.description = "instant public tunnel to your local web server" - p.url = "http://github.com/progrium/localtunnel" - p.author = "Jeff Lindsay" - p.email = "jeff.lindsay@twilio.com" - p.rdoc_pattern = // - p.rdoc_options = [] - p.ignore_pattern = ["tmp/*", "script/*"] - p.executable_pattern = ["bin/*"] - p.runtime_dependencies = ["json >=1.2.4", "net-ssh >=2.0.22", "net-ssh-gateway >=1.0.1"] - p.development_dependencies = [] -end \ No newline at end of file +require "bundler/gem_tasks" diff --git a/bin/localtunnel b/bin/passageway similarity index 100% rename from bin/localtunnel rename to bin/passageway diff --git a/lib/localtunnel.rb b/lib/localtunnel.rb deleted file mode 100644 index 6cb7b35..0000000 --- a/lib/localtunnel.rb +++ /dev/null @@ -1,2 +0,0 @@ -$: << File.expand_path(File.dirname(__FILE__)) -require 'localtunnel/tunnel' diff --git a/lib/passageway.rb b/lib/passageway.rb new file mode 100644 index 0000000..cc3cee4 --- /dev/null +++ b/lib/passageway.rb @@ -0,0 +1,5 @@ +require "passageway/version" + +module Passageway + # Your code goes here... +end diff --git a/lib/localtunnel/tunnel.rb b/lib/passageway/client.rb similarity index 100% rename from lib/localtunnel/tunnel.rb rename to lib/passageway/client.rb diff --git a/lib/localtunnel/net_ssh_gateway_patch.rb b/lib/passageway/net_ssh_gateway_patch.rb similarity index 100% rename from lib/localtunnel/net_ssh_gateway_patch.rb rename to lib/passageway/net_ssh_gateway_patch.rb diff --git a/lib/passageway/version.rb b/lib/passageway/version.rb new file mode 100644 index 0000000..56f3ace --- /dev/null +++ b/lib/passageway/version.rb @@ -0,0 +1,3 @@ +module Passageway + VERSION = "0.0.1" +end diff --git a/passageway.gemspec b/passageway.gemspec new file mode 100644 index 0000000..f8cca8d --- /dev/null +++ b/passageway.gemspec @@ -0,0 +1,23 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'passageway/version' + +Gem::Specification.new do |spec| + spec.name = "passageway" + spec.version = Passageway::VERSION + spec.authors = ["R. Tyler Croy"] + spec.email = ["tyler@monkeypox.org"] + spec.description = %q{TODO: Write a gem description} + spec.summary = %q{TODO: Write a gem summary} + spec.homepage = "" + spec.license = "MIT" + + spec.files = `git ls-files`.split($/) + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } + spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.3" + spec.add_development_dependency "rake" +end diff --git a/server.py b/server.py deleted file mode 100644 index 646a0ed..0000000 --- a/server.py +++ /dev/null @@ -1,106 +0,0 @@ -try: - from twisted.internet import pollreactor - pollreactor.install() -except: pass -from twisted.internet import protocol, reactor, defer, task -from twisted.web import http, proxy, resource, server -from twisted.python import log -import sys, time -import urlparse -import socket -import simplejson -import re - -SSH_USER = 'localtunnel' -AUTHORIZED_KEYS = '/home/localtunnel/.ssh/authorized_keys' -PORT_RANGE = [32000, 64000] -BANNER = "This localtunnel service is brought to you by Twilio." -SSH_OPTIONS = 'command="/bin/echo Shell access denied",no-agent-forwarding,no-pty,no-user-rc,no-X11-forwarding ' -KEY_REGEX = re.compile(r'^ssh-(\w{3}) [^\n]+$') - -def port_available(port): - try: - socket.create_connection(['127.0.0.1', port]).close() - return False - except socket.error: - return True - -def baseN(num,b=32,numerals="23456789abcdefghijkmnpqrstuvwxyz"): - return ((num == 0) and "0" ) or (baseN(num // b, b).lstrip("0") + numerals[num % b]) - -class LocalTunnelReverseProxy(proxy.ReverseProxyResource): - isLeaf = True - - def __init__(self, user, host='127.0.0.1'): - self.user = user - self.tunnels = {} - proxy.ReverseProxyResource.__init__(self, host, None, None) - - def find_tunnel_name(self): - name = baseN(abs(hash(time.time())))[0:4] - if (name in self.tunnels and not port_available(self.tunnels[name])) or name == 'open': - time.sleep(0.1) - return self.find_tunnel_name() - return name - - def find_tunnel_port(self): - port = PORT_RANGE[0] - start_time = time.time() - while not port_available(port): - if time.time()-start_time > 3: - raise Exception("No port available") - port += 1 - if port >= PORT_RANGE[1]: port = PORT_RANGE[0] - return port - - def garbage_collect(self): - for name in self.tunnels: - if port_available(self.tunnels[name]): - del self.tunnels[name] - - def install_key(self, key): - if not KEY_REGEX.match(key.strip()): - return False - key = ''.join([SSH_OPTIONS, key.strip(), "\n"]) - fr = open(AUTHORIZED_KEYS, 'r') - if not key in fr.readlines(): - fa = open(AUTHORIZED_KEYS, 'a') - fa.write(key) - fa.close() - fr.close() - return True - - def register_tunnel(self, superhost, key=None): - if key and not self.install_key(key): return simplejson.dumps(dict(error="Invalid key.")) - name = self.find_tunnel_name() - port = self.find_tunnel_port() - self.tunnels[name] = port - return simplejson.dumps( - dict(through_port=port, user=self.user, host='%s.%s' % (name, superhost), banner=BANNER)) - - def render(self, request): - host = request.getHeader('host') - name, superhost = host.split('.', 1) - if host.startswith('open.'): - request.setHeader('Content-Type', 'application/json') - return self.register_tunnel(superhost, request.args.get('key', [None])[0]) - else: - if not name in self.tunnels: return "Not found" - - request.content.seek(0, 0) - clientFactory = self.proxyClientFactoryClass( - request.method, request.uri, request.clientproto, - request.getAllHeaders(), request.content.read(), request) - self.reactor.connectTCP(self.host, self.tunnels[name], clientFactory) - return server.NOT_DONE_YET - -#if 'location' in request.responseHeaders and host in request.responseHeaders['location']: -# # Strip out the port they think they need -# p = re.compile(r'%s\:\d+' % host) -# location = p.sub(host, request.responseHeaders['location']) -# request.responseHeaders['location'] = location - - -log.startLogging(sys.stdout) -reactor.listenTCP(80, server.Site(LocalTunnelReverseProxy(SSH_USER))) -reactor.run()