############################################################################
##
## Copyright (c) 2000-2015 BalaBit IT Ltd, Budapest, Hungary
## Copyright (c) 2015-2018 BalaSys IT Ltd, Budapest, Hungary
##
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along
## with this program; if not, write to the Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
##
############################################################################
"""<module maturity="stable">
  <summary>
    Proxy for the Telnet protocol.
  </summary>
  <description>
    <para>
      The Telnet module defines the classes constituting the proxy for the TELNET protocol.
    </para>
    <section>
      <title>The Telnet protocol</title>
      <para>
        The Telnet protocol was designed to remotely login to computers via the
        network. Although its main purpose is to access a remote standard terminal, it can be used for many other functions as well.
      </para>
      <para>
        The protocol follows a simple scenario. The client
        opens a TCP connection to the server at the port 23. The server
        authenticates the client and opens a terminal. At the end of the session
        the server closes the connection. All data is sent in plain text format whithout any encryption.
      </para>
      <section>
        <title>The network virtual terminal</title>
        <para>
          The communication is based on the network virtual terminal (NVT).
          Its goal is to map a character terminal so neither the "server" nor
          "user" hosts need to keep information about the characteristics
          of each other's terminals and terminal handling conventions.
          NVT uses 7 bit code ASCII characters as the display device. An end of line
          is transmitted as a CRLF (carriage return followed by a line feed).
          NVT ASCII is used by many other protocols as well.
        </para>
        <para>
          NVT defines three mandatory control codes which must be understood by
          the participants: NULL, CR (Carriage Return), which moves the printer to the left
          margin of the current line and LF (Line Feed), which moves the printer to the next
          line keeping the current horizontal position.
        </para>
        <para>
          NVT also contains some optional commands which are useful. These are the following:
        </para>
          <itemizedlist>
           <listitem>
            <para>
            <emphasis>BELL</emphasis> is an audible or visual sign.
            </para>
          </listitem>
          <listitem>
            <para>
            <emphasis>BS</emphasis> (Back Space) moves the printer back one position and deletes a character.
            </para>
          </listitem>
          <listitem>
            <para>
            <emphasis>HT</emphasis> (Horizontal Tab) moves the printer to the next horizontal tabular stop.
            </para>
          </listitem>
          <listitem>
          <para>
          <emphasis>VT</emphasis> Vertical Tab moves the printer to the next vertical tabular stop.
          </para>
          </listitem>
          <listitem>
          <para>
          <emphasis>FF</emphasis> (Form Feed) moves the printer to the top of the next page.
        </para>
        </listitem>
        </itemizedlist>
      </section>
      <section>
        <title>Protocol elements</title>
        <para>
          The protocol uses several commands that control the method and various details of the
          interaction between the client and the server. These commands can be either mandatory commands or extensions.
          During the session initialization the client and the server negotiates the connection parameters with these
          commands. Sub-negotiation is a process during the protocol which is for exchanging extra parameters of
          a command (e.g.: sending the window size). The commands of the protocol are:
          <table>
            <title>Telnet protocol commands</title>
            <tgroup cols="2">
              <thead>
                <row>
                  <entry>Request/Response</entry>
                  <entry>Description</entry>
                </row>
              </thead>
              <tbody>
                <row>
                  <entry>SE</entry>
                  <entry>End of sub-negotiation parameters.</entry>
                </row>
                <row>
                  <entry>NOP</entry>
                  <entry>No operation.</entry>
                </row>
                <row>
                  <entry>DM</entry>
                  <entry>Data mark - Indicates the position of Sync event within the data stream.</entry>
                </row>
                <row>
                  <entry>BRK</entry>
                  <entry>Break - Indicates that a break or attention key was hit.</entry>
                </row>
                <row>
                  <entry>IP</entry>
                  <entry>Suspend, interrupt or abort the process.</entry>
                </row>
                <row>
                  <entry>AO</entry>
                  <entry>Abort output - Run a command without sending the output back to the client.</entry>
                </row>
                <row>
                  <entry>AYT</entry>
                  <entry>Are you there - Request a visible evidence that the AYT command has been received.</entry>
                </row>
                <row>
                  <entry>EC</entry>
                  <entry>Erase character - Delete the character last received from the stream.</entry>
                </row>
                <row>
                  <entry>EL</entry>
                  <entry>Erase line - Erase a line without a CRLF.</entry>
                </row>
                <row>
                  <entry>GA</entry>
                  <entry>Go Ahead - Instruct the other machine to start the transmission.</entry>
                </row>
                <row>
                  <entry>SB</entry>
                  <entry>Sub-negotiation starts here.</entry>
                </row>
                <row>
                  <entry>WILL</entry>
                  <entry>Will (option code) - Indicates the desire to begin performing the indicated option, or confirms that it is being performed.</entry>
                </row>
                <row>
                  <entry>WONT</entry>
                  <entry>Will not (option code) - Indicates the refusal to perform,               or continue performing, the indicated option.</entry>
                </row>
                <row>
                  <entry>DO</entry>
                  <entry>Do (option code) - Indicates the request that the                        other party perform, or confirmation that                                 the other party is expected to perform, the                                 indicated option.</entry>
                </row>
                <row>
                  <entry>DONT</entry>
                  <entry>Do not (option code) - Indicates the request that the other party stop performing the indicated option, or confirmation that its performing               is no longer expected. </entry>
                </row>
                <row>
                  <entry>IAC</entry>
                  <entry>Interpret as command.</entry>
                </row>
              </tbody>
            </tgroup>
          </table>
        </para>

      </section>
    </section>
    <section>
      <title>Proxy behavior</title>
      <para>
        TelnetProxy is a module built for parsing TELNET protocol commands and the negotiation process.
        It reads and parses COMMANDs on the client side, and sends them to the server if the local security policy permits. Arriving RESPONSEs are parsed as well and sent to the client if the local security policy permits.
        It is possible to manipulate options by using TELNET_OPT_POLICY. It is also
        possible to accept or deny certain options and suboptions.
      </para>
      <para>
      The Telnet shell itself cannot be controlled, thus the commands issued by the users cannot be monitored or modified.
      </para>
      <section>
        <title>Default policy</title>
        <para>
          The low level abstract Telnet proxy denies every option and suboption negotiation
          sequences by default. The different options can be enabled either manually in a derived proxy class, or the predefined TelnetProxy class can be used.
        </para>
      </section>
      <section xml:id="telnet_policies">
        <title>Configuring policies for the TELNET protocol</title>
        <para>
          The Telnet proxy can enable/disable the use of the options and their suboptions within the session. Changing the default policy
          can be done using the <parameter>option</parameter> multi-dimensional hash,
          indexed by the option and the suboption (optional). If the suboption is specified, the lookup precedence described in <xref linkend="proxy_response_codes"/> is used.
          The possible action codes are listed in the table below.</para>
          <inline type="actiontuple" target="action.telnet.opt"/>

        <example>
          <title>Example for disabling the Telnet X Display Location option</title>
          <synopsis>class MyTelnetProxy(TelnetProxy):
        def config(self):
            TelnetProxy.config(self)
            self.option[TELNET_X_DISPLAY_LOCATION] = (TELNET_OPT_REJECT)</synopsis>
        </example>

        <para>
        Constants have been defined for the easier use of TELNET options and suboptions. These are listed in <xref linkend="telnet_options"/>.
        </para>

      <section>
        <title>Policy callback functions</title>
        <para>
          Policy callback functions can be used to make decisions based on the
          content of the suboption negotiation sequence. For example, the  suboption negotiation sequences of the Telnet
          Environment option transfer environment
          variables. The low level proxy implementation parses these variables, and
          passes their name and value to the callback function one-by-one. These
          values can also be manipulated during transfer, by changing the <parameter>current_var_name</parameter> and
          <parameter>current_var_value</parameter> attributes of the proxy class.
        </para>
        <example>
          <title>Rewriting the DISPLAY environment variable</title>
          <synopsis>class MyRewritingTelnetProxy(TelnetProxy):
        def config(self):
            TelnetProxy.config()
            self.option[TELNET_ENVIRONMENT, TELNET_SB_IS] = (TELNET_OPTION_POLICY, self.rewriteVar)

        def rewriteVar(self, option, name, value):
            if name == "DISPLAY":
                    self.current_var_value = "rewritten_value:0"
            return TELNET_OPTION_ACCEPT</synopsis>
        </example>
      </section>
      <section xml:id="telnet_negotiation">
      <title>
      Option negotiation
      </title>
      <para>
      In the Telnet protocol, options and the actual commands are represented on one byte. In order to be able to use a command in a session, the option (and its suboptions if there are any) corresponding to the command has to be negotiated between the client and the server. Usually the command and the option is represented by the same value, e.g.: the <parameter>TELNET_STATUS</parameter> command and option are both represented by the value "5". However, this is not always the case. The <parameter>negotiation</parameter> hash is indexed by the code of the command, and contains the code of the option to be negotiated for the given command (or the <parameter>TELNET_NEG_NONE</parameter> when no negotation is needed). </para>
      <para>Currently the only command where the code of the command differs from the related option is <parameter>self.negotiation["239"] = int(TELNET_EOR)</parameter>.
      </para>
      </section>
     </section>
    </section>
    <section>
      <title>Related standards</title>
          <para>
              The Telnet protocol is described in RFC 854. The different options of the protocol are described in various other RFCs, listed in <xref linkend="telnet_options"/>.
            </para>
   </section>
  </description>
  <appendix xml:id="telnet_app_options">
  <title>TELNET appendix</title>
        <para>
        The constants defined for the easier use of TELNET options and suboptions are listed in the table below. Suboptions are listed directly under the option they refer to. All suboptions have the TELNET_SB prefix. The RFC describing the given option is also shown in the table.
        </para>
        <table frame="all" xml:id="telnet_options">
<title>TELNET options and suboptions</title>
<tgroup cols="3">
<thead><row><entry>Name</entry><entry>Constant value of option/suboption</entry><entry>Detailed in RFC #</entry></row></thead>
<tbody>

<row><entry>TELNET_BINARY</entry><entry>0</entry><entry>856</entry></row>

<row><entry>TELNET_ECHO</entry><entry>1</entry><entry>857</entry></row>

<row><entry>TELNET_SUPPRESS_GO_AHEAD</entry><entry>3</entry><entry>858</entry></row>

<row><entry>TELNET_STATUS</entry><entry>5</entry><entry>859 </entry></row>
<row><entry>TELNET_SB_STATUS_SB_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_STATUS_SB_SEND</entry><entry>1</entry></row>

<row><entry>TELNET_TIMING_MARK</entry><entry>6</entry><entry>860</entry></row>

<row><entry>TELNET_RCTE</entry><entry>7</entry><entry>726</entry></row>

<row><entry>TELNET_NAOCRD</entry><entry>10</entry><entry>652</entry></row>
<row><entry>TELNET_SB_NAOCRD_DR</entry><entry>0</entry></row>
<row><entry>TELNET_SB_NAOCRD_DS</entry><entry>1</entry></row>

<row><entry>TELNET_NAOHTS</entry><entry>11</entry><entry>653</entry></row>
<row><entry>TELNET_SB_NAOHTS_DR</entry><entry>0</entry></row>
<row><entry>TELNET_SB_NAOHTS_DS</entry><entry>1</entry></row>

<row><entry>TELNET_NAOHTD</entry><entry>12</entry><entry>654</entry></row>
<row><entry>TELNET_SB_NAOHTD_DR</entry><entry>0</entry></row>
<row><entry>TELNET_SB_NAOHTD_DS</entry><entry>1</entry></row>

<row><entry>TELNET_NAOFFD</entry><entry>13</entry><entry>655</entry></row>
<row><entry>TELNET_SB_NAOFFD_DR</entry><entry>0</entry></row>
<row><entry>TELNET_SB_NAOFFD_DS</entry><entry>1</entry></row>

<row><entry>TELNET_NAOVTS</entry><entry>14</entry><entry>656</entry></row>
<row><entry>TELNET_SB_NAOVTS_DR</entry><entry>0</entry></row>
<row><entry>TELNET_SB_NAOVTS_DS</entry><entry>1</entry></row>

<row><entry>TELNET_NAOVTD</entry><entry>15</entry><entry>657</entry></row>
<row><entry>TELNET_SB_NAOVTD_DR</entry><entry>0</entry></row>
<row><entry>TELNET_SB_NAOVTD_DS</entry><entry>1</entry></row>

<row><entry>TELNET_NAOLFD</entry><entry>16</entry><entry>658</entry></row>
<row><entry>TELNET_SB_NAOLFD_DR</entry><entry>0</entry></row>
<row><entry>TELNET_SB_NAOLFD_DS</entry><entry>1</entry></row>

<row><entry>TELNET_EXTEND_ASCII</entry><entry>17</entry><entry>698</entry></row>

<row><entry>TELNET_LOGOUT</entry><entry>18</entry><entry>727</entry></row>

<row><entry>TELNET_BM</entry><entry>19</entry><entry>735</entry></row>
<row><entry>TELNET_SB_BM_DEFINE</entry><entry>1</entry></row>
<row><entry>TELNET_SB_BM_ACCEPT</entry><entry>2</entry></row>
<row><entry>TELNET_SB_BM_REFUSE</entry><entry>3</entry></row>
<row><entry>TELNET_SB_BM_LITERAL</entry><entry>4</entry></row>
<row><entry>TELNET_SB_BM_CANCEL</entry><entry>5</entry></row>

<row><entry>TELNET_DET</entry><entry>20</entry><entry>1043, 732</entry></row>
<row><entry>TELNET_SB_DET_DEFINE</entry><entry>1</entry></row>
<row><entry>TELNET_SB_DET_ERASE</entry><entry>2</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT</entry><entry>3</entry></row>
<row><entry>TELNET_SB_DET_FORMAT</entry><entry>4</entry></row>
<row><entry>TELNET_SB_DET_MOVE_CURSOR</entry><entry>5</entry></row>
<row><entry>TELNET_SB_DET_SKIP_TO_LINE</entry><entry>6</entry></row>
<row><entry>TELNET_SB_DET_SKIP_TO_CHAR</entry><entry>7</entry></row>
<row><entry>TELNET_SB_DET_UP</entry><entry>8</entry></row>
<row><entry>TELNET_SB_DET_DOWN</entry><entry>9</entry></row>
<row><entry>TELNET_SB_DET_LEFT</entry><entry>10</entry></row>
<row><entry>TELNET_SB_DET_RIGHT</entry><entry>11</entry></row>
<row><entry>TELNET_SB_DET_HOME</entry><entry>12</entry></row>
<row><entry>TELNET_SB_DET_LINE_INSERT</entry><entry>13</entry></row>
<row><entry>TELNET_SB_DET_LINE_DELETE</entry><entry>14</entry></row>
<row><entry>TELNET_SB_DET_CHAR_INSERT</entry><entry>15</entry></row>
<row><entry>TELNET_SB_DET_CHAR_DELETE</entry><entry>16</entry></row>
<row><entry>TELNET_SB_DET_READ_CURSOR</entry><entry>17</entry></row>
<row><entry>TELNET_SB_DET_CURSOR_POSITION</entry><entry>18</entry></row>
<row><entry>TELNET_SB_DET_REVERSE_TAB</entry><entry>19</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_SCREEN</entry><entry>20</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_UNPROTECTED</entry><entry>21</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_LINE</entry><entry>22</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_FIELD</entry><entry>23</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_REST_SCREEN</entry><entry>24</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_REST_LINE</entry><entry>25</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_REST_FIELD</entry><entry>26</entry></row>
<row><entry>TELNET_SB_DET_TRANSMIT_MODIFIED</entry><entry>27</entry></row>
<row><entry>TELNET_SB_DET_DATA_TRANSMIT</entry><entry>28</entry></row>
<row><entry>TELNET_SB_DET_ERASE_SCREEN</entry><entry>29</entry></row>
<row><entry>TELNET_SB_DET_ERASE_LINE</entry><entry>30</entry></row>
<row><entry>TELNET_SB_DET_ERASE_FIELD</entry><entry>31</entry></row>
<row><entry>TELNET_SB_DET_ERASE_REST_SCREEN</entry><entry>32</entry></row>
<row><entry>TELNET_SB_DET_ERASE_REST_LINE</entry><entry>33</entry></row>
<row><entry>TELNET_SB_DET_ERASE_REST_FIELD</entry><entry>34</entry></row>
<row><entry>TELNET_SB_DET_ERASE_UNPROTECTED</entry><entry>35</entry></row>
<row><entry>TELNET_SB_DET_FORMAT_DATA</entry><entry>36</entry></row>
<row><entry>TELNET_SB_DET_REPEAT</entry><entry>37</entry></row>
<row><entry>TELNET_SB_DET_SUPPRESS_PROTECTION</entry><entry>38</entry></row>
<row><entry>TELNET_SB_DET_FIELD_SEPARATOR</entry><entry>39</entry></row>
<row><entry>TELNET_SB_DET_FN</entry><entry>40</entry></row>
<row><entry>TELNET_SB_DET_ERROR</entry><entry>41</entry></row>

<row><entry>TELNET_SUPDUP</entry><entry>21</entry><entry>736, 734</entry></row>

<row><entry>TELNET_SUPDUP_OUTPUT</entry><entry>22</entry><entry>749</entry></row>

<row><entry>TELNET_SEND_LOCATION</entry><entry>23</entry><entry>779</entry></row>

<row><entry>TELNET_TERMINAL_TYPE</entry><entry>24</entry><entry>1091</entry></row>
<row><entry>TELNET_SB_TERMINAL_TYPE_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_TERMINAL_TYPE_SEND</entry><entry>1</entry></row>

<row><entry>TELNET_EOR</entry><entry>25</entry><entry>885</entry></row>

<row><entry>TELNET_TUID</entry><entry>26</entry><entry>927</entry></row>

<row><entry>TELNET_OUTMRK</entry><entry>27</entry><entry>933</entry></row>

<row><entry>TELNET_TTYLOC</entry><entry>28 946</entry></row>

<row><entry>TELNET_3270_REGIME</entry><entry>29</entry><entry>1041</entry></row>
<row><entry>TELNET_SB_3270_REGIME_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_3270_REGIME_ARE</entry><entry>1</entry></row>

<row><entry>TELNET_X3_PAD</entry><entry>30</entry><entry>1053</entry></row>
<row><entry>TELNET_SB_X3_PAD_SET</entry><entry>0</entry></row>
<row><entry>TELNET_SB_X3_PAD_RESPONSE_SET</entry><entry>1</entry></row>
<row><entry>TELNET_SB_X3_PAD_IS</entry><entry>2</entry></row>
<row><entry>TELNET_SB_X3_PAD_RESPONSE_IS</entry><entry>3</entry></row>
<row><entry>TELNET_SB_X3_PAD_SEND</entry><entry>4</entry></row>

<row><entry>TELNET_NAWS</entry><entry>31</entry><entry>1073</entry></row>

<row><entry>TELNET_TERMINAL_SPEED</entry><entry>32</entry><entry>1079</entry></row>
<row><entry>TELNET_SB_TERMINAL_SPEED_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_TERMINAL_SPEED_SEND</entry><entry>1</entry></row>

<row><entry>TELNET_TOGGLE_FLOW_CONTROL</entry><entry>33</entry><entry>1372</entry></row>
<row><entry>TELNET_SB_TOGGLE_FLOW_CONTROL_OFF</entry><entry>0</entry></row>
<row><entry>TELNET_SB_TOGGLE_FLOW_CONTROL_ON</entry><entry>1</entry></row>
<row><entry>TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_ANY </entry><entry>2</entry></row>
<row><entry>TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_XON </entry><entry>3</entry></row>

<row><entry>TELNET_LINEMODE</entry><entry>34</entry><entry>1184</entry></row>
<row><entry>TELNET_SB_LINEMODE_MODE</entry><entry>1</entry></row>
<row><entry>TELNET_SB_LINEMODE_FORWARDMASK</entry><entry>2</entry></row>
<row><entry>TELNET_SB_LINEMODE_SLC</entry><entry>3</entry></row>

<row><entry>TELNET_X_DISPLAY_LOCATION</entry><entry>35</entry><entry>1096</entry></row>
<row><entry>TELNET_SB_X_DISPLAY_LOCATION_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_X_DISPLAY_LOCATION_SEND</entry><entry>1</entry></row>

<row><entry>TELNET_OLD_ENVIRONMENT</entry><entry>36</entry><entry>1408</entry></row>
<row><entry>TELNET_SB_OLD_ENVIRONMENT_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_OLD_ENVIRONMENT_SEND</entry><entry>1</entry></row>
<row><entry>TELNET_SB_OLD_ENVIRONMENT_INFO</entry><entry>2</entry></row>

<row><entry>TELNET_AUTHENTICATION</entry><entry>37</entry><entry>2941</entry></row>
<row><entry>TELNET_SB_AUTHENTICATION_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_AUTHENTICATION_SEND</entry><entry>1</entry></row>
<row><entry>TELNET_SB_AUTHENTICATION_REPLY</entry><entry>2</entry></row>
<row><entry>TELNET_SB_AUTHENTICATION_NAME</entry><entry>3</entry></row>

<row><entry>TELNET_ENCRYPT</entry><entry>38</entry><entry>2946</entry></row>
<row><entry>TELNET_SB_ENCRYPT_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_ENCRYPT_SUPPORT</entry><entry>1</entry></row>
<row><entry>TELNET_SB_ENCRYPT_REPLY</entry><entry>2</entry></row>
<row><entry>TELNET_SB_ENCRYPT_START</entry><entry>3</entry></row>
<row><entry>TELNET_SB_ENCRYPT_END</entry><entry>4</entry></row>
<row><entry>TELNET_SB_ENCRYPT_REQUEST_START</entry><entry>5</entry></row>
<row><entry>TELNET_SB_ENCRYPT_REQUEST_END</entry><entry>6</entry></row>
<row><entry>TELNET_SB_ENCRYPT_ENC_KEYID</entry><entry>7</entry></row>
<row><entry>TELNET_SB_ENCRYPT_DEC_KEYID</entry><entry>8</entry></row>

<row><entry>TELNET_ENVIRONMENT</entry><entry>39</entry><entry>1572</entry></row>
<row><entry>TELNET_SB_ENVIRONMENT_IS</entry><entry>0</entry></row>
<row><entry>TELNET_SB_ENVIRONMENT_SEND</entry><entry>1</entry></row>
<row><entry>TELNET_SB_ENVIRONMENT_INFO</entry><entry>2</entry></row>

<row><entry>TELNET_TN3270E</entry><entry>40</entry><entry>1647</entry></row>
<row><entry>TELNET_SB_TN3270E_ASSOCIATE</entry><entry>0</entry></row>
<row><entry>TELNET_SB_TN3270E_CONNECT</entry><entry>1</entry></row>
<row><entry>TELNET_SB_TN3270E_DEVICE_TYPE</entry><entry>2</entry></row>
<row><entry>TELNET_SB_TN3270E_FUNCTIONS</entry><entry>3</entry></row>
<row><entry>TELNET_SB_TN3270E_IS</entry><entry>4</entry></row>
<row><entry>TELNET_SB_TN3270E_REASON</entry><entry>5</entry></row>
<row><entry>TELNET_SB_TN3270E_REJECT</entry><entry>6</entry></row>
<row><entry>TELNET_SB_TN3270E_REQUEST</entry><entry>7</entry></row>
<row><entry>TELNET_SB_TN3270E_SEND</entry><entry>8</entry></row>

<row><entry>TELNET_CHARSET</entry><entry>42</entry><entry>2066</entry></row>
<row><entry>TELNET_SB_CHARSET_REQUEST</entry><entry>1</entry></row>
<row><entry>TELNET_SB_CHARSET_ACCEPTED</entry><entry>2</entry></row>
<row><entry>TELNET_SB_CHARSET_REJECTED</entry><entry>3</entry></row>
<row><entry>TELNET_SB_CHARSET_TTABLE_IS</entry><entry>4</entry></row>
<row><entry>TELNET_SB_CHARSET_TTABLE_REJECTED</entry><entry>5</entry></row>
<row><entry>TELNET_SB_CHARSET_TTABLE_ACK</entry><entry>6</entry></row>
<row><entry>TELNET_SB_CHARSET_TTABLE_NAK</entry><entry>7</entry></row>

<row><entry>TELNET_COM_PORT</entry><entry>44</entry><entry>2217</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_SET_BAUDRATE</entry><entry>1</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_SET_DATASIZE</entry><entry>2</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_SET_PARITY</entry><entry>3</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_SET_STOPSIZE</entry><entry>4</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_SET_CONTROL</entry><entry>5</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_NOTIFY_LINESTATE</entry><entry>6</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_NOTIFY_MODEMSTATE </entry><entry>7</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_FLOWCONTROL_SUSPEND </entry><entry>8</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_FLOWCONTROL_RESUME </entry><entry>9</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_SET_LINESTATE_MASK </entry><entry>10</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_SET_MODEMSTATE_MASK </entry><entry>11</entry></row>
<row><entry>TELNET_SB_COM_PORT_CLI_PURGE_DATA</entry><entry>12</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_SET_BAUDRATE</entry><entry>101</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_SET_DATASIZE</entry><entry>102</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_SET_PARITY</entry><entry>103</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_SET_STOPSIZE</entry><entry>104</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_SET_CONTROL</entry><entry>105</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_NOTIFY_LINESTATE</entry><entry>106</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_NOTIFY_MODEMSTATE </entry><entry>107</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_FLOWCONTROL_SUSPEND </entry><entry>108</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_FLOWCONTROL_RESUME </entry><entry>109</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_SET_LINESTATE_MASK </entry><entry>110</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_SET_MODEMSTATE_MASK </entry><entry>111</entry></row>
<row><entry>TELNET_SB_COM_PORT_SVR_PURGE_DATA</entry><entry>112</entry></row>

<row><entry>TELNET_KERMIT</entry><entry>47</entry><entry>2840</entry></row>
<row><entry>TELNET_SB_KERMIT_START_SERVER</entry><entry>0</entry></row>
<row><entry>TELNET_SB_KERMIT_STOP_SERVER</entry><entry>1</entry></row>
<row><entry>TELNET_SB_KERMIT_REQ_START_SERVER</entry><entry>2</entry></row>
<row><entry>TELNET_SB_KERMIT_REQ_STOP_SERVER</entry><entry>3</entry></row>
<row><entry>TELNET_SB_KERMIT_SOP</entry><entry>4</entry></row>
<row><entry>TELNET_SB_KERMIT_RESP_START_SERVER</entry><entry>8</entry></row>
<row><entry>TELNET_SB_KERMIT_RESP_STOP_SERVER</entry><entry>9</entry></row>

<row><entry>TELNET_EXOPL</entry><entry>255</entry><entry>861</entry></row>

<row><entry>TELNET_SUBLIMINAL_MSG</entry><entry>257</entry><entry>1097</entry></row>

</tbody>
</tgroup>
</table>


  </appendix>
  <metainfo>
    <enums>
      <enum maturity="stable" id="enum.telnet.opt">
        <description>
          Telnet proxy option hashes.
        </description>
        <item>
          <name>TELNET_OPT_ACCEPT</name>
        </item>
        <item>
          <name>TELNET_OPT_REJECT</name>
        </item>
        <item>
          <name>TELNET_OPT_ABORT</name>
        </item>
        <item>
          <name>TELNET_OPT_DROP</name>
        </item>
        <item>
          <name>TELNET_OPT_POLICY</name>
        </item>
      </enum>
      <enum maturity="stable" id="enum.telnet.prot">
        <description>
          Protocol parameter hashes to control the dataflow.
        </description>
        <item>
          <name>TELNET_TERMINAL_TYPE</name>
        </item>
        <item>
          <name>TELNET_TERMINAL_SPEED</name>
        </item>
        <item>
          <name>TELNET_X_DISPLAY_LOCATION</name>
        </item>
        <item>
          <name>TELNET_ENVIRONMENT</name>
        </item>
        <item>
          <name>TELNET_SUPPRESS_GO_AHEAD</name>
        </item>
        <item>
          <name>TELNET_ECHO</name>
        </item>
        <item>
          <name>TELNET_NAWS</name>
        </item>
      </enum>
      <enum maturity="stable" id="enum.telnet.sb">
        <description>
          SB hashes of Telnet.
        </description>
        <item>
          <name>TELNET_SB_IS</name>
        </item>
        <item>
          <name>TELNET_SB_SEND</name>
        </item>
        <item>
          <name>TELNET_SB_INFO</name>
        </item>
      </enum>
    </enums>
    <constants>
      <constantgroup maturity="stable" id="const.telnet.log">
        <description>
          Parameters of the logging system. These values are printed into the log messages.
        </description>
        <item>
          <name>TELNET_POLICY</name>
          <value>"telnet.policy"</value>
        </item>
      </constantgroup>
    </constants>
    <actiontuples>
      <actiontuple maturity="stable" id="action.telnet.opt" action_enum="enum.telnet.opt">
        <description>
          Action codes for Telnet options
        </description>
        <tuple action="TELNET_OPT_ACCEPT">
          <args/>
          <description>
            <para>
              Allow the option.
            </para>
          </description>
        </tuple>
        <tuple action="TELNET_OPT_DROP">
          <args/>
          <description>
            <para>
              Reject the option.
            </para>
          </description>
        </tuple>
        <tuple action="TELNET_OPT_ABORT">
          <args/>
          <description>
            <para>
              Reject the option and terminate the Telnet session.
            </para>
          </description>
        </tuple>
        <tuple action="TELNET_OPT_POLICY">
          <args>METHOD</args>
          <description>
            <para>
              Call the function specified to make a decision about the event. The function receives two parameters: self, and option (an integer). See <xref linkend="proxy_policies"/> for details.
            </para>
          </description>
        </tuple>
      </actiontuple>
    </actiontuples>
  </metainfo>
</module>"""

