# Creating and working with a telnet server

## Simple Configuration

To start things off, we're going to create a simple server that just gives you remote access to a Python interpreter. We will use a telnet client to access this server.

Run `mktap telnet -p 4040 -u admin -w admin` at your shell prompt. If you list the contents of your current directory, you'll notice a new file -- `telnet.tap`. After you do this, run `twistd -f telnet.tap`. Since the Application has a telnet server that you specified to be on port 4040, it will start listening for connections on this port. Try connecting with your favorite telnet utility to 127.0.0.1 port 4040.

    $ telnet localhost 4040
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.

    twisted.manhole.telnet.ShellFactory
    Twisted 1.0.4
    username: admin
    password: admin
    >>>

Now, you should see a Python prompt -- `>>>`. You can type any valid Python code here. Let's try looking around.

    >>> dir()
    ['__builtins__']

Ok, not much. let's play a little more:

    >>> import __main__
    >>> dir(__main__)
    ['__builtins__', '__doc__', '__name__', 'os', 'run', 'string', 'sys']

    >>> from twisted.internet import app
    >>> app.theApplication
    <'telnet' app>
    >>> app.theApplication.tcpPorts
    [(4040, <twisted.manhole.telnet.ShellFactory instance at 0x8268edc>,5,'')]

From this session we learned that there is an application object stored in `twisted.internet.app.theApplication` that's a telnet app, and that it is listening on port 4040 with something called a `ShellFactory`. There are lots of other attributes in `theApplication`, which we're not going to worry about for now.

Alright, so now you've decided that you hate Twisted and want to shut it down. Or you just want to go to bed. Either way, I'll tell you what to do. First, disconnect from your telnet server. Then, back at your system's shell prompt, type `kill `cat twistd.pid`` (the quotes around `cat twistd.pid` are backticks, not single-quotes). If you list the contents of your current directory again, you'll notice that there will be a file named telnet-shutdown.tap. If you wanted to restart the server with exactly the same state as you left it, you could just run `twistd -f telnet-shutdown.tap`. This is why Twisted doesn't need any sort of configuration files -- all the configuration data is stored right in the objects!

Now that you've learned how to create a telnet server with 'mktap telnet', we'll delve a little deeper and learn how one is created behind the scenes. Start up a python interpreter and make sure that the 'twisted' directory is in your module search path.

    Python 2.2.2 (#1, Mar 21 2003, 23:01:54)
    [GCC 3.2.3 20030316 (Debian prerelease)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import sys
    >>> sys.path.append('/twisted/Twisted')

I installed Twisted in /twisted, so the place where my 'twisted' package directory is at is /twisted/Twisted/twisted (confusing, I know). For Python to find the 'twisted' package, it must have the directory containing the package in sys.path -- which is why I added /twisted/Twisted.

    >>> from twisted.internet import app
    >>> from twisted.manhole import telnet
    >>> application = app.Application('telnet')
    >>> ts = telnet.ShellFactory()
    >>> application.listenTCP(4040, ts)

The above is basically what `mktap telnet` does. First we create a new Twisted Application, we create a new telnet Shell Factory, and we tell the application to listen on TCP port 4040 with the ShellFactory we've created.

Now let's start the application. This causes all ports on the application to start listening for incoming connections. This step is basically what the 'twistd' utility does.

    >>> application.run()
    twisted.protocols.telnet.ShellFactory starting on 4040

You now have a functioning telnet server! You can connect with your telnet program and work with it just the same as you did before. The username and password both default to admin, but you can change those by modifying the attributes of the `ShellFactory` object you created earlier. When you're done using the telnet server, you can switch back to your python console and hit ctrl-C. The following should appear:

    Starting Shutdown Sequence.
    Stopping main loop.
    Main loop terminated.
    Saving telnet application to telnet-shutdown.tap...
    Saved.
    >>>

Your server was pickled up again and saved to the telnet-shutdown.tap file, just like when you did `kill `cat twistd.pid``.

## More Complicated Configuration

Let's suppose that we have the following application:

manhole1.py

This will give us a basic quote-of-the-day server: running `telnet localhost 8123` will give us a quote. However, once this is running, it would be nice to poke around inside it. We can add the manhole-shell by adding a few lines to create a new server (a Factory) listening on a different point:

manhole2.py

With this in place, you can telnet to port 8007, give the username boss and password sekrit, and you'll end up with a shell that behaves very much like the Python interpreter that you get by running `python` all by itself, with lines you type prefixed with `>>>`.

    % telnet localhost 8007
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.

    twisted.manhole.telnet.ShellFactory
    Twisted 0.99.2
    username: boss
    password: *****
    >>>

Note that the original Quote-Of-The-Day server is still running on port 8123 by using `nc localhost 8123` (or `telnet localhost 8123` if you don't have netcat installed).

    % nc localhost 8123
    An apple a day keeps the doctor away.

The initial namespace of the manhole interpreter is defined by a dictionary stored in the '`namespace`' attribute of the ShellFactory. For convenience, you can put references to any objects you like in that dict (`f.namespace['foo'] = 12`), and then retrieve them by name from the telnet session.

    >>> foo
    12

Of course we can change that namespace by evaluating expressions in the interpreter. To be a useful debugging tool, however, we want to get access to our servers (the protocol `Factory` instances and everything hanging off of them). We start by gaining access to the main `Application` instance through a global variable stored in the `app` module (assuming we ran using an Application and not the reactor directly):

    >>> import twisted.internet.app
    >>> a = twisted.internet.app.theApplication
    >>> a
    <'demo' app>

This object holds a number of things of interest: the list of Services (subclasses of ApplicationService that have been added to the application, most notably Perspective Broker services), and the list of ports on which protocol Factories are listening. The ports are kept in a number of lists, and the Factory object itself is available inside those lists (word wrapped for clarity):

    >>> a.tcpPorts
    [(8123, <twisted.internet.protocol.Factory instance at 0x8249b8c>, 5, ''),
    (8007, <twisted.manhole.telnet.ShellFactory instance at 0x824aefc>, 5, '')
    ]
    >>> f = a.tcpPorts[0][1]
    >>> f
    <twisted.internet.protocol.Factory instance at 0x8249b8c>

Now that we have access to that QOTD Factory, what can we do? We can modify any attribute of the object, or call functions on it. Remember that the Factory stores a reference to a subclass of Protocol, and it uses that reference to create new Protocol instances for each new connection. We can change that reference to make the Factory create something else:

    >>> f.protocol
    <class twisted.protocols.wire.QOTD at 0x824a66c>
    >>> from twisted.protocols.wire import Daytime
    >>> f.protocol = Daytime

Congratulations, you've just changed the Factory to use the `Daytime` protocol instead of the `QOTD` protocol. You have just transformed the QOTD server into a Daytime server. Connect to port 8123 now and see the difference: you get a timestamp instead of a quote:

    % nc localhost 8123
    Sat Sep 28 09:11:37 2002

From here, you can do anything you want to your application. It is a good idea to check the source for the `Application` and `Service` classes to see what else you can extract from them.

Note: to terminate your session, you'll need to exit the telnet or netcat program (the usual control-D that works in the Python interpreter won't work here). Try control-] for telnet. Also note that any exceptions caused by your manhole session will be displayed both in the telnet session and in the stderr on the application side.