Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package microcom for openSUSE:Factory 
checked in at 2024-10-25 19:20:15
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/microcom (Old)
 and      /work/SRC/openSUSE:Factory/.microcom.new.2020 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "microcom"

Fri Oct 25 19:20:15 2024 rev:3 rq:1218364 version:2023.09.0.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/microcom/microcom.changes        2022-09-16 
13:32:51.577369269 +0200
+++ /work/SRC/openSUSE:Factory/.microcom.new.2020/microcom.changes      
2024-10-25 19:22:59.379002898 +0200
@@ -1,0 +2,8 @@
+Fri Oct 25 13:54:08 UTC 2024 - Matthias Brugger <mbrug...@suse.com>
+
+- Updat to version 2023.09.0.0
+  * use flock(2) instead of POSIX locking
+  * A new command sendescape to send a Ctrl-\. This makes it possible to drive 
a nested microcom instance.
+  * Several fixes, e.g. consistently use LFS and improve on rfc2217 
subnegotiation handling
+
+-------------------------------------------------------------------

Old:
----
  microcom-2019.01.0.11.tar

New:
----
  microcom-2023.09.0.0.tar

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ microcom.spec ++++++
--- /var/tmp/diff_new_pack.k5Vgu6/_old  2024-10-25 19:23:00.411045939 +0200
+++ /var/tmp/diff_new_pack.k5Vgu6/_new  2024-10-25 19:23:00.431046773 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           microcom
-Version:        2019.01.0.11
+Version:        2023.09.0.0
 Release:        0
 Summary:        Minimalistic terminal program
 License:        GPL-2.0-only

++++++ microcom-2019.01.0.11.tar -> microcom-2023.09.0.0.tar ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/.github/workflows/build.yml 
new/microcom-2023.09.0.0/.github/workflows/build.yml
--- old/microcom-2019.01.0.11/.github/workflows/build.yml       1970-01-01 
01:00:00.000000000 +0100
+++ new/microcom-2023.09.0.0/.github/workflows/build.yml        2023-09-07 
11:51:05.000000000 +0200
@@ -0,0 +1,38 @@
+---
+name: Build test
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Install Dependencies
+        run:
+          sudo apt install
+          libreadline6-dev
+          autoconf
+          automake
+
+      - name: Prepare (autoreconf)
+        run: autoreconf -i
+
+      - name: Prepare (configure)
+        run: ./configure
+
+      - name: Build
+        run: make
+
+      - name: Run check
+        run: make check
+
+      - name: Run distcheck
+        run: make distcheck
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/.travis.yml 
new/microcom-2023.09.0.0/.travis.yml
--- old/microcom-2019.01.0.11/.travis.yml       2022-09-07 12:24:58.000000000 
+0200
+++ new/microcom-2023.09.0.0/.travis.yml        1970-01-01 01:00:00.000000000 
+0100
@@ -1,7 +0,0 @@
-language: c
-compiler: gcc
-dist: trusty
-script:
-  - autoreconf -fi
-  - ./configure
-  - make distcheck
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/can.c 
new/microcom-2023.09.0.0/can.c
--- old/microcom-2019.01.0.11/can.c     2022-09-07 12:24:58.000000000 +0200
+++ new/microcom-2023.09.0.0/can.c      2023-09-07 11:51:05.000000000 +0200
@@ -14,6 +14,7 @@
  * GNU General Public License for more details at www.gnu.org
  *
  */
+#include "config.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -134,7 +135,6 @@
        ios->set_flow = can_set_flow;
        ios->send_break = can_send_break;
        ios->exit = can_exit;
-       ios->istelnet = false;
 
        /*
         * the string is supposed to be formated this way:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/commands.c 
new/microcom-2023.09.0.0/commands.c
--- old/microcom-2019.01.0.11/commands.c        2022-09-07 12:24:58.000000000 
+0200
+++ new/microcom-2023.09.0.0/commands.c 2023-09-07 11:51:05.000000000 +0200
@@ -6,6 +6,8 @@
  * Foundation; either version 2 of the License, or (at your option) any later
  * version.
  */
+#include "config.h"
+
 #include <stdlib.h>
 #include "microcom.h"
 
@@ -151,6 +153,12 @@
        return 0;
 }
 