from Zorp import *
from Proxy import Proxy, proxyLog
from Util import makeSequence

TELNET_OPT_ACCEPT = ZV_ACCEPT
TELNET_OPT_REJECT = ZV_REJECT
TELNET_OPT_ABORT  = ZV_ABORT
TELNET_OPT_DROP   = ZV_DROP
TELNET_OPT_POLICY = ZV_POLICY

# No negotiation is needed for that command
TELNET_NEG_NONE = 255


# option name                           value   RFC, subopt
# -------------------------             ------  -----------
#TELNET_BINARY                          = "0"   # 856

TELNET_ECHO                             = "1"   # 857

TELNET_SUPPRESS_GO_AHEAD                = "3"   # 858

#TELNET_STATUS                          = "5"   # 859
#TELNET_SB_STATUS_SB_IS                         = "0"
#TELNET_SB_STATUS_SB_SEN                        = "1"

#TELNET_TIMING_MARK                     = "6"  # 860

#TELNET_RCTE                            = "7"  # 726

#TELNET_NAOCRD                          = "10"  # 652
#TELNET_SB_NAOCRD_DR                            = "0"
#TELNET_SB_NAOCRD_DS                            = "1"

#TELNET_NAOHTS                          = "11"  # 653
#TELNET_SB_NAOHTS_DR                            = "0"
#TELNET_SB_NAOHTS_DS                            = "1"

#TELNET_NAOHTD                          = "12"  # 654
#TELNET_SB_NAOHTD_DR                            = "0"
#TELNET_SB_NAOHTD_DS                            = "1"

#TELNET_NAOFFD                          = "13"  # 655
#TELNET_SB_NAOFFD_DR                            = "0"
#TELNET_SB_NAOFFD_DS                            = "1"

#TELNET_NAOVTS                          = "14"  # 656
#TELNET_SB_NAOVTS_DR                            = "0"
#TELNET_SB_NAOVTS_DS                            = "1"

#TELNET_NAOVTD                          = "15"  # 657
#TELNET_SB_NAOVTD_DR                            = "0"
#TELNET_SB_NAOVTD_DS                            = "1"

#TELNET_NAOLFD                          = "16"  # 658

#TELNET_SB_NAOLFD_DR                            = "0"
#TELNET_SB_NAOLFD_DS                            = "1"

#TELNET_EXTEND_ASCII                    = "17"  # 698

#TELNET_LOGOUT                          = "18"  # 727

#TELNET_BM                              = "19"  # 735
#TELNET_SB_BM_DEFINE                            = "1"
#TELNET_SB_BM_ACCEPT                            = "2"
#TELNET_SB_BM_REFUSE                            = "3"
#TELNET_SB_BM_LITERAL                           = "4"
#TELNET_SB_BM_CANCEL                            = "5"

#TELNET_DET                             = "20"  # 1043, 732
#TELNET_SB_DET_DEFINE                           = "1"
#TELNET_SB_DET_ERASE                            = "2"
#TELNET_SB_DET_TRANSMIT                         = "3"
#TELNET_SB_DET_FORMAT                           = "4"
#TELNET_SB_DET_MOVE_CURSOR                      = "5"
#TELNET_SB_DET_SKIP_TO_LINE                     = "6"
#TELNET_SB_DET_SKIP_TO_CHAR                     = "7"
#TELNET_SB_DET_UP                               = "8"
#TELNET_SB_DET_DOWN                             = "9"
#TELNET_SB_DET_LEFT                             = "10"
#TELNET_SB_DET_RIGHT                            = "11"
#TELNET_SB_DET_HOME                             = "12"
#TELNET_SB_DET_LINE_INSERT                      = "13"
#TELNET_SB_DET_LINE_DELETE                      = "14"
#TELNET_SB_DET_CHAR_INSERT                      = "15"
#TELNET_SB_DET_CHAR_DELETE                      = "16"
#TELNET_SB_DET_READ_CURSOR                      = "17"
#TELNET_SB_DET_CURSOR_POSITION                  = "18"
#TELNET_SB_DET_REVERSE_TAB                      = "19"
#TELNET_SB_DET_TRANSMIT_SCREEN                  = "20"
#TELNET_SB_DET_TRANSMIT_UNPROTECTED             = "21"
#TELNET_SB_DET_TRANSMIT_LINE                    = "22"
#TELNET_SB_DET_TRANSMIT_FIELD                   = "23"
#TELNET_SB_DET_TRANSMIT_REST_SCREEN             = "24"
#TELNET_SB_DET_TRANSMIT_REST_LINE               = "25"
#TELNET_SB_DET_TRANSMIT_REST_FIELD              = "26"
#TELNET_SB_DET_TRANSMIT_MODIFIED                = "27"
#TELNET_SB_DET_DATA_TRANSMIT                    = "28"
#TELNET_SB_DET_ERASE_SCREEN                     = "29"
#TELNET_SB_DET_ERASE_LINE                       = "30"
#TELNET_SB_DET_ERASE_FIELD                      = "31"
#TELNET_SB_DET_ERASE_REST_SCREEN                = "32"
#TELNET_SB_DET_ERASE_REST_LINE                  = "33"
#TELNET_SB_DET_ERASE_REST_FIELD                 = "34"
#TELNET_SB_DET_ERASE_UNPROTECTED                = "35"
#TELNET_SB_DET_FORMAT_DATA                      = "36"
#TELNET_SB_DET_REPEAT                           = "37"
#TELNET_SB_DET_SUPPRESS_PROTECTION              = "38"
#TELNET_SB_DET_FIELD_SEPARATOR                  = "39"
#TELNET_SB_DET_FN                               = "40"
#TELNET_SB_DET_ERROR                            = "41"

