Skip to content

Instantly share code, notes, and snippets.

@pcdinh
Forked from xiocode/pinhole.py
Created August 2, 2012 07:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pcdinh/3234781 to your computer and use it in GitHub Desktop.
Save pcdinh/3234781 to your computer and use it in GitHub Desktop.
A simple port multiplexer
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# author: mayli <mayli.he@gmail.com>
#
# Modified from Pinhole, and the original code is found here:
# {{{ http://code.activestate.com/recipes/114642/ (r1)
#
"""
usage: pinhole
This program forwards a local port to different ports by guessing the protocol,
also can be called port-multiplex. It's useful when you behind a NAT and only
have one port availabe.
Changing the guessing part can achieve more services multiplex, but you have
to find some "pattern" between these services' protocol.
Configure HOSTS to match your server.
Test your configuration by, and see it works:
POP3S:openssl s_client -connect 127.0.0.1:9955 -showcerts
SSH:ssh 192.168.0.101 -p 9955
NOTE:To listen on port below 1024 on linux needs ROOT privilage.
"""
import sys
from socket import *
from threading import Thread
import time
import select
# Configure HOSTS to match your server
HOSTS = {
'ExtIP':('192.168.0.101',9955),
'POP3S':('pop.gmail.com',995), # Might be 127.0.0.1:995 in your case
'SSH':('127.0.0.1',22),
}
LOGGING = 1
CLIENT_TIMEOUT = 1
def log( s ):
if LOGGING:
print '%s:%s' % ( time.ctime(), s )
sys.stdout.flush()
class PipeThread( Thread ):
pipes = []
def __init__( self, source, sink ):
Thread.__init__( self )
self.source = source
self.sink = sink
log( 'Creating new pipe thread %s ( %s -> %s )' % \
( self, source.getpeername(), sink.getpeername() ))
PipeThread.pipes.append( self )
log( '%s pipes active' % len( PipeThread.pipes ))
def run( self ):
while 1:
try:
data = self.source.recv( 1024 )
if not data: break
self.sink.send( data )
except:
break
log( '%s terminating' % self )
PipeThread.pipes.remove( self )
log( '%s pipes active' % len( PipeThread.pipes ))
class Pinhole( Thread ):
def __init__( self, port ):
Thread.__init__( self )
self.port=port
self.sock = socket( AF_INET, SOCK_STREAM )
self.sock.bind(HOSTS['ExtIP'])
self.sock.listen(5)
def run( self ):
while 1:
newsock, address = self.sock.accept()
log( 'Creating new session for %s %s ' % address )
fwd = socket( AF_INET, SOCK_STREAM )
# Guess which protocol is used.
# Normally the ssh client will keep slient until server say 'SSH',
# but pop3s needs client to send header to ssl shake.
# So, we wait 1 s
ready=select.select([newsock],[],[],CLIENT_TIMEOUT)
if ready[0]:
print 'POP3 ready'
fwd.connect(HOSTS['POP3S'])
else:
print 'SSH ready'
# The ssh case
fwd.connect(HOSTS['SSH'])
log( 'Redirecting: localhost:%s -> %s' % ( self.port, repr(fwd.getpeername()) ))
PipeThread( newsock, fwd ).start()
PipeThread( fwd, newsock ).start()
if __name__ == '__main__':
print 'Starting Pinhole'
#import sys
#sys.stdout = open( 'pinhole.log', 'w' )
Pinhole( HOSTS['ExtIP'][1] ).start()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment