mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			546 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			546 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#! /usr/bin/env python
 | 
						||
"""An RFC 2821 smtp proxy.
 | 
						||
 | 
						||
Usage: %(program)s [options] [localhost:localport [remotehost:remoteport]]
 | 
						||
 | 
						||
Options:
 | 
						||
 | 
						||
    --nosetuid
 | 
						||
    -n
 | 
						||
        This program generally tries to setuid `nobody', unless this flag is
 | 
						||
        set.  The setuid call will fail if this program is not run as root (in
 | 
						||
        which case, use this flag).
 | 
						||
 | 
						||
    --version
 | 
						||
    -V
 | 
						||
        Print the version number and exit.
 | 
						||
 | 
						||
    --class classname
 | 
						||
    -c classname
 | 
						||
        Use `classname' as the concrete SMTP proxy class.  Uses `SMTPProxy' by
 | 
						||
        default.
 | 
						||
 | 
						||
    --debug
 | 
						||
    -d
 | 
						||
        Turn on debugging prints.
 | 
						||
 | 
						||
    --help
 | 
						||
    -h
 | 
						||
        Print this message and exit.
 | 
						||
 | 
						||
Version: %(__version__)s
 | 
						||
 | 
						||
If localhost is not given then `localhost' is used, and if localport is not
 | 
						||
given then 8025 is used.  If remotehost is not given then `localhost' is used,
 | 
						||
and if remoteport is not given, then 25 is used.
 | 
						||
"""
 | 
						||
 | 
						||
 | 
						||
# Overview:
 | 
						||
#
 | 
						||
# This file implements the minimal SMTP protocol as defined in RFC 821.  It
 | 
						||
# has a hierarchy of classes which implement the backend functionality for the
 | 
						||
# smtpd.  A number of classes are provided:
 | 
						||
#
 | 
						||
#   SMTPServer - the base class for the backend.  Raises NotImplementedError
 | 
						||
#   if you try to use it.
 | 
						||
#
 | 
						||
#   DebuggingServer - simply prints each message it receives on stdout.
 | 
						||
#
 | 
						||
#   PureProxy - Proxies all messages to a real smtpd which does final
 | 
						||
#   delivery.  One known problem with this class is that it doesn't handle
 | 
						||
#   SMTP errors from the backend server at all.  This should be fixed
 | 
						||
#   (contributions are welcome!).
 | 
						||
#
 | 
						||
#   MailmanProxy - An experimental hack to work with GNU Mailman
 | 
						||
#   <www.list.org>.  Using this server as your real incoming smtpd, your
 | 
						||
#   mailhost will automatically recognize and accept mail destined to Mailman
 | 
						||
#   lists when those lists are created.  Every message not destined for a list
 | 
						||
#   gets forwarded to a real backend smtpd, as with PureProxy.  Again, errors
 | 
						||
#   are not handled correctly yet.
 | 
						||
#
 | 
						||
# Please note that this script requires Python 2.0
 | 
						||
#
 | 
						||
# Author: Barry Warsaw <barry@digicool.com>
 | 
						||
#
 | 
						||
# TODO:
 | 
						||
#
 | 
						||
# - support mailbox delivery
 | 
						||
# - alias files
 | 
						||
# - ESMTP
 | 
						||
# - handle error codes from the backend smtpd
 | 
						||
 | 
						||
import sys
 | 
						||
import os
 | 
						||
import errno
 | 
						||
import getopt
 | 
						||
import time
 | 
						||
import socket
 | 
						||
import asyncore
 | 
						||
import asynchat
 | 
						||
 | 
						||
__all__ = ["SMTPServer","DebuggingServer","PureProxy","MailmanProxy"]
 | 
						||
 | 
						||
program = sys.argv[0]
 | 
						||
__version__ = 'Python SMTP proxy version 0.2'
 | 
						||
 | 
						||
 | 
						||
class Devnull:
 | 
						||
    def write(self, msg): pass
 | 
						||
    def flush(self): pass
 | 
						||
 | 
						||
 | 
						||
DEBUGSTREAM = Devnull()
 | 
						||
NEWLINE = '\n'
 | 
						||
EMPTYSTRING = ''
 | 
						||
COMMASPACE = ', '
 | 
						||
 | 
						||
 | 
						||
 | 
						||
def usage(code, msg=''):
 | 
						||
    print >> sys.stderr, __doc__ % globals()
 | 
						||
    if msg:
 | 
						||
        print >> sys.stderr, msg
 | 
						||
    sys.exit(code)
 | 
						||
 | 
						||
 | 
						||
 | 
						||
class SMTPChannel(asynchat.async_chat):
 | 
						||
    COMMAND = 0
 | 
						||
    DATA = 1
 | 
						||
 | 
						||
    def __init__(self, server, conn, addr):
 | 
						||
        asynchat.async_chat.__init__(self, conn)
 | 
						||
        self.__server = server
 | 
						||
        self.__conn = conn
 | 
						||
        self.__addr = addr
 | 
						||
        self.__line = []
 | 
						||
        self.__state = self.COMMAND
 | 
						||
        self.__greeting = 0
 | 
						||
        self.__mailfrom = None
 | 
						||
        self.__rcpttos = []
 | 
						||
        self.__data = ''
 | 
						||
        self.__fqdn = socket.getfqdn()
 | 
						||
        self.__peer = conn.getpeername()
 | 
						||
        print >> DEBUGSTREAM, 'Peer:', repr(self.__peer)
 | 
						||
        self.push('220 %s %s' % (self.__fqdn, __version__))
 | 
						||
        self.set_terminator('\r\n')
 | 
						||
 | 
						||
    # Overrides base class for convenience
 | 
						||
    def push(self, msg):
 | 
						||
        asynchat.async_chat.push(self, msg + '\r\n')
 | 
						||
 | 
						||
    # Implementation of base class abstract method
 | 
						||
    def collect_incoming_data(self, data):
 | 
						||
        self.__line.append(data)
 | 
						||
 | 
						||
    # Implementation of base class abstract method
 | 
						||
    def found_terminator(self):
 | 
						||
        line = EMPTYSTRING.join(self.__line)
 | 
						||
        print >> DEBUGSTREAM, 'Data:', repr(line)
 | 
						||
        self.__line = []
 | 
						||
        if self.__state == self.COMMAND:
 | 
						||
            if not line:
 | 
						||
                self.push('500 Error: bad syntax')
 | 
						||
                return
 | 
						||
            method = None
 | 
						||
            i = line.find(' ')
 | 
						||
            if i < 0:
 | 
						||
                command = line.upper()
 | 
						||
                arg = None
 | 
						||
            else:
 | 
						||
                command = line[:i].upper()
 | 
						||
                arg = line[i+1:].strip()
 | 
						||
            method = getattr(self, 'smtp_' + command, None)
 | 
						||
            if not method:
 | 
						||
                self.push('502 Error: command "%s" not implemented' % command)
 | 
						||
                return
 | 
						||
            method(arg)
 | 
						||
            return
 | 
						||
        else:
 | 
						||
            if self.__state != self.DATA:
 | 
						||
                self.push('451 Internal confusion')
 | 
						||
                return
 | 
						||
            # Remove extraneous carriage returns and de-transparency according
 | 
						||
            # to RFC 821, Section 4.5.2.
 | 
						||
            data = []
 | 
						||
            for text in line.split('\r\n'):
 | 
						||
                if text and text[0] == '.':
 | 
						||
                    data.append(text[1:])
 | 
						||
                else:
 | 
						||
                    data.append(text)
 | 
						||
            self.__data = NEWLINE.join(data)
 | 
						||
            status = self.__server.process_message(self.__peer,
 | 
						||
                                                   self.__mailfrom,
 | 
						||
                                                   self.__rcpttos,
 | 
						||
                                                   self.__data)
 | 
						||
            self.__rcpttos = []
 | 
						||
            self.__mailfrom = None
 | 
						||
            self.__state = self.COMMAND
 | 
						||
            self.set_terminator('\r\n')
 | 
						||
            if not status:
 | 
						||
                self.push('250 Ok')
 | 
						||
            else:
 | 
						||
                self.push(status)
 | 
						||
 | 
						||
    # SMTP and ESMTP commands
 | 
						||
    def smtp_HELO(self, arg):
 | 
						||
        if not arg:
 | 
						||
            self.push('501 Syntax: HELO hostname')
 | 
						||
            return
 | 
						||
        if self.__greeting:
 | 
						||
            self.push('503 Duplicate HELO/EHLO')
 | 
						||
        else:
 | 
						||
            self.__greeting = arg
 | 
						||
            self.push('250 %s' % self.__fqdn)
 | 
						||
 | 
						||
    def smtp_NOOP(self, arg):
 | 
						||
        if arg:
 | 
						||
            self.push('501 Syntax: NOOP')
 | 
						||
        else:
 | 
						||
            self.push('250 Ok')
 | 
						||
 | 
						||
    def smtp_QUIT(self, arg):
 | 
						||
        # args is ignored
 | 
						||
        self.push('221 Bye')
 | 
						||
        self.close_when_done()
 | 
						||
 | 
						||
    # factored
 | 
						||
    def __getaddr(self, keyword, arg):
 | 
						||
        address = None
 | 
						||
        keylen = len(keyword)
 | 
						||
        if arg[:keylen].upper() == keyword:
 | 
						||
            address = arg[keylen:].strip()
 | 
						||
            if not address:
 | 
						||
                pass
 | 
						||
            elif address[0] == '<' and address[-1] == '>' and address != '<>':
 | 
						||
                # Addresses can be in the form <person@dom.com> but watch out
 | 
						||
                # for null address, e.g. <>
 | 
						||
                address = address[1:-1]
 | 
						||
        return address
 | 
						||
 | 
						||
    def smtp_MAIL(self, arg):
 | 
						||
        print >> DEBUGSTREAM, '===> MAIL', arg
 | 
						||
        address = self.__getaddr('FROM:', arg)
 | 
						||
        if not address:
 | 
						||
            self.push('501 Syntax: MAIL FROM:<address>')
 | 
						||
            return
 | 
						||
        if self.__mailfrom:
 | 
						||
            self.push('503 Error: nested MAIL command')
 | 
						||
            return
 | 
						||
        self.__mailfrom = address
 | 
						||
        print >> DEBUGSTREAM, 'sender:', self.__mailfrom
 | 
						||
        self.push('250 Ok')
 | 
						||
 | 
						||
    def smtp_RCPT(self, arg):
 | 
						||
        print >> DEBUGSTREAM, '===> RCPT', arg
 | 
						||
        if not self.__mailfrom:
 | 
						||
            self.push('503 Error: need MAIL command')
 | 
						||
            return
 | 
						||
        address = self.__getaddr('TO:', arg)
 | 
						||
        if not address:
 | 
						||
            self.push('501 Syntax: RCPT TO: <address>')
 | 
						||
            return
 | 
						||
        if address.lower().startswith('stimpy'):
 | 
						||
            self.push('503 You suck %s' % address)
 | 
						||
            return
 | 
						||
        self.__rcpttos.append(address)
 | 
						||
        print >> DEBUGSTREAM, 'recips:', self.__rcpttos
 | 
						||
        self.push('250 Ok')
 | 
						||
 | 
						||
    def smtp_RSET(self, arg):
 | 
						||
        if arg:
 | 
						||
            self.push('501 Syntax: RSET')
 | 
						||
            return
 | 
						||
        # Resets the sender, recipients, and data, but not the greeting
 | 
						||
        self.__mailfrom = None
 | 
						||
        self.__rcpttos = []
 | 
						||
        self.__data = ''
 | 
						||
        self.__state = self.COMMAND
 | 
						||
        self.push('250 Ok')
 | 
						||
 | 
						||
    def smtp_DATA(self, arg):
 | 
						||
        if not self.__rcpttos:
 | 
						||
            self.push('503 Error: need RCPT command')
 | 
						||
            return
 | 
						||
        if arg:
 | 
						||
            self.push('501 Syntax: DATA')
 | 
						||
            return
 | 
						||
        self.__state = self.DATA
 | 
						||
        self.set_terminator('\r\n.\r\n')
 | 
						||
        self.push('354 End data with <CR><LF>.<CR><LF>')
 | 
						||
 | 
						||
 | 
						||
 | 
						||