#TELNET_SUPDUP                          = "21"  # 736, 734

#TELNET_SUPDUP_OUTPUT                   = "22"  # 749

#TELNET_SEND_LOCATION                   = "23"  # 779

TELNET_TERMINAL_TYPE                    = "24"  # 1091
TELNET_SB_TERMINAL_TYPE_IS                      = "0"
TELNET_SB_TERMINAL_TYPE_SEND                    = "1"

TELNET_EOR                              = "25"  # 885

TELNET_STARTTLS                 = "46"
TELNET_SB_STARTTLS_FOLLOWS      = "1"

#TELNET_TUID                            = "26"  # 927

#TELNET_OUTMRK                          = "27"  # 933

#TELNET_TTYLOC                          = "28   # 946

#TELNET_3270_REGIME                     = "29"  # 1041
#TELNET_SB_3270_REGIME_IS                       = "0"
#TELNET_SB_3270_REGIME_ARE                      = "1"

#TELNET_X3_PAD                          = "30"  # 1053
#TELNET_SB_X3_PAD_SET                           = "0"
#TELNET_SB_X3_PAD_RESPONSE_SET                  = "1"
#TELNET_SB_X3_PAD_IS                            = "2"
#TELNET_SB_X3_PAD_RESPONSE_IS                   = "3"
#TELNET_SB_X3_PAD_SEND                          = "4"

