ldmud-3.2.9/doc/
ldmud-3.2.9/doc/efun/
ldmud-3.2.9/mud/
ldmud-3.2.9/mud/heaven7/
ldmud-3.2.9/mud/heaven7/lib/
ldmud-3.2.9/mud/lp-245/
ldmud-3.2.9/mud/lp-245/banish/
ldmud-3.2.9/mud/lp-245/doc/
ldmud-3.2.9/mud/lp-245/doc/examples/
ldmud-3.2.9/mud/lp-245/doc/sefun/
ldmud-3.2.9/mud/lp-245/log/
ldmud-3.2.9/mud/lp-245/obj/Go/
ldmud-3.2.9/mud/lp-245/players/lars/
ldmud-3.2.9/mud/lp-245/room/death/
ldmud-3.2.9/mud/lp-245/room/maze1/
ldmud-3.2.9/mud/lp-245/room/sub/
ldmud-3.2.9/mud/lp-245/secure/
ldmud-3.2.9/mud/morgengrauen/
ldmud-3.2.9/mud/morgengrauen/lib/
ldmud-3.2.9/mud/sticklib/
ldmud-3.2.9/mud/sticklib/src/
ldmud-3.2.9/mudlib/uni-crasher/
ldmud-3.2.9/pkg/
ldmud-3.2.9/pkg/debugger/
ldmud-3.2.9/pkg/diff/
ldmud-3.2.9/pkg/misc/
ldmud-3.2.9/src/autoconf/
ldmud-3.2.9/src/bugs/
ldmud-3.2.9/src/bugs/MudCompress/
ldmud-3.2.9/src/bugs/b-020916-files/
ldmud-3.2.9/src/bugs/doomdark/
ldmud-3.2.9/src/bugs/ferrycode/ferry/
ldmud-3.2.9/src/bugs/ferrycode/obj/
ldmud-3.2.9/src/bugs/psql/
ldmud-3.2.9/src/done/
ldmud-3.2.9/src/done/order_alist/
ldmud-3.2.9/src/done/order_alist/obj/
ldmud-3.2.9/src/done/order_alist/room/
ldmud-3.2.9/src/gcc/
ldmud-3.2.9/src/gcc/2.7.0/
ldmud-3.2.9/src/gcc/2.7.1/
ldmud-3.2.9/src/hosts/
ldmud-3.2.9/src/hosts/GnuWin32/
ldmud-3.2.9/src/hosts/amiga/NetIncl/
ldmud-3.2.9/src/hosts/amiga/NetIncl/netinet/
ldmud-3.2.9/src/hosts/amiga/NetIncl/sys/
ldmud-3.2.9/src/hosts/i386/
ldmud-3.2.9/src/hosts/msdos/byacc/
ldmud-3.2.9/src/hosts/msdos/doc/
ldmud-3.2.9/src/hosts/os2/
ldmud-3.2.9/src/hosts/win32/
ldmud-3.2.9/src/util/
ldmud-3.2.9/src/util/erq/
ldmud-3.2.9/src/util/indent/hosts/next/
ldmud-3.2.9/src/util/xerq/
ldmud-3.2.9/src/util/xerq/lpc/
ldmud-3.2.9/src/util/xerq/lpc/www/
Short: Windows clients cause telnet malfunction
Date: Wed, 10 May 2000 17:12:43 +0200
From: Heiko Kopp <hkopp@informatik.uni-rostock.de>
Type: Done - Mudlib error
State: New
See also: b-000508

The problem was in the login.c which used its own, faulty telnet_neg instead
of the global correct one.

------------

Hallo Lars,

wir haben ein kleines unangenehmes Problem das in Falle von
Evermore dazu fuehrt, das die Rechentechnik in Tuebingen wo
das Mud laeuft uns die Tueren einrennt. Seit einiger Zeit
(ich kann nicht genau sagen seid wann, es kann auch schon
laenger her sein) scheint es so, das Evermore, wenn es denn
von einem Windows Client benutzt wird, immense Mengen an
Acknoledges sendet und empfaengt. Ein Spieler hat uns darauf
aufmerksam gemacht und ich hab das selbst auch schon gemerkt.
So sendet der Client innerhalb einer Stunde ueber 1 MByte
Daten an das Mud.

Folgende tcptrace zeigt dies ganz deutlich:

1: fangorn:telnet - pcoldag.iap-kborn.de:4572 (a2b)       1048> 1033<
2: cks1.rz.uni-rostock.de:3654 - fangorn:5555 (e2f)         10>    7<

1 ist ein Windows Rechner, 2 eine IBM RS/6000

(das ganze waren ca. 15 Sekunden tcpdump)

Ich bin mir fast sicher, dass das ganze an Evermore liegt, denn eine
parallele Entwicklung auf NATIVE basis zeigt das Problem (noch) nicht.
Wir sind hier ziemlich ratlos woran das liegen koennte. Hast du denn
vielleicht eine Idee?
Sollten wir das Problem nicht loesen koennen wuerde das dazu fuehren,
das Evermore vom Netz muss.... *sigh deeply*


>Ist das ein spezieller Client, oder passiert das bei jedem Windows-
>Client?

Ich habe das Problem bis jetzt bei ZMud beobachtet, und selbst ein
simples Telnet unter Windows aendert nichts. 
Es gibt aber einige Dinge die ich berichten kann. Evermore ist zur
Zeit auf dem Weg von COMPAT zu NATIVE. Daher existiert bereits eine
simplere Architektur die komplett native ist. Der Player und viele
Libfunktionen funktionieren schon, so dass ich mit dieser Version
testen konnte. Und siehe da, das Problem tritt dort nicht mehr auf.
Ausserdem sieht es so aus, dass bestimmte Spieler nochmals deutlich
mehr Traffic erzeugen, als andere die auch Windows benutzen. So hatte
ich heute einen Spieler der hat 5x mehr Traffic mit Win98 produziert
als ich mit Win2000.

>Was schoen waere, waere eine Beschreibung wie man dieses 
>Fehlverhalten gezielt produziert, sowie ein Log der dabei gesendeten 
>Daten speziell vom Anfang des Fehlverhaltens.

Das ist mein Problem, ich hab keine Ahnung wie ich genau sehen, welche
Daten gesendet werden. Ich habe bisher nur ein tcpdump gemacht. Gibt 
es eine Moeglichkeit direkt die Daten einzusehen? 
Moeglicherweise liegt es auch an Evermore wie das Beispiel mit der
Native Version gezeigt hat, nur bin ich Ratlos WORAN es liegen koennte.
Wir haben schon den ERQ abgeschaltet und den Player den Heart Beat geklaut,
hilft alles nichts. 

> Damit ich das richtig verstehen:  wenn der Client auf Unix laeuft, 
> sendet der Driver 'DONT TELOPT_ECHO 0' einmal, wenn der Client auf 
> Unix laeuft, mehrere Tausend Mal?

Bei einen _Windows_ Client empfaengt h_telnet_neg in der File, die
ich Dir gesant habe tausendemale diese Zeile, bei einem Unix Client
nur einmal (entsprechend wird es sicherlich auch zu einer Ruecksendung
dieser Zeile vom Server an den Client kommen ... denke ich)

Wir haben uns diese Zeile ueber ein debug_message() auf die Console
gelenkt (die in ein File geschrieben wird). Nach 1 Minute waren dort
10000 solche Zeilen von einem Windows Nutzer drin.


wir haben heute Stunden damit zugebracht, das Problem einzuschraenken.
Dabei ist folgendes herausgekommen.
Wir benutzen den H_TELNET_NEG und den H_NOECHO. Wenn wir beides aus
kommentieren, dann funktioniert es wunderbar. Nur moechten unsere
Spieler (und die Wizards auch) nicht auf die automatische 
Groessenanpassung verzichten. Da ich selbst in unserer h_telnet_neg
file nicht durchsehe, haenge ich dir die Datei mal an. Wir konnten
weiterhin ermitteln, das bei einem Windows Rechner die Sequenz
die gesendet wird ein

    DONT TELOPT_ECHO mit parameter 0 ist, bei Unix kommt das genau
    1 mal, bei Windows tausende Male.

Ich denke es liegt an unserer Art das zu managen, aber ich weiss nicht
wie.
Eine Moeglichkeit die wir schon ausgeschlossen haben war folgende:
Wir haben den H_NOECHO komplett abgeschaltet. Zusaetzlich haben wir 
in der Methode init_telnet_neg() die im Player zum Schluss seiner
initialisierungsphase aufgerufen wird, das send_dont(TELOPT_NEW_ENVIRONMENT)
herausgenommen.
Dies hat bewirkt das mein Rechner (WindowsNT) auf einmal keine Probleme 
mehr verursacht hat, bei den anderen Spielern blieb das Problem aber
bestehen
(auch WindowsNT).

