qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2 for-2.10 1/8] chardev: Basic support for TN32


From: Cornelia Huck
Subject: Re: [Qemu-devel] [PATCH v2 for-2.10 1/8] chardev: Basic support for TN3270
Date: Mon, 24 Apr 2017 12:38:50 +0200

On Fri,  7 Apr 2017 13:18:44 +0200
Cornelia Huck <address@hidden> wrote:

> From: Jing Liu <address@hidden>
> 
> This introduces basic support for TN3270, which needs to negotiate
> three Telnet options during handshake:
>   - End of Record
>   - Binary Transmission
>   - Terminal-Type
> 
> As a basic implementation, this simply ignores NOP and Interrupt
> Process(IP) commands. More work should be done for them later.
> 
> For more details, please refer to RFC 854 and 1576.
> 
> Signed-off-by: Jing Liu <address@hidden>
> Signed-off-by: Yang Chen <address@hidden>
> Reviewed-by: QingFeng Hao <address@hidden>
> Acked-by: Dong Jia Shi <address@hidden>
> Signed-off-by: Cornelia Huck <address@hidden>
> ---
>  chardev/char-socket.c | 76 
> +++++++++++++++++++++++++++++++++++++--------------
>  chardev/char.c        | 11 ++++++--
>  include/sysemu/char.h |  8 ++++++
>  qapi-schema.json      |  3 ++
>  4 files changed, 76 insertions(+), 22 deletions(-)

ping for some acks for the telnet changes

