Experimenteller Support fuer Telnetoption START_TLS.

Diese Telnetoption kann von vom Client benutzt werden,
um eine unsichere Verbindung zu einer TLS-Verbindung
zu machen. Nach Aktivierung beim Client synchronieren
sich Server und Client und starten dann die TLS-
Verhandlung. Anschliessend wird die nun TLS-
Verbindung als neue Verbindung behandelt und der
Login-Prozess neu gestartet.

Change-Id: I9bf79d611311c46b1ef8317e26806a95fc94379d
diff --git a/secure/telnetneg.c b/secure/telnetneg.c
index 42d18c4..1b175a5 100644
--- a/secure/telnetneg.c
+++ b/secure/telnetneg.c
@@ -511,7 +511,7 @@
   }
 }
 
-// Der Handler fuer die BINARY option, wenn sie auf/fuer unserere Seite
+// Der Handler fuer die CHARSET option, wenn sie auf/fuer unserere Seite
 // aktiviert/deaktivert wird.
 private void _std_lo_handler_charset(struct telopt_s opt, int action,
                                    int *data)
@@ -544,6 +544,58 @@
 #undef TTABLE-IS
 #undef TTABLE-REJECTED
 
+// Called from the telnetneg handler for TELOPT_STARTTLS to initiate the TLS
+// connection negotiation.
+protected void init_tls()
+{
+  // Dabei muss unser ganzer Telnet-Option-State muss zurueckgesetzt werden.
+  // Ja, wirklich! (Keine Sorge, der client muss das auch tun.)
+  TN = ([]);
+}
+
+#ifdef __TLS__
+// Der Handler fuer STARTTLS, wenn es auf der Clientseite
+// deaktiviert/aktiviert wird. Es wird nur auf der Clientseite aktiviert, der
+// Server darf kein WILL senden. Nach Aktivierung muessen wir ein FOLLOWS
+// senden.
+#define FOLLOWS 1
+private void _std_re_handler_starttls(struct telopt_s opt, int action,
+                                      int *data)
+{
+  DTN("starttls handler client",({action}));
+
+  // Wenn action == REMOTEON: Ab diesem Moment darf uns der Client einen
+  // STARTTLS FOLLOWS senden (weil wir haben ihm auch schon ein DO
+  // geschickt). Wir sollen ihm aber jetzt auch ein FOLLOWS senden. Sobald wir
+  // das gesendet haben und ein FOLLOWS erhalten haben, geht die Negotiation
+  // los.
+  if (action  == REMOTEON)
+  {
+    send_telnet_neg(({ SB, TELOPT_STARTTLS, FOLLOWS }));
+    opt->data = 1; // Nur ein Flag, dass wir es gesendet haben.
+  }
+  else if (action == REMOTEOFF)
+  {
+    // data zuruecksetzen, sonst muessen wir nix machen.
+    opt->data = 0;
+  }
+  else if (action == SB)
+  {
+    if (data[0] == FOLLOWS)
+    {
+      // FOLLOWS empfangen. Wenn wir noch kein FOLLOWS gesendet haben, tun wir
+      // das jetzt.
+      if (!opt->data)
+        send_telnet_neg(({ SB, TELOPT_STARTTLS, FOLLOWS }));
+      // Jetzt wird die Verhandlung auf unserer Seite gestartet, der Client
+      // macht das entweder schon oder spaetestens, wenn er unser FOLLOWS
+      // empfangen kann.
+      init_tls();
+    }
+  }
+}
+#undef FOLLOWS
+#endif // __TLS__
 
 // Bindet/registriert Handler fuer die jew. Telnet Option. (Oder loescht sie
 // auch wieder.) Je nach <initneg> wird versucht, die Option neu zu
@@ -593,6 +645,15 @@
 //            laufen.
 protected void SendTelopts()
 {
+#if __TLS__
+  // If this is a non-TLS-connection, we offer STARTTLS, but wait for the
+  // client to ask for it.
+  if (tls_available() && tls_query_connection_state() == 0)
+  {
+    bind_telneg_handler(TELOPT_STARTTLS, #'_std_re_handler_starttls,
+                        0, 0);
+  }
+#endif
   bind_telneg_handler(TELOPT_BINARY, #'_std_re_handler_binary,
                       #'_std_lo_handler_binary, 1);
   bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 1);
@@ -604,12 +665,14 @@
   // und auch CHARSET wird verzoegert bis das Spielerobjekt da ist.
 }
 
-
 // Bindet die Standardhandler _aus diesem_ Programm (und ueberschreibt dabei
 // ggf. andere). Hierbei werden nur die Handler neu gebunden, keine neuen
 // Verhandlungen initiiert.
 // gerufen aus base.c indirekt via startup_telnet_negs().
-protected void _bind_telneg_std_handlers() {
+protected void _bind_telneg_std_handlers()
+{
+  // BTW: es ist absicht, im Spielerobjekt keinen Support fuer STARTTLS mehr
+  // anzubieten.
   bind_telneg_handler(TELOPT_BINARY, #'_std_re_handler_binary,
                       #'_std_lo_handler_binary, 0);
   bind_telneg_handler(TELOPT_EOR, 0, #'_std_lo_handler_eor, 0);