TELNET_NAWS                             = "31"  # 1073

TELNET_TERMINAL_SPEED                   = "32"  # 1079
TELNET_SB_TERMINAL_SPEED_IS                     = "0"
TELNET_SB_TERMINAL_SPEED_SEND                   = "1"

#TELNET_TOGGLE_FLOW_CONTROL             = "33"  # 1372
#TELNET_SB_TOGGLE_FLOW_CONTROL_OFF              = "0"
#TELNET_SB_TOGGLE_FLOW_CONTROL_ON               = "1"
#TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_ANY      = "2"
#TELNET_SB_TOGGLE_FLOW_CONTROL_RESTART_XON      = "3"

#TELNET_LINEMODE                        = "34"  # 1184
#TELNET_SB_LINEMODE_MODE                        = "1"
#TELNET_SB_LINEMODE_FORWARDMASK                 = "2"
#TELNET_SB_LINEMODE_SLC                         = "3"

TELNET_X_DISPLAY_LOCATION               = "35"  # 1096
TELNET_SB_X_DISPLAY_LOCATION_IS                 = "0"
TELNET_SB_X_DISPLAY_LOCATION_SEND               = "1"

#TELNET_OLD_ENVIRONMENT                 = "36"  # 1408
#TELNET_SB_OLD_ENVIRONMENT_IS                   = "0"
#TELNET_SB_OLD_ENVIRONMENT_SEND                 = "1"
#TELNET_SB_OLD_ENVIRONMENT_INFO                 = "2"

#TELNET_AUTHENTICATION                  = "37"  # 2941
#TELNET_SB_AUTHENTICATION_IS                    = "0"
#TELNET_SB_AUTHENTICATION_SEND                  = "1"
#TELNET_SB_AUTHENTICATION_REPLY                 = "2"
#TELNET_SB_AUTHENTICATION_NAME                  = "3"

#TELNET_ENCRYPT                         = "38"  # 2946
#TELNET_SB_ENCRYPT_IS                           = "0"
#TELNET_SB_ENCRYPT_SUPPORT                      = "1"
#TELNET_SB_ENCRYPT_REPLY                        = "2"
#TELNET_SB_ENCRYPT_START                        = "3"
#TELNET_SB_ENCRYPT_END                          = "4"
#TELNET_SB_ENCRYPT_REQUEST_START                = "5"
#TELNET_SB_ENCRYPT_REQUEST_END                  = "6"
#TELNET_SB_ENCRYPT_ENC_KEYID                    = "7"
#TELNET_SB_ENCRYPT_DEC_KEYID                    = "8"

TELNET_ENVIRONMENT                      = "39"  # 1572
TELNET_SB_ENVIRONMENT_IS                        = "0"
TELNET_SB_ENVIRONMENT_SEND                      = "1"
TELNET_SB_ENVIRONMENT_INFO                      = "2"

#TELNET_TN3270E                         = "40"  # 1647
#TELNET_SB_TN3270E_ASSOCIATE                    = "0"
#TELNET_SB_TN3270E_CONNECT                      = "1"
#TELNET_SB_TN3270E_DEVICE_TYPE                  = "2"
#TELNET_SB_TN3270E_FUNCTIONS                    = "3"
#TELNET_SB_TN3270E_IS                           = "4"
#TELNET_SB_TN3270E_REASON                       = "5"
#TELNET_SB_TN3270E_REJECT                       = "6"
#TELNET_SB_TN3270E_REQUEST                      = "7"
#TELNET_SB_TN3270E_SEND                         = "8"

#TELNET_CHARSET                         = "42"  # 2066
#TELNET_SB_CHARSET_REQUEST                      = "1"
#TELNET_SB_CHARSET_ACCEPTED                     = "2"
#TELNET_SB_CHARSET_REJECTED                     = "3"
#TELNET_SB_CHARSET_TTABLE_IS                    = "4"
#TELNET_SB_CHARSET_TTABLE_REJECTED              = "5"
#TELNET_SB_CHARSET_TTABLE_ACK                   = "6"
#TELNET_SB_CHARSET_TTABLE_NAK                   = "7"

#TELNET_COM_PORT                        = "44"  # 2217
#TELNET_SB_COM_PORT_CLI_SET_BAUDRATE            = "1"
#TELNET_SB_COM_PORT_CLI_SET_DATASIZE            = "2"
#TELNET_SB_COM_PORT_CLI_SET_PARITY              = "3"
#TELNET_SB_COM_PORT_CLI_SET_STOPSIZE            = "4"
#TELNET_SB_COM_PORT_CLI_SET_CONTROL             = "5"
#TELNET_SB_COM_PORT_CLI_NOTIFY_LINESTATE        = "6"
#TELNET_SB_COM_PORT_CLI_NOTIFY_MODEMSTATE       = "7"
#TELNET_SB_COM_PORT_CLI_FLOWCONTROL_SUSPEND     = "8"
#TELNET_SB_COM_PORT_CLI_FLOWCONTROL_RESUME      = "9"
#TELNET_SB_COM_PORT_CLI_SET_LINESTATE_MASK      = "10"
#TELNET_SB_COM_PORT_CLI_SET_MODEMSTATE_MASK     = "11"
#TELNET_SB_COM_PORT_CLI_PURGE_DATA              = "12"
#TELNET_SB_COM_PORT_SVR_SET_BAUDRATE            = "101"
#TELNET_SB_COM_PORT_SVR_SET_DATASIZE            = "102"
#TELNET_SB_COM_PORT_SVR_SET_PARITY              = "103"
#TELNET_SB_COM_PORT_SVR_SET_STOPSIZE            = "104"
#TELNET_SB_COM_PORT_SVR_SET_CONTROL             = "105"
#TELNET_SB_COM_PORT_SVR_NOTIFY_LINESTATE        = "106"
#TELNET_SB_COM_PORT_SVR_NOTIFY_MODEMSTATE       = "107"
#TELNET_SB_COM_PORT_SVR_FLOWCONTROL_SUSPEND     = "108"
#TELNET_SB_COM_PORT_SVR_FLOWCONTROL_RESUME      = "109"
#TELNET_SB_COM_PORT_SVR_SET_LINESTATE_MASK      = "110"
#TELNET_SB_COM_PORT_SVR_SET_MODEMSTATE_MASK     = "111"
#TELNET_SB_COM_PORT_SVR_PURGE_DATA              = "112"