> 
> diff --git a/chardev/char-socket.c b/chardev/char-socket.c
> index 36ab0d633a..175fb8c3ec 100644
> --- a/chardev/char-socket.c
> +++ b/chardev/char-socket.c
> @@ -55,6 +55,7 @@ typedef struct {
>      SocketAddress *addr;
>      bool is_listen;
>      bool is_telnet;
> +    bool is_tn3270;
> 
>      guint reconnect_timer;
>      int64_t reconnect_time;
> @@ -141,19 +142,25 @@ static int tcp_chr_read_poll(void *opaque)
>      return s->max_size;
>  }
> 
> -#define IAC 255
> -#define IAC_BREAK 243
>  static void tcp_chr_process_IAC_bytes(Chardev *chr,
>                                        SocketChardev *s,
>                                        uint8_t *buf, int *size)
>  {
> -    /* Handle any telnet client's basic IAC options to satisfy char by
> -     * char mode with no echo.  All IAC options will be removed from
> -     * the buf and the do_telnetopt variable will be used to track the
> -     * state of the width of the IAC information.
> +    /* Handle any telnet or tn3270 client's basic IAC options.
> +     * For telnet options, it satisfies char by char mode with no echo.
> +     * For tn3270 options, it satisfies binary mode with EOR.
> +     * All IAC options will be removed from the buf and the do_opt
> +     * pointer will be used to track the state of the width of the
> +     * IAC information.
>       *
> -     * IAC commands come in sets of 3 bytes with the exception of the
> -     * "IAC BREAK" command and the double IAC.
> +     * RFC854: "All TELNET commands consist of at least a two byte sequence.
> +     * The commands dealing with option negotiation are three byte sequences,
> +     * the third byte being the code for the option referenced."
> +     * "IAC BREAK", "IAC IP", "IAC NOP" and the double IAC are two bytes.
> +     * "IAC SB", "IAC SE" and "IAC EOR" are saved to split up data boundary
> +     * for tn3270.
> +     * NOP, Break and Interrupt Process(IP) might be encountered during a 
> TN3270
> +     * session, and NOP and IP need to be done later.
>       */
> 
>      int i;
> @@ -174,6 +181,18 @@ static void tcp_chr_process_IAC_bytes(Chardev *chr,
>                      /* Handle IAC break commands by sending a serial break */
>                      qemu_chr_be_event(chr, CHR_EVENT_BREAK);
>                      s->do_telnetopt++;
> +                } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_EOR
> +                           || (unsigned char)buf[i] == IAC_SB
> +                           || (unsigned char)buf[i] == IAC_SE)
> +                           && s->do_telnetopt == 2) {
> +                    buf[j++] = IAC;
> +                    buf[j++] = buf[i];
> +                    s->do_telnetopt++;
> +                } else if (s->is_tn3270 && ((unsigned char)buf[i] == IAC_IP
> +                           || (unsigned char)buf[i] == IAC_NOP)
> +                           && s->do_telnetopt == 2) {
> +                    /* TODO: IP and NOP need to be implemented later. */
> +                    s->do_telnetopt++;
>                  }
>                  s->do_telnetopt++;
>              }
> @@ -512,7 +531,7 @@ static void tcp_chr_update_read_handler(Chardev *chr,
> 
>  typedef struct {
>      Chardev *chr;
> -    char buf[12];
> +    char buf[21];
>      size_t buflen;
>  } TCPChardevTelnetInit;
> 
> @@ -550,9 +569,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
>      TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
>      size_t n = 0;
> 
> -    init->chr = chr;
> -    init->buflen = 12;
> -
>  #define IACSET(x, a, b, c)                      \
>      do {                                        \
>          x[n++] = a;                             \
> @@ -560,12 +576,26 @@ static void tcp_chr_telnet_init(Chardev *chr)
>          x[n++] = c;                             \
>      } while (0)
> 
> -    /* Prep the telnet negotion to put telnet in binary,
> -     * no echo, single char mode */
> -    IACSET(init->buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
> -    IACSET(init->buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
> -    IACSET(init->buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
> -    IACSET(init->buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
> +    init->chr = chr;
> +    if (!s->is_tn3270) {
> +        init->buflen = 12;
> +        /* Prep the telnet negotion to put telnet in binary,
> +         * no echo, single char mode */
> +        IACSET(init->buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
> +        IACSET(init->buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead 
> */
> +        IACSET(init->buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
> +        IACSET(init->buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
> +    } else {
> +        init->buflen = 21;
> +        /* Prep the TN3270 negotion based on RFC1576 */
> +        IACSET(init->buf, 0xff, 0xfd, 0x19);  /* IAC DO EOR */
> +        IACSET(init->buf, 0xff, 0xfb, 0x19);  /* IAC WILL EOR */
> +        IACSET(init->buf, 0xff, 0xfd, 0x00);  /* IAC DO BINARY */
> +        IACSET(init->buf, 0xff, 0xfb, 0x00);  /* IAC WILL BINARY */
> +        IACSET(init->buf, 0xff, 0xfd, 0x18);  /* IAC DO TERMINAL TYPE */
> +        IACSET(init->buf, 0xff, 0xfa, 0x18);  /* IAC SB TERMINAL TYPE */
> +        IACSET(init->buf, 0x01, 0xff, 0xf0);  /* SEND IAC SE */
> +    }
> 
>  #undef IACSET
> 
> @@ -585,7 +615,8 @@ static void tcp_chr_tls_handshake(QIOTask *task,
>      if (qio_task_propagate_error(task, NULL)) {
>          tcp_chr_disconnect(chr);
>      } else {
> -        if (s->do_telnetopt) {
> +        /* tn3270 does not support TLS yet */
> +        if (s->do_telnetopt && !s->is_tn3270) {
>              tcp_chr_telnet_init(chr);
>          } else {
>              tcp_chr_connect(chr);
> @@ -824,12 +855,14 @@ static void qmp_chardev_open_socket(Chardev *chr,
>      bool do_nodelay     = sock->has_nodelay ? sock->nodelay : false;
>      bool is_listen      = sock->has_server  ? sock->server  : true;
>      bool is_telnet      = sock->has_telnet  ? sock->telnet  : false;
> +    bool is_tn3270      = sock->has_tn3270  ? sock->tn3270  : false;
>      bool is_waitconnect = sock->has_wait    ? sock->wait    : false;
>      int64_t reconnect   = sock->has_reconnect ? sock->reconnect : 0;
>      QIOChannelSocket *sioc = NULL;
> 
>      s->is_listen = is_listen;
>      s->is_telnet = is_telnet;
> +    s->is_tn3270 = is_tn3270;
>      s->do_nodelay = do_nodelay;
>      if (sock->tls_creds) {
>          Object *creds;
> @@ -879,7 +912,7 @@ static void qmp_chardev_open_socket(Chardev *chr,
>                                           addr, is_listen, is_telnet);
> 
>      if (is_listen) {
> -        if (is_telnet) {
> +        if (is_telnet || is_tn3270) {
>              s->do_telnetopt = 1;
>          }
>      } else if (reconnect > 0) {
> @@ -933,6 +966,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, 
> ChardevBackend *backend,
>      bool is_listen      = qemu_opt_get_bool(opts, "server", false);
>      bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
>      bool is_telnet      = qemu_opt_get_bool(opts, "telnet", false);
> +    bool is_tn3270      = qemu_opt_get_bool(opts, "tn3270", false);
>      bool do_nodelay     = !qemu_opt_get_bool(opts, "delay", true);
>      int64_t reconnect   = qemu_opt_get_number(opts, "reconnect", 0);
>      const char *path = qemu_opt_get(opts, "path");
> @@ -968,6 +1002,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, 
> ChardevBackend *backend,
>      sock->server = is_listen;
>      sock->has_telnet = true;
>      sock->telnet = is_telnet;
> +    sock->has_tn3270 = true;
> +    sock->tn3270 = is_tn3270;
>      sock->has_wait = true;
>      sock->wait = is_waitconnect;
>      sock->has_reconnect = true;
> diff --git a/chardev/char.c b/chardev/char.c
> index 3df116350b..309734f2b7 100644
> --- a/chardev/char.c
> +++ b/chardev/char.c
> @@ -696,7 +696,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const 
> char *filename)
>          return opts;
>      }
>      if (strstart(filename, "tcp:", &p) ||
> -        strstart(filename, "telnet:", &p)) {
> +        strstart(filename, "telnet:", &p) ||
> +        strstart(filename, "tn3270:", &p)) {
>          if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
>              host[0] = 0;
>              if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
> @@ -712,8 +713,11 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const 
> char *filename)
>                  goto fail;
>              }
>          }
> -        if (strstart(filename, "telnet:", &p))
> +        if (strstart(filename, "telnet:", &p)) {
>              qemu_opt_set(opts, "telnet", "on", &error_abort);
> +        } else if (strstart(filename, "tn3270:", &p)) {
> +            qemu_opt_set(opts, "tn3270", "on", &error_abort);
> +        }
>          return opts;
>      }
>      if (strstart(filename, "udp:", &p)) {
> @@ -1177,6 +1181,9 @@ QemuOptsList qemu_chardev_opts = {
>              .name = "telnet",
>              .type = QEMU_OPT_BOOL,
>          },{
> +            .name = "tn3270",
> +            .type = QEMU_OPT_BOOL,
> +        },{
>              .name = "tls-creds",
>              .type = QEMU_OPT_STRING,
>          },{
> diff --git a/include/sysemu/char.h b/include/sysemu/char.h
> index 450881d42c..f6d5cd0c9b 100644
> --- a/include/sysemu/char.h
> +++ b/include/sysemu/char.h
> @@ -7,6 +7,14 @@
>  #include "qemu/bitmap.h"
>  #include "qom/object.h"
> 
> +#define IAC_EOR 239
> +#define IAC_SE 240
> +#define IAC_NOP 241
> +#define IAC_BREAK 243
> +#define IAC_IP 244
> +#define IAC_SB 250
> +#define IAC 255
> +
>  /* character device */
> 
>  typedef enum {
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 250e4dc49b..f0302958ce 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -4874,6 +4874,8 @@
>  # @nodelay: set TCP_NODELAY socket option (default: false)
>  # @telnet: enable telnet protocol on server
>  #          sockets (default: false)
> +# @tn3270: enable tn3270 protocol on server
> +#          sockets (default: false) (Since: 2.10)
>  # @reconnect: For a client socket, if a socket is disconnected,
>  #          then attempt a reconnect after the given number of seconds.
>  #          Setting this to zero disables this function. (default: 0)
> @@ -4887,6 +4889,7 @@
>                                       '*wait'      : 'bool',
>                                       '*nodelay'   : 'bool',
>                                       '*telnet'    : 'bool',
> +                                     '*tn3270'    : 'bool',
>                                       '*reconnect' : 'int' },
>    'base': 'ChardevCommon' }
> 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]