Ich bin ehrlich gesagt total ratlos, und wenn wir nicht ne andere
Moeglichkeit
finden, wird es wohl darauf hinaus laufen, das wir die eigene
Telnet-geschichte
aufgeben muessen. 

Waehre nett, wenn du mal in die Files schaust.

Folgende Hierarchie:

/std/login.c ruft create() auf um die Variablen zu initialiseren.
/std/login.c ruft h_noecho(0) in check_password auf, damit das password
nicht
             angezeigt wird

/std/player.c ruft create() auf um die variablen zu initalisieren.
/std/player.c ruft init_telnet_neg() auf und da crachts ... Wenn int dieser
Methode
              alle send_* auskommentiert sind, funktionierts ....


/*
 * @(#)/basic/player/telnet.c
 *
 * Copyright (c) 2000 Evermore
 * All rights reserved.
 *
 * This software has to be used in accordance with the terms of the licence
 * agreement.
 *
 * @author       : Fafnir (1999)
 * @last_changed : Bardioc at 04/21/2000
 *
 * A cut down version of a telnet daemon to retrieve the terminal type and type
 * and size of the player. It makes use of the driver hook H_TELNET_NEG
 */

#pragma no_clone

#include <telnet.h>

#define IT_NOECHO                     1
#define IT_CHARMODE_REQ               2

#define CLIENT_KNOWS                  ({ TELOPT_SGA, TELOPT_TTYPE, \
                                         TELOPT_NAWS, TELOPT_NEW_ENVIRON })
#define CLIENT_UNWANTED               ({ TELOPT_TTYPE, TELOPT_NAWS, \
                                         TELOPT_NEW_ENVIRON })
#define SERVER_KNOWS                  ({ })
#define SERVER_UNWANTED               ({ })

virtual inherit "/basic/create";

private nosave mapping env_vars;
// Environment variables received from telnet client

private nosave string telnet_flags_client;
// telnet options the client have to be set

private nosave string telnet_flags_server;
// telnet options we want to set

private nosave string first_terminal_type;
// first transmitted terminal type

private nosave string terminal_type;
// first compatible terminal type recognized

private nosave int *display_dimension;
// display dimension

private nosave int old_noecho;

private string last_terminal_type;
// last used terminal type

string query_terminal_type(status which)
{
    return (which
        ? terminal_type
        : first_terminal_type) || "unknown";
}

int *query_display_dimension()
{
    return display_dimension;
}

mapping query_env_vars()
{
    return env_vars;
}

private void send_will(int option)
{
    binary_message(({ IAC, WILL, option }));
}

private void send_wont(int option)
{
    binary_message(({ IAC, WONT, option }));
}

private void send_do(int option)
{
    binary_message(({ IAC, DO, option }));
}

private void send_dont(int option)
{
    binary_message(({ IAC, DONT, option }));
}

void create()
{
    create::create();
    
    env_vars = ([]);
    telnet_flags_client = "";
    telnet_flags_server = "";
}

void init_telnet_neg()
{
    env_vars = ([]);
    telnet_flags_client = "";
    telnet_flags_server = "";

    //send_dont(TELOPT_NEW_ENVIRON);
    //send_dont(TELOPT_TTYPE);
    //send_dont(TELOPT_NAWS);

    //telnet_flags_client = set_bit(telnet_flags_client, TELOPT_TTYPE);
    //telnet_flags_client = set_bit(telnet_flags_client, TELOPT_NAWS);

    //send_do(TELOPT_TTYPE);
    //send_do(TELOPT_NAWS);
}

private void received_terminal_type(int *ttype)
{
    string tt;

    tt = lower_case(to_string(ttype[1..]));
    if (!first_terminal_type) {
        first_terminal_type = tt;
    }

    if (tt == "unknown") {
        telnet_flags_client = set_bit(telnet_flags_client, TELOPT_NEW_ENVIRON);
        send_do(TELOPT_NEW_ENVIRON);

        return;
    }

    if (last_terminal_type == tt) {
        // stupid telnet client (violating protocol)
        telnet_flags_client = set_bit(telnet_flags_client, TELOPT_NEW_ENVIRON);
        send_do(TELOPT_NEW_ENVIRON);

        return;
    }

    last_terminal_type = tt;

    if (tt != "ansi" && tt != "linux"
            && !(tt[0..1] == "vt" && to_int(tt[2..]) >= 100)) {
        binary_message(({ IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }));
    }
    else {
        terminal_type = tt;
    }
}