+static int cmd_sendescape(int argc, char *argv[])
+{
+       ios->write(ios, "\x1c", 1);
+       return 0;
+}
+
 static int cmd_help(int argc, char *argv[])
 {
        struct cmd *cmd;
@@ -224,6 +232,10 @@
                .fn = cmd_break,
                .info = "send break",
        }, {
+               .name = "sendescape",
+               .fn = cmd_sendescape,
+               .info = "send a Ctrl-\\",
+       }, {
                .name = "quit",
                .fn = cmd_quit,
                .info = "quit microcom",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/commands_fsl_imx.c 
new/microcom-2023.09.0.0/commands_fsl_imx.c
--- old/microcom-2019.01.0.11/commands_fsl_imx.c        2022-09-07 
12:24:58.000000000 +0200
+++ new/microcom-2023.09.0.0/commands_fsl_imx.c 2023-09-07 11:51:05.000000000 
+0200
@@ -6,6 +6,8 @@
  * Foundation; either version 2 of the License, or (at your option) any later
  * version.
  */
+#include "config.h"
+
 #include <stdio.h>
 #include <sys/select.h>
 #include <stdint.h>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/configure.ac 
new/microcom-2023.09.0.0/configure.ac
--- old/microcom-2019.01.0.11/configure.ac      2022-09-07 12:24:58.000000000 
+0200
+++ new/microcom-2023.09.0.0/configure.ac       2023-09-07 11:51:05.000000000 
+0200
@@ -1,4 +1,4 @@
-AC_INIT([microcom], [2019.01.0], [oss-to...@pengutronix.de])
+AC_INIT([microcom], [2023.09.0], [oss-to...@pengutronix.de])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_MACRO_DIR([m4])
 AM_INIT_AUTOMAKE([dist-xz])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/microcom.h 
new/microcom-2023.09.0.0/microcom.h
--- old/microcom-2019.01.0.11/microcom.h        2022-09-07 12:24:58.000000000 
+0200
+++ new/microcom-2023.09.0.0/microcom.h 2023-09-07 11:51:05.000000000 +0200
@@ -51,7 +51,6 @@
        int (*set_handshake_line)(struct ios_ops *, int pin, int enable);
        int (*send_break)(struct ios_ops *);
        void (*exit)(struct ios_ops *);
-       bool istelnet;
        int fd;
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/mux.c 
new/microcom-2023.09.0.0/mux.c
--- old/microcom-2019.01.0.11/mux.c     2022-09-07 12:24:58.000000000 +0200
+++ new/microcom-2023.09.0.0/mux.c      2023-09-07 11:51:05.000000000 +0200
@@ -17,196 +17,13 @@
 ****************************************************************************
 ** Rev. 1.0 - Feb. 2000
 ****************************************************************************/
+#include "config.h"
+
 #include "microcom.h"
-#include <arpa/telnet.h>
-#include <arpa/inet.h>
-#include <stdarg.h>
 #include <stdbool.h>
 
 #define BUFSIZE 1024
 
-/* This is called with buf[-2:0] being IAC SB COM_PORT_OPTION */
-static int do_com_port_option(struct ios_ops *ios, unsigned char *buf, int len)
-{
-       int i = 2;
-
-       switch (buf[1]) {
-       case SET_BAUDRATE_CS:
-               dbg_printf("SET_BAUDRATE_CS ");
-               break;
-       case SET_DATASIZE_CS:
-               dbg_printf("SET_DATASIZE_CS ");
-               break;
-       case SET_PARITY_CS:
-               dbg_printf("SET_PARITY_CS ");
-               break;
-       case SET_STOPSIZE_CS:
-               dbg_printf("SET_STOPSIZE_CS ");
-               break;
-       case SET_CONTROL_CS:
-               dbg_printf("SET_CONTROL_CS ");
-               break;
-       case NOTIFY_LINESTATE_CS:
-               dbg_printf("NOTIFY_LINESTATE_CS ");
-               break;
-       case NOTIFY_MODEMSTATE_CS:
-               dbg_printf("NOTIFY_MODEMSTATE_CS ");
-               break;
-       case FLOWCONTROL_SUSPEND_CS:
-               dbg_printf("FLOWCONTROL_SUSPEND_CS ");
-               break;
-       case FLOWCONTROL_RESUME_CS:
-               dbg_printf("FLOWCONTROL_RESUME_CS ");
-               break;
-       case SET_LINESTATE_MASK_CS:
-               dbg_printf("SET_LINESTATE_MASK_CS ");
-               break;
-       case SET_MODEMSTATE_MASK_CS:
-               dbg_printf("SET_MODEMSTATE_MASK_CS ");
-               break;
-       case PURGE_DATA_CS:
-               dbg_printf("PURGE_DATA_CS ");
-               break;
-       case SET_BAUDRATE_SC:
-               dbg_printf("SET_BAUDRATE_SC %d ",
-                       buf[2] << 24 | buf[3] << 16 | buf[4] << 8 | buf[5]);
-               i += 4;
-               break;
-       case SET_DATASIZE_SC:
-               dbg_printf("SET_DATASIZE_SC ");
-               break;
-       case SET_PARITY_SC:
-               dbg_printf("SET_PARITY_SC ");
-               break;
-       case SET_STOPSIZE_SC:
-               dbg_printf("SET_STOPSIZE_SC ");
-               break;
-       case SET_CONTROL_SC:
-               i++;
-               dbg_printf("SET_CONTROL_SC 0x%02x ", buf[i]);
-               break;
-       case NOTIFY_LINESTATE_SC:
-               dbg_printf("NOTIFY_LINESTATE_SC ");
-               break;
-       case NOTIFY_MODEMSTATE_SC:
-               i++;
-               dbg_printf("NOTIFY_MODEMSTATE_SC 0x%02x ", buf[i]);
-               break;
-       case FLOWCONTROL_SUSPEND_SC:
-               dbg_printf("FLOWCONTROL_SUSPEND_SC ");
-               break;
-       case FLOWCONTROL_RESUME_SC:
-               dbg_printf("FLOWCONTROL_RESUME_SC ");
-               break;
-       case SET_LINESTATE_MASK_SC:
-               dbg_printf("SET_LINESTATE_MASK_SC ");
-               break;
-       case SET_MODEMSTATE_MASK_SC:
-               dbg_printf("SET_MODEMSTATE_MASK_SC ");
-               break;
-       case PURGE_DATA_SC:
-               dbg_printf("PURGE_DATA_SC ");
-               break;
-       default:
-               dbg_printf("??? %d ", buf[i]);
-               break;
-       }
-
-       while (i < len) {
-               if (buf[i] == IAC) {
-                       if (i + 1 < len && buf[i+1] == IAC) {
-                               /* quoted IAC -> unquote */
-                               ++i;
-                       } else if (i + 1 < len && buf[i+1] == SE) {
-                               dbg_printf("IAC SE\n");
-                               return i + 2;
-                       }
-               }
-               dbg_printf("%d ", buf[i]);
-
-               ++i;
-       }
-
-       fprintf(stderr, "Incomplete SB string\n");
-       return -EINVAL;
-}
-
-struct telnet_option {
-       unsigned char id;
-       const char *name;
-       int (*subneg_handler)(struct ios_ops *ios, unsigned char *buf, int len);
-       bool sent_will;
-};
-
-#define TELNET_OPTION(x)       .id = TELNET_OPTION_ ## x, .name = #x
-
-static const struct telnet_option telnet_options[] = {
-       {
-               TELNET_OPTION(COM_PORT_CONTROL),
-               .subneg_handler = do_com_port_option,
-               .sent_will = true,
-       }, {
-               TELNET_OPTION(BINARY_TRANSMISSION),
-       }, {
-               TELNET_OPTION(ECHO),
-       }, {
-               TELNET_OPTION(SUPPRESS_GO_AHEAD),
-       }
-};
-
-static const struct telnet_option *get_telnet_option(unsigned char id)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(telnet_options); ++i) {
-               if (id == telnet_options[i].id)
-                       return &telnet_options[i];
-       }
-
-       return NULL;
-}
-
-
-/* This function is called with buf[-2:-1] being IAC SB */
-static int do_subneg(struct ios_ops *ios, unsigned char *buf, int len)
-{
-       const struct telnet_option *option = get_telnet_option(buf[0]);
-
-       if (option)
-               dbg_printf("%s ", option->name);
-       if (option->subneg_handler) {
-               return option->subneg_handler(ios, buf, len);
-       } else {
-               /* skip over subneg string */
-               int i;
-               for (i = 0; i < len - 1; ++i) {
-                       if (buf[i] != IAC) {
-                               dbg_printf("%d ", buf[i]);
-                               continue;
-                       }
-
-                       if (buf[i + 1] == SE) {
-                               dbg_printf("IAC SE\n");
-                               return i + 1;
-                       }
-
-                       /* skip over IAC IAC */
-                       if (buf[i + 1] == IAC) {
-                               dbg_printf("%d \n", IAC);
-                               i++;
-                       }
-               }
-
-               /* the subneg string isn't finished yet */
-               if (i == len - 1)
-                       dbg_printf("%d", buf[i]);
-               dbg_printf("\\\n");
-               fprintf(stderr, "Incomplete SB string\n");
-
-               return -EINVAL;
-       }
-}
-
 static int logfd = -1;
 char *answerback;
 