class SMTPServer(asyncore.dispatcher):
 | 
						||
    def __init__(self, localaddr, remoteaddr):
 | 
						||
        self._localaddr = localaddr
 | 
						||
        self._remoteaddr = remoteaddr
 | 
						||
        asyncore.dispatcher.__init__(self)
 | 
						||
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
						||
        # try to re-use a server port if possible
 | 
						||
        self.set_reuse_addr()
 | 
						||
        self.bind(localaddr)
 | 
						||
        self.listen(5)
 | 
						||
        print >> DEBUGSTREAM, \
 | 
						||
              '%s started at %s\n\tLocal addr: %s\n\tRemote addr:%s' % (
 | 
						||
            self.__class__.__name__, time.ctime(time.time()),
 | 
						||
            localaddr, remoteaddr)
 | 
						||
 | 
						||
    def handle_accept(self):
 | 
						||
        conn, addr = self.accept()
 | 
						||
        print >> DEBUGSTREAM, 'Incoming connection from %s' % repr(addr)
 | 
						||
        channel = SMTPChannel(self, conn, addr)
 | 
						||
 | 
						||
    # API for "doing something useful with the message"
 | 
						||
    def process_message(self, peer, mailfrom, rcpttos, data):
 | 
						||
        """Override this abstract method to handle messages from the client.
 | 
						||
 | 
						||
        peer is a tuple containing (ipaddr, port) of the client that made the
 | 
						||
        socket connection to our smtp port.
 | 
						||
 | 
						||
        mailfrom is the raw address the client claims the message is coming
 | 
						||
        from.
 | 
						||
 | 
						||
        rcpttos is a list of raw addresses the client wishes to deliver the
 | 
						||
        message to.
 | 
						||
 | 
						||
        data is a string containing the entire full text of the message,
 | 
						||
        headers (if supplied) and all.  It has been `de-transparencied'
 | 
						||
        according to RFC 821, Section 4.5.2.  In other words, a line
 | 
						||
        containing a `.' followed by other text has had the leading dot
 | 
						||
        removed.
 | 
						||
 | 
						||
        This function should return None, for a normal `250 Ok' response;
 | 
						||
        otherwise it returns the desired response string in RFC 821 format.
 | 
						||
 | 
						||
        """
 | 
						||
        raise NotImplementedError
 | 
						||
 | 
						||
 | 
						||
 | 
						||