#TELNET_KERMIT                          = "47"  # 2840
#TELNET_SB_KERMIT_START_SERVER                  = "0"
#TELNET_SB_KERMIT_STOP_SERVER                   = "1"
#TELNET_SB_KERMIT_REQ_START_SERVER              = "2"
#TELNET_SB_KERMIT_REQ_STOP_SERVER               = "3"
#TELNET_SB_KERMIT_SOP                           = "4"
#TELNET_SB_KERMIT_RESP_START_SERVER             = "8"
#TELNET_SB_KERMIT_RESP_STOP_SERVER              = "9"

TELNET_EXOPL                            = "255" # 861

#TELNET_SUBLIMINAL_MSG                  = "257" # 1097


################################################################################
# Non-RFC telnet options
#
# 2 - reconnection                      # [nic 50005]
# 4 - approx msg size negotiation       # [ethernet]
# 8 - output line width                 # [nic 50005]
# 9 - output page size                  # [nic 50005]
# 41 - xauth                            # [Earhart]
# 43 - remote serial port               # [Barnes]
# 45 - suppress local echo              # [Atmar]
# 46 - start tls                        # [Boe]
# 48 - send url                         # [Croft]
# 49 - forward X                        # [Altman]
# 50-137 - unassigned                   # [IANA]
# 138 - telopt pragma logon             # [McGregory]
# 139 - telopt sspi logon               # [McGregory]
# 140 - telopt pargma heartbeat         # [McGregory]
# 141-254 - unassigned                  # [IANA]
# 256 - unassigned                      # [IANA]

TELNET_SB_IS    = "0"
TELNET_SB_SEND  = "1"
TELNET_SB_INFO  = "2"

TELNET_POLICY   = "telnet.policy"


class ParseInbandAuthError(Exception):
    """<class internal="yes"/>"""
    pass

class AbstractTelnetProxy(Proxy):
    """<class maturity="stable" abstract="yes">
            <summary>
              Class encapsulating the abstract Telnet proxy.
            </summary>
            <description>
              <para>
                This class implements the Telnet protocol (as described in RFC 854) and its most common
                extensions. Although not all possible options are checked by the low
                level proxy, it is possible to filter any option
                and suboption negotiation sequences using policy callbacks.
                AbstractTelnetProxy serves as a starting point for customized proxy classes, but is itself not directly usable. Service definitions should refer to a customized class derived from AbstractTelnetProxy, or one of the predefined TelnetProxy proxy classes. AbstractTelnetProxy denies all options by default.
              </para>
            </description>
            <metainfo>
              <attributes>
                <attribute maturity="stable">
                  <name>option</name>
                  <type>
                    <hash>
                      <key>
                        <choice>
                          <string/>
                          <tuple>
                            <string/>
                            <string/>
                          </tuple>
                        </choice>
                      </key>
                      <value>
                        <link id="action.telnet.opt"/>
                      </value>
                    </hash>
                  </type>
                  <conftime>
                    <read/>
                    <write/>
                  </conftime>
                  <runtime>
                    <read/>
                    <write/>
                  </runtime>
                  <description>
                    Normative policy hash for Telnet options
                  indexed by the option and (optionally) the suboption. See also <xref linkend="telnet_policies"/>.
                  </description>
                </attribute>
                <attribute maturity="stable">
                  <name>negotiation</name>
                  <type>
                    <hash>
                      <key>
                        <string/>
                      </key>
                      <value>
                        <integer/>
                      </value>
                    </hash>
                  </type>
                  <default/>
                  <conftime>
                    <read/>
                    <write/>
                  </conftime>
                  <runtime>
                    <read/>
                    <write/>
                  </runtime>
                  <description>
                    Normative hash listing which options must be negotiated for a given command. See <xref linkend="telnet_negotiation"/> for details.
                  </description>
                </attribute>
                <attribute maturity="stable">
                  <name>timeout</name>
                  <type>
                    <integer/>
                  </type>
                  <default>600000</default>
                  <conftime>
                    <read/>
                    <write/>
                  </conftime>
                  <runtime>
                    <read/>
                  </runtime>
                  <description>
                    I/O timeout in milliseconds.
                  </description>
                </attribute>
                <attribute maturity="stable">
                  <name>current_var_name</name>
                  <type>
                    <string/>
                  </type>
                  <default>n/a</default>
                  <conftime/>
                  <runtime>
                    <read/>
                    <write/>
                  </runtime>
                  <description>
                    Name of the variable being negotiated.
                  </description>
                </attribute>
                <attribute maturity="stable">
                  <name>current_var_value</name>
                  <type>
                    <string/>
                  </type>
                  <default>n/a</default>
                  <conftime/>
                  <runtime>
                    <read/>
                    <write/>
                  </runtime>
                  <description>
                    Value of the variable being negotiated (e.g.: value of an environment variable,
                    an X display location value, etc.).
                  </description>
                </attribute>
                <attribute maturity="stable">
                  <name>enable_audit</name>
                  <type>
                    <boolean/>
                  </type>
                  <default>FALSE</default>
                  <conftime>
                    <write/>
                  </conftime>
                  <runtime>
                    <read/>
                  </runtime>
                  <description>
                    Enable session auditing.
                  </description>
                </attribute>
              </attributes>
            </metainfo>
          </class>
          """
    name = "telnet"

    def __init__(self, session):
        """<method maturity="stable" internal="yes">
                      <summary>
                        Constructor to initialize a TelnetProxy instance.
                      </summary>
                      <description>
                        <para>
                          This function initializes a TelnetProxy instance by calling
                          the inherited __init__ constructor with appropriate
                          parameters.
                        </para>
                      </description>
                      <metainfo>
                        <arguments/>
                      </metainfo>
                    </method>
                    """
        Proxy.__init__(self, session)
        self.username = ""
        self.gateway_user = ""
        self.server_hostname = ""
        self.server_port = 23

    def config(self):
        """<method maturity="stable" internal="yes">
          <metainfo>
            <arguments/>
          </metainfo>
        </method>
        """
        pass

    def __post_config__(self):
        """<method maturity="stable" internal="yes">
          <metainfo>
            <arguments/>
          </metainfo>
        </method>
        """
        Proxy.__post_config__(self)



    def parseInbandAuth(self, env, content):
        """<method internal="yes">
            <summary>
                This method should be called when inband authentication is used, to parse the data in SERVER and USER environment variables.
            </summary>
            <description>
                This method fills in self.username, self.gateway_user, self.server_hostname, self.server_port
                from the SERVER and USER environment parameters passed to it. The self.server_port defaults to 23.
                If both are specified the actual hostname comes from the more specific SERVER option.
                SERVER option's notation form: server[:port]
                USER option's notation form: gu=user@[serveruser]@server[:port]
                [] means the value is optional.
            </description>
            <metainfo>
                <arguments/>
            </metainfo>
            </method>
        """

        state = {
          "server_hostname" : self.server_hostname,
          "server_port" : self.server_port,
          "gateway_user" : self.gateway_user,
          "username" : self.username,
        }

        def parseServer(self, content, state):
            """<method internal="yes"/>"""
            colon_count = content.count(':')
            if not content:
                #Empty content is not acceptable, so make it fail
                colon_count = 2

            if colon_count == 0:
                state["server_hostname"] = content
            elif colon_count == 1 and content[-1] == ':':
                state["server_hostname"], port_string = content.split(':')
            elif colon_count == 1 and content[0] != ':':
                state["server_hostname"], port_string = content.split(':')
                try:
                    state["server_port"] = int(port_string)
                    if state["server_port"] < 1 or state["server_port"] > 65535:
                        raise ValueError, "Port number out of range"
                except ValueError as e:
                    raise ParseInbandAuthError, "Invalid port value"
            else:
                raise ParseInbandAuthError, "SERVER environment variable must be in the form of server[:port]"

        def parseUser(self, content, state):
            """<method internal="yes"/>"""
            at_count = content.count('@')
            colon_count = content.count(':')
            if not content:
                #Empty content is not acceptable, so make it fail
                colon_count = 2

            if at_count > 2 or colon_count > 1 or (at_count == 2 and content[:3] != "gu="):
                raise ParseInbandAuthError, "USER environment variable must be in the form of user[@server[:port]], or gu=gwuser@[serveruser]@server[:port]"

            if at_count == 0:
                state["username"] = content
            elif at_count == 1:
                if content[:3] == "gu=":
                     """ With a Directed router this form of the USER variable
                         can also be used: gu=gwuser@serveruser """
                     gwuser, state["username"] = content.split('@')
                     state["gateway_user"] = gwuser[3:]
                else:
                    state["username"], host = content.split('@')
                    if not state["server_hostname"]:
                        parseServer(self, host, state)
            elif at_count == 2:
                gwuser, username, host = content.split('@')
                state["gateway_user"] = gwuser[3:]
                if username:
                    state["username"] = username
                if not state["server_hostname"]:
                    parseServer(self, host, state)

        def applyState(o, state):
            for (attr, value) in state.items():
                setattr(o, attr, value)

        env = env.upper()
        try:
            if env == 'SERVER':
                parseServer(self, content, state)
            elif env == 'USER':
                parseUser(self, content, state)
            else:
                proxyLog(self, TELNET_POLICY, 3, "Error parsing inband authorization token, " \
                                                 "unknown environment variable; env='%s'", (env, ))
                return FALSE
        except ParseInbandAuthError as e:
            proxyLog(self, TELNET_POLICY, 3, "Error parsing inband authorization token; " \
                                             "env='%s', content='%s', error='%s'", (env, content, e.args[0]))
            return FALSE
        except ValueError as e:
            proxyLog(self, TELNET_POLICY, 3, "Error parsing inband authorization token, " \
                                             "input does not match the supported format; " \
                                             "env='%s', content='%s'", (env, content))
            return FALSE

        applyState(self, state)


        return TRUE