private void received_display_dimensions(int *dimensions)
{
    int width;
    int height;

    width = dimensions[0] * 256 + dimensions[1];
    height = dimensions[2] * 256 + dimensions[3];

    display_dimension = ({ width, height });
}

private void received_new_environment(int *parameter)
{
    parameter += ({ 0 });
    for (int i = 0; i < sizeof(parameter); ++i) {
        int start;
        int end;
        int what;
        string value;
        string key;

        switch(parameter[i]) {
        case 0:
            // beginning of variable name
        case 1:
            // beginning of variable value
            end = i - 1;
            if (start) {
                if (what) {
                    value = lower_case(to_string(parameter[start..end]));
                }
                else {
                    key = lower_case(to_string(parameter[start..end]));
                }
            }
            if (key && value) {
                env_vars += ([ key : value ]);
                key = 0;
                value = 0;
            }

            what = parameter[i];
            start = i + 1;
        }
    }
}

varargs status h_telnet_neg(int what, int option, int *parameter)
{
     debug_message(sprintf("player = %s - what = %O - option = %O - parameter = %O\n", (string) this_object() -> query_real_name(), what, option, parameter));
   
    switch (what) {
    case SB:
        switch (option) {
        case TELOPT_TTYPE:
            received_terminal_type(parameter);
            break;
        case TELOPT_NAWS:
            received_display_dimensions(parameter);
            break;
        case TELOPT_NEW_ENVIRON:
            received_new_environment(parameter[1..]);
            break;
        default:
            // suboption of an option we don't support?
        }

        break;
    case WILL:           
        // do we know the option from the client?
        if (member(CLIENT_KNOWS, option) < 0) {
            // no, disallow
            send_dont(option);

            return true;
        }
        // did we requested that option at that point?
        if ((member(CLIENT_UNWANTED, option) < 0)
                && !test_bit(telnet_flags_client, option)) {
            // we didnt requested it, disallow
            send_dont(option);

            return true;
        }

        // further processing of special commands
        switch (option) {
        case TELOPT_TTYPE:
            // received acknowledge, request terminal type
            binary_message(({ IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }));
            break;
        case TELOPT_NEW_ENVIRON:
            binary_message(({ IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND,
                0, 3, IAC, SE }));
            break;
        }

        break;
    case WONT:
        switch (option) {
        case TELOPT_TTYPE:
            // received negative acknowledge to terminal type, go ahead to
            // requesting environment variables
            telnet_flags_client = set_bit(telnet_flags_client,
                TELOPT_NEW_ENVIRON);
            send_do(TELOPT_NEW_ENVIRON);

            break;
        default:
            if (!test_bit(telnet_flags_client, option)) {
                send_dont(option);
            }
            else {
                telnet_flags_client = clear_bit(telnet_flags_client, option);
            }
       }

       break;
    case DO:
        // do we know the option?
        if (member(SERVER_KNOWS, option) < 0) {
            // no, disallow
            send_wont(option);

            return true;
        }
        // did we wanted to enable that option at that point?
        if ((member(SERVER_UNWANTED, option) < 0) &&
                !test_bit(telnet_flags_server, option)) {
            // we didn't requested it, disallow
            send_wont(option);

            return true;
        }

        break;
    case DONT:
        switch (option) {
        case 0:
        default:
            if (!test_bit(telnet_flags_server, option)) {
                send_wont(option);
            }
            else {
                telnet_flags_server = clear_bit(telnet_flags_server, option);
            }
        }

        break;
    }

    return true;
}
/*
void h_noecho(int noecho, object user)
{
    if (~noecho & old_noecho & IT_NOECHO) {
        telnet_flags_server = clear_bit(telnet_flags_server, TELOPT_ECHO);
        send_wont(TELOPT_ECHO);
    }

    if (noecho & ~old_noecho & IT_NOECHO) { 
        telnet_flags_server = set_bit(telnet_flags_server, TELOPT_ECHO);
        send_will(TELOPT_ECHO);
    }

    if (~noecho & old_noecho & IT_CHARMODE_REQ) {
        telnet_flags_client = clear_bit(telnet_flags_client, TELOPT_SGA);
        send_dont(TELOPT_SGA);
    }

    if (noecho & ~old_noecho & IT_CHARMODE_REQ) {
        telnet_flags_client = set_bit(telnet_flags_client, TELOPT_SGA);
        send_do(TELOPT_SGA);
        send_will(TELOPT_SGA);
    }

    old_noecho = noecho;
} 
*/