@@ -220,123 +37,9 @@
                write(logfd, buf, len);
 }
 
-static int ios_printf(struct ios_ops *ios, const char *format, ...)
-{
-       char buf[20];
-       int size, written = 0;
-       ssize_t ret;
-       va_list args;
-
-       va_start(args, format);
-
-       size = vsnprintf(buf, sizeof(buf), format, args);
-
-       va_end(args);
-
-       if (size >= sizeof(buf)) {
-               /* truncated output */
-               errno = EIO;
-               return -1;
-       }
-
-       while (written < size) {
-               ret = ios->write(ios, buf + written, size - written);
-               if (ret < 0)
-                       return ret;
-
-               written += ret;
-               assert(written <= size);
-       }
-
-       return written;
-}
-
-/* This function is called with buf[0] being IAC. */
-static int handle_command(struct ios_ops *ios, unsigned char *buf, int len)
-{
-       int ret;
-       const struct telnet_option *option;
-
-       switch (buf[1]) {
-       case SB:
-               dbg_printf("SB ");
-               ret = do_subneg(ios, &buf[2], len - 2);
-               if (ret < 0)
-                       return ret;
-               return ret + 2;
-
-       case IAC:
-               /* escaped IAC -> unescape */
-               write_receive_buf(&buf[1], 1);
-               return 2;
-
-       case WILL:
-               option = get_telnet_option(buf[2]);
-               if (option)
-                       dbg_printf("WILL %s", option->name);
-               else
-                       dbg_printf("WILL #%d", buf[2]);
-
-               if (option && option->subneg_handler) {
-                       /* ok, we already requested that, so take this as
-                        * confirmation to actually do COM_PORT stuff.
-                        * Everything is fine. Don't reconfirm to prevent an
-                        * request/confirm storm.
-                        */
-                       dbg_printf("\n");
-               } else {
-                       /* unknown/unimplemented option -> DONT */
-                       dbg_printf(" -> DONT\n");
-                       ios_printf(ios, "%c%c%c", IAC, DONT, buf[2]);
-               }
-               return 3;
-
-       case WONT:
-               option = get_telnet_option(buf[2]);
-               if (option)
-                       dbg_printf("WONT %s\n", option->name);
-               else
-                       dbg_printf("WONT #%d\n", buf[2]);
-               return 3;
-
-       case DO:
-               option = get_telnet_option(buf[2]);
-               if (option)
-                       dbg_printf("DO %s", option->name);
-               else
-                       dbg_printf("DO #%d", buf[2]);
-
-               if (option && option->sent_will) {
-                       /*
-                        * This is a confirmation of an WILL sent by us before.
-                        * There is nothing to do now.
-                        */
-                       dbg_printf("\n");
-               } else {
-                       /* Oh, cannot handle that one, so send a WONT */
-                       dbg_printf(" -> WONT\n");
-                       ios_printf(ios, "%c%c%c", IAC, WONT, buf[2]);
-               }
-               return 3;
-
-       case DONT:
-               option = get_telnet_option(buf[2]);
-               if (option)
-                       dbg_printf("DONT %s\n", option->name);
-               else
-                       dbg_printf("DONT #%d\n", buf[2]);
-               return 3;
-
-       default:
-               dbg_printf("??? %d\n", buf[1]);
-               return 1;
-       }
-}
-
 static int handle_receive_buf(struct ios_ops *ios, unsigned char *buf, int len)
 {
        unsigned char *sendbuf = buf;
-       int i;
 
        while (len) {
                switch (*buf) {
@@ -351,19 +54,6 @@
                        len -= 1;
                        sendbuf = buf;
                        break;
-               case IAC:
-                       if (ios->istelnet) {
-                               write_receive_buf(sendbuf, buf - sendbuf);
-                               i = handle_command(ios, buf, len);
-                               if (i < 0)
-                                       return i;
-
-                               buf += i;
-                               len -= i;
-                               sendbuf = buf;
-                               break;
-                       }
-                       /* fall through */
                default:
                        buf += 1;
                        len -= 1;
@@ -447,19 +137,20 @@
                        /* pf has characters for us */
                        len = ios->read(ios, buf, BUFSIZE);
                        if (len < 0) {
-                               ret = -errno;
-                               fprintf(stderr, "%s\n", strerror(-ret));
-                               return ret;
-                       }
-                       if (len == 0) {
+                               if (errno != EAGAIN && errno != EWOULDBLOCK) {
+                                       ret = -errno;
+                                       fprintf(stderr, "%s\n", strerror(-ret));
+                                       return ret;
+                               }
+                       } else if (len == 0) {
                                fprintf(stderr, "Got EOF from port\n");
                                return -EINVAL;
-                       }
-
-                       i = handle_receive_buf(ios, buf, len);
-                       if (i < 0) {
-                               fprintf(stderr, "%s\n", strerror(-i));
-                               return i;
+                       } else {
+                               i = handle_receive_buf(ios, buf, len);
+                               if (i < 0) {
+                                       fprintf(stderr, "%s\n", strerror(-i));
+                                       return i;
+                               }
                        }
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/parser.c 
new/microcom-2023.09.0.0/parser.c
--- old/microcom-2019.01.0.11/parser.c  2022-09-07 12:24:58.000000000 +0200
+++ new/microcom-2023.09.0.0/parser.c   2023-09-07 11:51:05.000000000 +0200
@@ -6,6 +6,8 @@
  * Foundation; either version 2 of the License, or (at your option) any later
  * version.
  */
+#include "config.h"
+
 #include <stdio.h>
 #include <readline/readline.h>
 #include <readline/history.h>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/serial.c 
new/microcom-2023.09.0.0/serial.c
--- old/microcom-2019.01.0.11/serial.c  2022-09-07 12:24:58.000000000 +0200
+++ new/microcom-2023.09.0.0/serial.c   2023-09-07 11:51:05.000000000 +0200
@@ -19,6 +19,7 @@
 ** Rev. 1.01 - March 2000
 ** Rev. 1.02 - June 2000
 ****************************************************************************/
+#include "config.h"
 
 #include <limits.h>
 #include <sys/file.h>
@@ -203,9 +204,20 @@
 
 static int serial_send_break(struct ios_ops *ios)
 {
-       tcsendbreak(ios->fd, 0);
+       int ret;
+       struct timeval delay = {
+               .tv_sec = 0,
+               .tv_usec = 400000,
+       };
 
-       return 0;
+       ret = ioctl(ios->fd, TIOCSBRK, NULL);
+       if (ret < 0)
+               return ret;
+
+       select(0, NULL, NULL, NULL, &delay);
+
+       ret = ioctl(ios->fd, TIOCCBRK, NULL);
+       return ret;
 }
 
 /* restore original terminal settings on exit */
@@ -233,7 +245,6 @@
        ops->set_handshake_line = serial_set_handshake_line;
        ops->send_break = serial_send_break;
        ops->exit = serial_exit;
-       ops->istelnet = false;
 
        /* open the device */
        fd = open(device, O_RDWR | O_NONBLOCK);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/microcom-2019.01.0.11/telnet.c 
new/microcom-2023.09.0.0/telnet.c
--- old/microcom-2019.01.0.11/telnet.c  2022-09-07 12:24:58.000000000 +0200
+++ new/microcom-2023.09.0.0/telnet.c   2023-09-07 11:51:05.000000000 +0200
@@ -15,6 +15,7 @@
 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ** GNU General Public License for more details at www.gnu.org
 ****************************************************************************/
+#include "config.h"
 
 #include <stdlib.h>
 #include <arpa/telnet.h>
@@ -23,28 +24,464 @@
 #include <netdb.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <stdarg.h>
+#include <string.h>
 
 #include "microcom.h"
 
+static int telnet_printf(struct ios_ops *ios, const char *format, ...)
+{
+       char buf[20];
+       int size, written = 0;
+       ssize_t ret;
+       va_list args;
+
+       va_start(args, format);
+
+       size = vsnprintf(buf, sizeof(buf), format, args);
+
+       va_end(args);
+
+       if (size >= sizeof(buf)) {
+               /* truncated output */
+               errno = EIO;
+               return -1;
+       }
+
+       while (written < size) {
+               ret = write(ios->fd, buf + written, size - written);
+               if (ret < 0)
+                       return ret;
+
+               written += ret;
+               assert(written <= size);
+       }
+
+       return written;
+}
+
+static ssize_t get(unsigned char *buf, unsigned char *out, size_t len)
+{
+       if (!len)
+               return -1;
+
+       if (buf[0] == IAC) {
+               if (len < 1)
+                       return -1;
+               if (buf[1] == IAC) {
+                       *out = IAC;
+                       return 2;
+               }
+               return -1;
+       } else {
+               *out = buf[0];
+               return 1;
+       }
+}
+
+static size_t getl(unsigned char *buf, uint32_t *out, size_t len)
+{
+       *out = 0;
+       int i;
+       size_t offset = 0;
+
+       for (i = 0; i < 4; ++i) {
+               ssize_t getres;
+               unsigned char c;
+
+               getres = get(buf + offset, &c, len - offset);
+               if (getres < 0)
+                       return getres;
+
+               *out <<= 8;
+               *out |= c;
+
+               offset += getres;
+       }
+
+       return offset;
+}
+
+/* This is called with buf[-2:0] being IAC SB COM_PORT_OPTION */
+static int do_com_port_option(struct ios_ops *ios, unsigned char *buf, int len)
+{
+       int i = 2;
+
+       switch (buf[1]) {
+       case SET_BAUDRATE_CS:
+               dbg_printf("SET_BAUDRATE_CS ");
+               break;
+       case SET_DATASIZE_CS:
+               dbg_printf("SET_DATASIZE_CS ");
+               break;
+       case SET_PARITY_CS:
+               dbg_printf("SET_PARITY_CS ");
+               break;
+       case SET_STOPSIZE_CS:
+               dbg_printf("SET_STOPSIZE_CS ");
+               break;
+       case SET_CONTROL_CS:
+               dbg_printf("SET_CONTROL_CS ");
+               break;
+       case NOTIFY_LINESTATE_CS:
+               dbg_printf("NOTIFY_LINESTATE_CS ");
+               break;
+       case NOTIFY_MODEMSTATE_CS:
+               dbg_printf("NOTIFY_MODEMSTATE_CS ");
+               break;
+       case FLOWCONTROL_SUSPEND_CS:
+               dbg_printf("FLOWCONTROL_SUSPEND_CS ");
+               break;
+       case FLOWCONTROL_RESUME_CS:
+               dbg_printf("FLOWCONTROL_RESUME_CS ");
+               break;
+       case SET_LINESTATE_MASK_CS:
+               dbg_printf("SET_LINESTATE_MASK_CS ");
+               break;
+       case SET_MODEMSTATE_MASK_CS:
+               dbg_printf("SET_MODEMSTATE_MASK_CS ");
+               break;
+       case PURGE_DATA_CS:
+               dbg_printf("PURGE_DATA_CS ");
+               break;
+       case SET_BAUDRATE_SC:
+               {
+                       uint32_t baudrate;
+                       ssize_t getres = getl(buf + 2, &baudrate, len - 2);
+
+                       if (getres < 0) {
+                               fprintf(stderr, "Incomplete or broken SB 
(SET_BAUDRATE_SC)\n");
+                               return getres;
+                       }
+                       dbg_printf("SET_BAUDRATE_SC %u ", baudrate);
+                       i += getres;;
+               }
+               break;
+       case SET_DATASIZE_SC:
+               dbg_printf("SET_DATASIZE_SC ");
+               break;
+       case SET_PARITY_SC:
+               dbg_printf("SET_PARITY_SC ");
+               break;
+       case SET_STOPSIZE_SC:
+               dbg_printf("SET_STOPSIZE_SC ");
+               break;
+       case SET_CONTROL_SC:
+               {
+                       unsigned char ctrl;
+                       ssize_t getres = get(buf + 2, &ctrl, len - 2);
+
+                       if (getres < 0) {
+                               fprintf(stderr, "Incomplete or broken SB 
(SET_CONTROL_SC)\n");
+                               return getres;
+                       }
+
+                       dbg_printf("SET_CONTROL_SC 0x%02x ", ctrl);
+                       i += getres;
+               }
+               break;
+       case NOTIFY_LINESTATE_SC:
+               dbg_printf("NOTIFY_LINESTATE_SC ");
+               break;
+       case NOTIFY_MODEMSTATE_SC:
+               {
+                       unsigned char ms;
+                       ssize_t getres = get(buf + 2, &ms, len - 2);
+
+                       if (getres < 0) {
+                               fprintf(stderr, "Incomplete or broken SB 
(NOTIFY_MODEMSTATE_SC)\n");
+                               return getres;
+                       }
+
+                       dbg_printf("NOTIFY_MODEMSTATE_SC 0x%02x ", ms);
+                       i += getres;
+               }
+       case FLOWCONTROL_SUSPEND_SC:
+               dbg_printf("FLOWCONTROL_SUSPEND_SC ");
+               break;
+       case FLOWCONTROL_RESUME_SC:
+               dbg_printf("FLOWCONTROL_RESUME_SC ");
+               break;
+       case SET_LINESTATE_MASK_SC:
+               dbg_printf("SET_LINESTATE_MASK_SC ");
+               break;
+       case SET_MODEMSTATE_MASK_SC:
+               dbg_printf("SET_MODEMSTATE_MASK_SC ");
+               break;
+       case PURGE_DATA_SC:
+               dbg_printf("PURGE_DATA_SC ");
+               break;
+       default:
+               dbg_printf("??? %d ", buf[1]);
+               break;
+       }
+
+       while (i < len) {
+               if (buf[i] == IAC) {
+                       if (i + 1 < len && buf[i+1] == IAC) {
+                               /* quoted IAC -> unquote */
+                               ++i;
+                       } else if (i + 1 < len && buf[i+1] == SE) {
+                               dbg_printf("IAC SE\n");
+                               return i + 2;
+                       }
+               }
+               dbg_printf("%d ", buf[i]);
+
+               ++i;
+       }
+
+       fprintf(stderr, "Incomplete SB string\n");
+       return -EINVAL;
+}
+
+/* This is called with buf[-2:0] being IAC SB COM_PORT_OPTION */
+static int do_binary_transmission_option(struct ios_ops *ios, unsigned char 
*buf, int len)
+{
+       /* There are no subcommands for the BINARY_TRANSMISSION option (rfc856) 
*/
+       return -EINVAL;
+}
+
+struct telnet_option {
+       unsigned char id;
+       const char *name;
+       int (*subneg_handler)(struct ios_ops *ios, unsigned char *buf, int len);
+       bool sent_will;
+};
+
+#define TELNET_OPTION(x)       .id = TELNET_OPTION_ ## x, .name = #x
+
+static const struct telnet_option telnet_options[] = {
+       {
+               TELNET_OPTION(COM_PORT_CONTROL),
+               .subneg_handler = do_com_port_option,
+               .sent_will = true,
+       }, {
+               TELNET_OPTION(BINARY_TRANSMISSION),
+               .subneg_handler = do_binary_transmission_option,
+               .sent_will = true,
+       }, {
+               TELNET_OPTION(ECHO),
+       }, {
+               TELNET_OPTION(SUPPRESS_GO_AHEAD),
+       }
+};
+
+static const struct telnet_option *get_telnet_option(unsigned char id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(telnet_options); ++i) {
+               if (id == telnet_options[i].id)
+                       return &telnet_options[i];
+       }
+
+       return NULL;
+}
+
+
+/* This function is called with buf[-2:-1] being IAC SB */
+static int do_subneg(struct ios_ops *ios, unsigned char *buf, int len)
+{
+       const struct telnet_option *option = get_telnet_option(buf[0]);
+
+       if (option)
+               dbg_printf("%s ", option->name);
+       if (option->subneg_handler) {
+               return option->subneg_handler(ios, buf, len);
+       } else {
+               /* skip over subneg string */
+               int i;
+               for (i = 0; i < len - 1; ++i) {
+                       if (buf[i] != IAC) {
+                               dbg_printf("%d ", buf[i]);
+                               continue;
+                       }
+
+                       if (buf[i + 1] == SE) {
+                               dbg_printf("IAC SE\n");
+                               return i + 1;
+                       }
+
+                       /* skip over IAC IAC */
+                       if (buf[i + 1] == IAC) {
+                               dbg_printf("%d \n", IAC);
+                               i++;
+                       }
+               }
+
+               /* the subneg string isn't finished yet */
+               if (i == len - 1)
+                       dbg_printf("%d", buf[i]);
+               dbg_printf("\\\n");
+               fprintf(stderr, "Incomplete SB string\n");
+
+               return -EINVAL;
+       }
+}
+
+/* This function is called with buf[0] being IAC. */
+static int handle_command(struct ios_ops *ios, unsigned char *buf, int len)
+{
+       int ret;
+       const struct telnet_option *option;
+
+       /* possible out-of-bounds access */
+       switch (buf[1]) {
+       case SB:
+               dbg_printf("SB ");
+               ret = do_subneg(ios, &buf[2], len - 2);
+               if (ret < 0)
+                       return ret;
+               return ret + 2;
+
+       case WILL:
+               option = get_telnet_option(buf[2]);
+               if (option)
+                       dbg_printf("WILL %s", option->name);
+               else
+                       dbg_printf("WILL #%d", buf[2]);
+
+               if (option && option->subneg_handler) {
+                       /* ok, we already requested that, so take this as
+                        * confirmation to actually do COM_PORT stuff.
+                        * Everything is fine. Don't reconfirm to prevent an
+                        * request/confirm storm.
+                        */
+                       dbg_printf("\n");
+               } else {
+                       /* unknown/unimplemented option -> DONT */
+                       dbg_printf(" -> DONT\n");
+                       telnet_printf(ios, "%c%c%c", IAC, DONT, buf[2]);
+               }
+               return 3;
+
+       case WONT:
+               option = get_telnet_option(buf[2]);
+               if (option)
+                       dbg_printf("WONT %s\n", option->name);
+               else
+                       dbg_printf("WONT #%d\n", buf[2]);
+               return 3;
+
+       case DO:
+               option = get_telnet_option(buf[2]);
+               if (option)
+                       dbg_printf("DO %s", option->name);
+               else
+                       dbg_printf("DO #%d", buf[2]);
+
+               if (option && option->sent_will) {
+                       /*
+                        * This is a confirmation of an WILL sent by us before.
+                        * There is nothing to do now.
+                        */
+                       dbg_printf("\n");
+               } else {
+                       /* Oh, cannot handle that one, so send a WONT */
+                       dbg_printf(" -> WONT\n");
+                       telnet_printf(ios, "%c%c%c", IAC, WONT, buf[2]);
+               }
+               return 3;
+
+       case DONT:
+               option = get_telnet_option(buf[2]);
+               if (option)
+                       dbg_printf("DONT %s\n", option->name);
+               else
+                       dbg_printf("DONT #%d\n", buf[2]);
+               return 3;
+
+       default:
+               dbg_printf("??? %d\n", buf[1]);
+               return 1;
+       }
+}
+
 static ssize_t telnet_write(struct ios_ops *ios, const void *buf, size_t count)
 {
-       return write(ios->fd, buf, count);
+       size_t handled = 0;
+       ssize_t ret;
+       void *iac;
+
+       /*
+        * To send an IAC character in the data stream, two IACs must be sent.
+        * So find the first IAC in the data to be send (if any), send the data
+        * before that IAC unquoted, then send the double IAC. Repeat until
+        * all IACs are handled.
+        */
+       while ((iac = memchr(buf + handled, IAC, count - handled)) != NULL) {
+               if (iac - (buf + handled)) {
+                       ret = write(ios->fd, buf + handled, iac - (buf + 
handled));
+                       if (ret < 0)
+                               return ret;
+                       handled += ret;
+               } else {
+                       dprintf(ios->fd, "%c%c", IAC, IAC);
+                       handled += 1;
+               }
+       }
+
+       /* Send the remaining data that needs no quoting. */
+       ret = write(ios->fd, buf + handled, count - handled);
+       if (ret < 0)
+               return ret;
+       return ret + handled;
 }
 
 static ssize_t telnet_read(struct ios_ops *ios, void *buf, size_t count)
 {
-       return read(ios->fd, buf, count);
+       ssize_t ret = read(ios->fd, buf, count);
+       void *iac;
+       size_t handled = 0;
+
+       if (ret <= 0)
+               return ret;
+
+       while ((iac = memchr(buf + handled, IAC, ret - handled)) != NULL) {
+               handled = iac - buf;
+
+               /* XXX: possible out-of-bounds access */
+               if (((unsigned char *)iac)[1] == IAC) {
+                       /* duplicated IAC = one payload IAC */
+                       ret -= 1;
+                       memmove(iac, iac + 1, ret - (iac - buf));
+                       handled += 1;
+               } else {
+                       int iaclen = handle_command(ios, iac, ret - handled);
+
+                       if (iaclen < 0)
+                               return iaclen;
+
+                       memmove(iac, iac + iaclen, ret - (handled + iaclen));
+                       ret -= iaclen;
+               }
+       }
+       if (ret) {
+               return ret;
+       } else {
+               errno = EAGAIN;
+               return -1;
+       }
 }
 
 static int telnet_set_speed(struct ios_ops *ios, unsigned long speed)
 {
+       unsigned char buf2[14] = {IAC, SB, TELNET_OPTION_COM_PORT_CONTROL, 
SET_BAUDRATE_CS};
+       size_t offset = 4;
+       int i;
+
+       for (i = 0; i < 4; ++i) {
+               buf2[offset] = (speed >> (24 - 8 * i)) & 0xff;
+               if (buf2[offset++] == IAC)
+                       buf2[offset++] = IAC;
+       }
 
-       unsigned char buf2[] = {IAC, SB, TELNET_OPTION_COM_PORT_CONTROL, 
SET_BAUDRATE_CS, 0, 0, 0, 0, IAC, SE};
-       int *speedp = (int *)&buf2[4];
+       buf2[offset++] = IAC;
+       buf2[offset++] = SE;
 
-       *speedp = htonl(speed);
        dbg_printf("-> IAC SB COM_PORT_CONTROL SET_BAUDRATE_CS 0x%lx IAC SE\n", 
speed);
-       write(ios->fd, buf2, 10);
+       write(ios->fd, buf2, offset);
 
        return 0;
 }
@@ -108,7 +545,6 @@
        ios->set_flow = telnet_set_flow;
        ios->send_break = telnet_send_break;
        ios->exit = telnet_exit;
-       ios->istelnet = true;
 
        memset(&hints, '\0', sizeof(hints));
        hints.ai_flags = AI_ADDRCONFIG;
@@ -169,11 +605,13 @@
                }
                printf("connected to %s (port %s)\n", connected_host, 
connected_port);
 
-               /* send intent to do and accept COM_PORT stuff */
+               /* send intent we WILL do COM_PORT stuff */
                dbg_printf("-> WILL COM_PORT_CONTROL\n");
                dprintf(sock, "%c%c%c", IAC, WILL, 
TELNET_OPTION_COM_PORT_CONTROL);
-               dbg_printf("-> DO COM_PORT_CONTROL\n");
-               dprintf(sock, "%c%c%c", IAC, DO, 
TELNET_OPTION_COM_PORT_CONTROL);
+               dbg_printf("-> DO BINARY_TRANSMISSION\n");
+               dprintf(sock, "%c%c%c", IAC, DO, 
TELNET_OPTION_BINARY_TRANSMISSION);
+               dbg_printf("-> WILL BINARY_TRANSMISSION\n");
+               dprintf(sock, "%c%c%c", IAC, WILL, 
TELNET_OPTION_BINARY_TRANSMISSION);
                goto out;
        }
 

Reply via email to