class TelnetProxy(AbstractTelnetProxy):
    """<class maturity="stable">
      <summary>
        Default Telnet proxy based on AbstractTelnetProxy.
      </summary>
      <description>
      <para>TelnetProxy is a proxy class based on AbstractTelnetProxy, allowing the use of all Telnet options.</para>
      </description>
      <metainfo>
        <attributes/>
      </metainfo>
    </class>
    """
    def config(self):
        """<method maturity="stable" internal="yes">
          <metainfo>
            <arguments/>
          </metainfo>
        </method>
        """
        self.option["*"]        = TELNET_OPT_ACCEPT
        self.option["*", "*"]   = TELNET_OPT_ACCEPT
        self.negotiation["239"] = int(TELNET_EOR)

class TelnetProxyStrict(AbstractTelnetProxy):
    """<class maturity="stable">
      <summary>
        Telnet proxy based on AbstractTelnetProxy, allowing only the minimal command set.
      </summary>
      <description>
      <para>TelnetProxyStrict is a proxy class based on AbstractTelnetProxy, allowing the use of the options minimally required for a useful Telnet session.</para>
      <para>The following options are permitted: ECHO; SUPPRESS_GO_AHEAD; TERMINAL_TYPE; NAWS; EOR; TERMINAL_SPEED; X_DISPLAY_LOCATION; ENVIRONMENT.
                All other options are rejected.
                </para>
      </description>
      <metainfo>
        <attributes/>
      </metainfo>
    </class>
    """
    def config(self):
        """<method maturity="stable" internal="yes">
          <summary>
            Configuration for TelnetProxy
          </summary>
          <description>
            <para>
              It enables all options needed for a useful Telnet session.
            </para>

          </description>
          <metainfo>
            <arguments/>
          </metainfo>
        </method>
        """
        self.option[TELNET_ECHO]                = TELNET_OPT_ACCEPT
        self.option[TELNET_SUPPRESS_GO_AHEAD]   = TELNET_OPT_ACCEPT
        self.option[TELNET_TERMINAL_TYPE]       = TELNET_OPT_ACCEPT
        self.option[TELNET_NAWS]                = TELNET_OPT_ACCEPT
        self.option[TELNET_EOR]                 = TELNET_OPT_ACCEPT
        self.option[TELNET_TERMINAL_SPEED]      = TELNET_OPT_ACCEPT
        self.option[TELNET_X_DISPLAY_LOCATION]  = TELNET_OPT_ACCEPT
        self.option[TELNET_ENVIRONMENT]         = TELNET_OPT_ACCEPT
        self.option["*"]                        = TELNET_OPT_REJECT

        self.option[TELNET_TERMINAL_TYPE,       TELNET_SB_TERMINAL_TYPE_IS]             = TELNET_OPT_ACCEPT
        self.option[TELNET_TERMINAL_TYPE,       TELNET_SB_TERMINAL_TYPE_SEND]           = TELNET_OPT_ACCEPT
        self.option[TELNET_TERMINAL_SPEED,      TELNET_SB_TERMINAL_SPEED_IS]            = TELNET_OPT_ACCEPT
        self.option[TELNET_TERMINAL_SPEED,      TELNET_SB_TERMINAL_SPEED_SEND]          = TELNET_OPT_ACCEPT
        self.option[TELNET_X_DISPLAY_LOCATION,  TELNET_SB_X_DISPLAY_LOCATION_IS]        = TELNET_OPT_ACCEPT
        self.option[TELNET_X_DISPLAY_LOCATION,  TELNET_SB_X_DISPLAY_LOCATION_SEND]      = TELNET_OPT_ACCEPT
        self.option[TELNET_ENVIRONMENT,         TELNET_SB_ENVIRONMENT_IS]               = TELNET_OPT_ACCEPT
        self.option[TELNET_ENVIRONMENT,         TELNET_SB_ENVIRONMENT_SEND]             = TELNET_OPT_ACCEPT
        self.option[TELNET_ENVIRONMENT,         TELNET_SB_ENVIRONMENT_INFO]             = TELNET_OPT_ACCEPT
        self.option[TELNET_NAWS,                "*"]                                    = TELNET_OPT_ACCEPT
        self.option[TELNET_EOR,                 "*"]                                    = TELNET_OPT_ACCEPT
        self.option["*",                        "*"]                                    = TELNET_OPT_REJECT

        self.negotiation["239"]                 = int(TELNET_EOR)

class TelnetProxyNonTransparent(TelnetProxy):
    """<class maturity="stable" internal="yes">
      <summary>
      </summary>
      <description/>
      <metainfo>
        <attributes/>
      </metainfo>
    </class>
    """
    auth_inband_supported = TRUE

    def config(self):
        """<method maturity="stable" internal="yes">
          <summary>
          </summary>
          <description>
          </description>
          <metainfo>
            <arguments/>
          </metainfo>
        </method>
        """
        super(TelnetProxyNonTransparent, self).config()
        self.transparent_mode = FALSE
        self.server_name_prompt = "Please enter server host name: "