class DebuggingServer(SMTPServer):
 | 
						||
    # Do something with the gathered message
 | 
						||
    def process_message(self, peer, mailfrom, rcpttos, data):
 | 
						||
        inheaders = 1
 | 
						||
        lines = data.split('\n')
 | 
						||
        print '---------- MESSAGE FOLLOWS ----------'
 | 
						||
        for line in lines:
 | 
						||
            # headers first
 | 
						||
            if inheaders and not line:
 | 
						||
                print 'X-Peer:', peer[0]
 | 
						||
                inheaders = 0
 | 
						||
            print line
 | 
						||
        print '------------ END MESSAGE ------------'
 | 
						||
 | 
						||
 | 
						||
 | 
						||
class PureProxy(SMTPServer):
 | 
						||
    def process_message(self, peer, mailfrom, rcpttos, data):
 | 
						||
        lines = data.split('\n')
 | 
						||
        # Look for the last header
 | 
						||
        i = 0
 | 
						||
        for line in lines:
 | 
						||
            if not line:
 | 
						||
                break
 | 
						||
            i += 1
 | 
						||
        lines.insert(i, 'X-Peer: %s' % peer[0])
 | 
						||
        data = NEWLINE.join(lines)
 | 
						||
        refused = self._deliver(mailfrom, rcpttos, data)
 | 
						||
        # TBD: what to do with refused addresses?
 | 
						||
        print >> DEBUGSTREAM, 'we got some refusals:', refused
 | 
						||
 | 
						||
    def _deliver(self, mailfrom, rcpttos, data):
 | 
						||
        import smtplib
 | 
						||
        refused = {}
 | 
						||
        try:
 | 
						||
            s = smtplib.SMTP()
 | 
						||
            s.connect(self._remoteaddr[0], self._remoteaddr[1])
 | 
						||
            try:
 | 
						||
                refused = s.sendmail(mailfrom, rcpttos, data)
 | 
						||
            finally:
 | 
						||
                s.quit()
 | 
						||
        except smtplib.SMTPRecipientsRefused, e:
 | 
						||
            print >> DEBUGSTREAM, 'got SMTPRecipientsRefused'
 | 
						||
            refused = e.recipients
 | 
						||
        except (socket.error, smtplib.SMTPException), e:
 | 
						||
            print >> DEBUGSTREAM, 'got', e.__class__
 | 
						||
            # All recipients were refused.  If the exception had an associated
 | 
						||
            # error code, use it.  Otherwise,fake it with a non-triggering
 | 
						||
            # exception code.
 | 
						||
            errcode = getattr(e, 'smtp_code', -1)
 | 
						||
            errmsg = getattr(e, 'smtp_error', 'ignore')
 | 
						||
            for r in rcpttos:
 | 
						||
                refused[r] = (errcode, errmsg)
 | 
						||
        return refused
 | 
						||
 | 
						||
 | 
						||
 | 
						||
class MailmanProxy(PureProxy):
 | 
						||
    def process_message(self, peer, mailfrom, rcpttos, data):
 | 
						||
        from cStringIO import StringIO
 | 
						||
        from Mailman import Utils
 | 
						||
        from Mailman import Message
 | 
						||
        from Mailman import MailList
 | 
						||
        # If the message is to a Mailman mailing list, then we'll invoke the
 | 
						||
        # Mailman script directly, without going through the real smtpd.
 | 
						||
        # Otherwise we'll forward it to the local proxy for disposition.
 | 
						||
        listnames = []
 | 
						||
        for rcpt in rcpttos:
 | 
						||
            local = rcpt.lower().split('@')[0]
 | 
						||
            # We allow the following variations on the theme
 | 
						||
            #   listname
 | 
						||
            #   listname-admin
 | 
						||
            #   listname-owner
 | 
						||
            #   listname-request
 | 
						||
            #   listname-join
 | 
						||
            #   listname-leave
 | 
						||
            parts = local.split('-')
 | 
						||
            if len(parts) > 2:
 | 
						||
                continue
 | 
						||
            listname = parts[0]
 | 
						||
            if len(parts) == 2:
 | 
						||
                command = parts[1]
 | 
						||
            else:
 | 
						||
                command = ''
 | 
						||
            if not Utils.list_exists(listname) or command not in (
 | 
						||
                    '', 'admin', 'owner', 'request', 'join', 'leave'):
 | 
						||
                continue
 | 
						||
            listnames.append((rcpt, listname, command))
 | 
						||
        # Remove all list recipients from rcpttos and forward what we're not
 | 
						||
        # going to take care of ourselves.  Linear removal should be fine
 | 
						||
        # since we don't expect a large number of recipients.
 | 
						||
        for rcpt, listname, command in listnames:
 | 
						||
            rcpttos.remove(rcpt)
 | 
						||
        # If there's any non-list destined recipients left,
 | 
						||
        print >> DEBUGSTREAM, 'forwarding recips:', ' '.join(rcpttos)
 | 
						||
        if rcpttos:
 | 
						||
            refused = self._deliver(mailfrom, rcpttos, data)
 | 
						||
            # TBD: what to do with refused addresses?
 | 
						||
            print >> DEBUGSTREAM, 'we got refusals:', refused
 | 
						||
        # Now deliver directly to the list commands
 | 
						||
        mlists = {}
 | 
						||
        s = StringIO(data)
 | 
						||
        msg = Message.Message(s)
 | 
						||
        # These headers are required for the proper execution of Mailman.  All
 | 
						||
        # MTAs in existance seem to add these if the original message doesn't
 | 
						||
        # have them.
 | 
						||
        if not msg.getheader('from'):
 | 
						||
            msg['From'] = mailfrom
 | 
						||
        if not msg.getheader('date'):
 | 
						||
            msg['Date'] = time.ctime(time.time())
 | 
						||
        for rcpt, listname, command in listnames:
 | 
						||
            print >> DEBUGSTREAM, 'sending message to', rcpt
 | 
						||
            mlist = mlists.get(listname)
 | 
						||
            if not mlist:
 | 
						||
                mlist = MailList.MailList(listname, lock=0)
 | 
						||
                mlists[listname] = mlist
 | 
						||
            # dispatch on the type of command
 | 
						||
            if command == '':
 | 
						||
                # post
 | 
						||
                msg.Enqueue(mlist, tolist=1)
 | 
						||
            elif command == 'admin':
 | 
						||
                msg.Enqueue(mlist, toadmin=1)
 | 
						||
            elif command == 'owner':
 | 
						||
                msg.Enqueue(mlist, toowner=1)
 | 
						||
            elif command == 'request':
 | 
						||
                msg.Enqueue(mlist, torequest=1)
 | 
						||
            elif command in ('join', 'leave'):
 | 
						||
                # TBD: this is a hack!
 | 
						||
                if command == 'join':
 | 
						||
                    msg['Subject'] = 'subscribe'
 | 
						||
                else:
 | 
						||
                    msg['Subject'] = 'unsubscribe'
 | 
						||
                msg.Enqueue(mlist, torequest=1)
 | 
						||
 | 
						||
 | 
						||
 | 
						||
class Options:
 | 
						||
    setuid = 1
 | 
						||
    classname = 'PureProxy'
 | 
						||
 | 
						||
 | 
						||
 | 
						||
def parseargs():
 | 
						||
    global DEBUGSTREAM
 | 
						||
    try:
 | 
						||
        opts, args = getopt.getopt(
 | 
						||
            sys.argv[1:], 'nVhc:d',
 | 
						||
            ['class=', 'nosetuid', 'version', 'help', 'debug'])
 | 
						||
    except getopt.error, e:
 | 
						||
        usage(1, e)
 | 
						||
 | 
						||
    options = Options()
 | 
						||
    for opt, arg in opts:
 | 
						||
        if opt in ('-h', '--help'):
 | 
						||
            usage(0)
 | 
						||
        elif opt in ('-V', '--version'):
 | 
						||
            print >> sys.stderr, __version__
 | 
						||
            sys.exit(0)
 | 
						||
        elif opt in ('-n', '--nosetuid'):
 | 
						||
            options.setuid = 0
 | 
						||
        elif opt in ('-c', '--class'):
 | 
						||
            options.classname = arg
 | 
						||
        elif opt in ('-d', '--debug'):
 | 
						||
            DEBUGSTREAM = sys.stderr
 | 
						||
 | 
						||
    # parse the rest of the arguments
 | 
						||
    if len(args) < 1:
 | 
						||
        localspec = 'localhost:8025'
 | 
						||
        remotespec = 'localhost:25'
 | 
						||
    elif len(args) < 2:
 | 
						||
        localspec = args[0]
 | 
						||
        remotespec = 'localhost:25'
 | 
						||
    elif len(args) < 3:
 | 
						||
        localspec = args[0]
 | 
						||
        remotespec = args[1]
 | 
						||
    else:
 | 
						||
        usage(1, 'Invalid arguments: %s' % COMMASPACE.join(args))
 | 
						||
 | 
						||
    # split into host/port pairs
 | 
						||
    i = localspec.find(':')
 | 
						||
    if i < 0:
 | 
						||
        usage(1, 'Bad local spec: %s' % localspec)
 | 
						||
    options.localhost = localspec[:i]
 | 
						||
    try:
 | 
						||
        options.localport = int(localspec[i+1:])
 | 
						||
    except ValueError:
 | 
						||
        usage(1, 'Bad local port: %s' % localspec)
 | 
						||
    i = remotespec.find(':')
 | 
						||
    if i < 0:
 | 
						||
        usage(1, 'Bad remote spec: %s' % remotespec)
 | 
						||
    options.remotehost = remotespec[:i]
 | 
						||
    try:
 | 
						||
        options.remoteport = int(remotespec[i+1:])
 | 
						||
    except ValueError:
 | 
						||
        usage(1, 'Bad remote port: %s' % remotespec)
 | 
						||
    return options
 | 
						||
 | 
						||
 | 
						||
 | 
						||
if __name__ == '__main__':
 | 
						||
    options = parseargs()
 | 
						||
    # Become nobody
 | 
						||
    if options.setuid:
 | 
						||
        try:
 | 
						||
            import pwd
 | 
						||
        except ImportError:
 | 
						||
            print >> sys.stderr, \
 | 
						||
                  'Cannot import module "pwd"; try running with -n option.'
 | 
						||
            sys.exit(1)
 | 
						||
        nobody = pwd.getpwnam('nobody')[2]
 | 
						||
        try:
 | 
						||
            os.setuid(nobody)
 | 
						||
        except OSError, e:
 | 
						||
            if e.errno != errno.EPERM: raise
 | 
						||
            print >> sys.stderr, \
 | 
						||
                  'Cannot setuid "nobody"; try running with -n option.'
 | 
						||
            sys.exit(1)
 | 
						||
    import __main__
 | 
						||
    class_ = getattr(__main__, options.classname)
 | 
						||
    proxy = class_((options.localhost, options.localport),
 | 
						||
                   (options.remotehost, options.remoteport))
 | 
						||
    try:
 | 
						||
        asyncore.loop()
 | 
						||
    except KeyboardInterrupt:
 | 
						||
        pass
 |